首页 文章 精选 留言 我的

精选列表

搜索[镜像无法拉取],共10000篇文章
优秀的个人博客,低调大师

Spring Boot 侵入式 实现RESTful API接口统一JSON格式返回

前言 现在我们做项目基本上中大型项目都是选择前后端分离,前后端分离已经成了一个趋势了,所以总这样·我们就要和前端约定统一的api 接口返回json 格式, 这样我们需要封装一个统一通用全局 模版api返回格式,下次再写项目时候直接拿来用就可以了 约定JSON格式 一般我们和前端约定json格式是这样的 { "code": 200, "message": "成功", "data": { } } code: 返回状态码 message: 返回信息的描述 data: 返回值 封装java bean 定义状态枚举 package cn.soboys.core.ret; import lombok.Data; import lombok.Getter; /** * @author kenx * @version 1.0 * @date 2021/6/17 15:35 * 响应码枚举,对应HTTP状态码 */ @Getter public enum ResultCode { SUCCESS(200, "成功"),//成功 //FAIL(400, "失败"),//失败 BAD_REQUEST(400, "Bad Request"), UNAUTHORIZED(401, "认证失败"),//未认证 NOT_FOUND(404, "接口不存在"),//接口不存在 INTERNAL_SERVER_ERROR(500, "系统繁忙"),//服务器内部错误 METHOD_NOT_ALLOWED(405,"方法不被允许"), /*参数错误:1001-1999*/ PARAMS_IS_INVALID(1001, "参数无效"), PARAMS_IS_BLANK(1002, "参数为空"); /*用户错误2001-2999*/ private Integer code; private String message; ResultCode(int code, String message) { this.code = code; this.message = message; } } 定义返回状态码,和信息一一对应,我们可以约定xxx~xxx 为什么错误码,防止后期错误码重复,使用混乱不清楚, 定义返回体结果体 package cn.soboys.core.ret; import lombok.Data; import java.io.Serializable; /** * @author kenx * @version 1.0 * @date 2021/6/17 15:47 * 统一API响应结果格式封装 */ @Data public class Result<T> implements Serializable { private static final long serialVersionUID = 6308315887056661996L; private Integer code; private String message; private T data; public Result setResult(ResultCode resultCode) { this.code = resultCode.getCode(); this.message = resultCode.getMessage(); return this; } public Result setResult(ResultCode resultCode,T data) { this.code = resultCode.getCode(); this.message = resultCode.getMessage(); this.setData(data); return this; } } code,和message都从定义的状态枚举中获取 这里有两个需要注意地方我的数据类型T data返回的是泛型类型而不是object类型而且我的结果累实现了Serializable接口 我看到网上有很多返回object,最后返回泛型因为泛型效率要高于object,object需要强制类型转换,还有最后实现了Serializable接口因为通过流bytes传输方式web传输,速率更块 定义返回结果方法 一般业务返回要么是 success成功,要么就是failure失败,所以我们需要单独定义两个返回实体对象方法, package cn.soboys.core.ret; /** * @author kenx * @version 1.0 * @date 2021/6/17 16:30 * 响应结果返回封装 */ public class ResultResponse { private static final String DEFAULT_SUCCESS_MESSAGE = "SUCCESS"; // 只返回状态 public static Result success() { return new Result() .setResult(ResultCode.SUCCESS); } // 成功返回数据 public static Result success(Object data) { return new Result() .setResult(ResultCode.SUCCESS, data); } // 失败 public static Result failure(ResultCode resultCode) { return new Result() .setResult(resultCode); } // 失败 public static Result failure(ResultCode resultCode, Object data) { return new Result() .setResult(resultCode, data); } } 注意这里我定义的是静态工具方法,因为使用构造方法进行创建对象调用太麻烦了, 我们使用静态方法来就直接类调用很方便 这样我们就可以在controller中很方便返回统一api格式了 package cn.soboys.mallapi.controller; import cn.soboys.core.ret.Result; import cn.soboys.core.ret.ResultResponse; import cn.soboys.mallapi.bean.User; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @author kenx * @version 1.0 * @date 2021/7/2 20:28 */ @RestController //默认全部返回json @RequestMapping("/user") public class UserController { @GetMapping("/list") public Result getUserInfo(){ User u=new User(); u.setUserId("21"); u.setUsername("kenx"); u.setPassword("224r2"); return ResultResponse.success(u); } } 返回结果符合我们预期json格式 但是这个代码还可以优化,不够完善,比如,每次controller中所有的方法的返回必须都是要Result类型,我们想返回其他类型格式怎么半,还有就是不够语义化,其他开发人员看你方法根本就不知道具体返回什么信息 如果改成这个样子就完美了如 @GetMapping("/list") public User getUserInfo() { User u = new User(); u.setUserId("21"); u.setUsername("kenx"); u.setPassword("224r2"); return u; } 其他开发人员一看就知道具体是返回什么数据。但这个格式要怎么去统一出来? 其实我们可以这么去优化,通过SpringBoot提供的ResponseBodyAdvice进行统一响应处理 自定义注解@ResponseResult来拦截有此controller注解类的代表需要统一返回json格式,没有就安照原来返回 package cn.soboys.core.ret; import java.lang.annotation.*; /** * @author kenx * @version 1.0 * @date 2021/6/17 16:43 * 统一包装接口返回的值 Result */ @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface ResponseResult { } 定义请求拦截器通过反射获取到有此注解的HandlerMethod设置包装拦截标志 package cn.soboys.core.ret; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerInterceptor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.lang.reflect.Method; /** * @author kenx * @version 1.0 * @date 2021/6/17 17:10 * 请求拦截 */ public class ResponseResultInterceptor implements HandlerInterceptor { //标记名称 public static final String RESPONSE_RESULT_ANN = "RESPONSE-RESULT-ANN"; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //请求方法 if (handler instanceof HandlerMethod) { final HandlerMethod handlerMethod = (HandlerMethod) handler; final Class<?> clazz = handlerMethod.getBeanType(); final Method method = handlerMethod.getMethod(); //判断是否在对象上加了注解 if (clazz.isAnnotationPresent(ResponseResult.class)) { //设置此请求返回体需要包装,往下传递,在ResponseBodyAdvice接口进行判断 request.setAttribute(RESPONSE_RESULT_ANN, clazz.getAnnotation(ResponseResult.class)); //方法体上是否有注解 } else if (method.isAnnotationPresent(ResponseResult.class)) { //设置此请求返回体需要包装,往下传递,在ResponseBodyAdvice接口进行判断 request.setAttribute(RESPONSE_RESULT_ANN, clazz.getAnnotation(ResponseResult.class)); } } return true; } } 实现ResponseBodyAdvice<Object> 接口自定义json返回解析器根据包装拦截标志判断是否需要自定义返回类型返回类型 package cn.soboys.core.ret; import cn.soboys.core.utils.HttpContextUtil; import com.alibaba.fastjson.JSON; import lombok.extern.slf4j.Slf4j; import org.springframework.core.MethodParameter; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice; import javax.servlet.http.HttpServletRequest; /** * @author kenx * @version 1.0 * @date 2021/6/17 16:47 * 全局统一响应返回体处理 */ @Slf4j @ControllerAdvice public class ResponseResultHandler implements ResponseBodyAdvice<Object> { public static final String RESPONSE_RESULT_ANN = "RESPONSE-RESULT-ANN"; /** * @param methodParameter * @param aClass * @return 此处如果返回false , 则不执行当前Advice的业务 * 是否请求包含了包装注解 标记,没有直接返回不需要重写返回体, */ @Override public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) { HttpServletRequest request = HttpContextUtil.getRequest(); //判断请求是否有包装标志 ResponseResult responseResultAnn = (ResponseResult) request.getAttribute(RESPONSE_RESULT_ANN); return responseResultAnn == null ? false : true; } /** * @param body * @param methodParameter * @param mediaType * @param aClass * @param serverHttpRequest * @param serverHttpResponse * @return 处理响应的具体业务方法 */ @Override public Object beforeBodyWrite(Object body, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) { if (body instanceof Result) { return body; } else if (body instanceof String) { return JSON.toJSONString(ResultResponse.success(body)); } else { return ResultResponse.success(body); } } } 注意这里string类型返回要单独json序列化返回一下,不然会报转换异常 这样我们就可以在controler中返回任意类型,了不用每次都必须返回 Result 如 package cn.soboys.mallapi.controller; import cn.soboys.core.ret.ResponseResult; import cn.soboys.core.ret.Result; import cn.soboys.core.ret.ResultResponse; import cn.soboys.mallapi.bean.User; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @author kenx * @version 1.0 * @date 2021/7/2 20:28 */ @RestController //默认全部返回json @RequestMapping("/user") @ResponseResult public class UserController { @GetMapping("/list") public User getUserInfo() { User u = new User(); u.setUserId("21"); u.setUsername("kenx"); u.setPassword("224r2"); return u; } @GetMapping("/test") public String test() { return "ok"; } @GetMapping("/test2") public Result test1(){ return ResultResponse.success(); } } 这里还有一个问题?正常情况返回成功的话是统一json 格式,但是返回失败,或者异常了,怎么统一返回错误json 格式,sprinboot有自己的错误格式? 请参考我上一篇,SpringBoot优雅的全局异常处理 扫码关注公众号猿人生了解更多好文

优秀的个人博客,低调大师

k8s监控体系搭建prometheus+grafana+alertmanager坑版

2 --> 提前准备 1.已经安装了k8s集群 2.已经安装了storgeclass(可以参考k8s存储管理 https://blog.51cto.com/luoguoling/2966225) 一.安装prometheus 1.0 提前设置namespace apiVersion: v1 kind: Namespace metadata: name: ops 1.1 prometheus配置文件 prometheus-configmap.yaml apiVersion: v1 kind: ConfigMap metadata: name: prometheus-config namespace: ops data: prometheus.yml: | rule_files: - /etc/config/rules/*.rules scrape_configs: - job_name: prometheus static_configs: - targets: - localhost:9090 - job_name: kubernetes-apiservers kubernetes_sd_configs: - role: endpoints relabel_configs: - action: keep regex: default;kubernetes;https source_labels: - __meta_kubernetes_namespace - __meta_kubernetes_service_name - __meta_kubernetes_endpoint_port_name scheme: https tls_config: ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt insecure_skip_verify: true bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token - job_name: kubernetes-nodes-kubelet kubernetes_sd_configs: - role: node # 发现集群中的节点 relabel_configs: # 将标签(.*)作为新标签名,原有值不变 - action: labelmap regex: __meta_kubernetes_node_label_(.+) scheme: https tls_config: ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt insecure_skip_verify: true bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token - job_name: kubernetes-nodes-cadvisor kubernetes_sd_configs: - role: node relabel_configs: # 将标签(.*)作为新标签名,原有值不变 - action: labelmap regex: __meta_kubernetes_node_label_(.+) # 实际访问指标接口 https://NodeIP:10250/metrics/cadvisor,这里替换默认指标URL路径 - target_label: __metrics_path__ replacement: /metrics/cadvisor scheme: https tls_config: ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt insecure_skip_verify: true bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token - job_name: kubernetes-service-endpoints kubernetes_sd_configs: - role: endpoints # 从Service列表中的Endpoint发现Pod为目标 relabel_configs: # Service没配置注解prometheus.io/scrape的不采集 - action: keep regex: true source_labels: - __meta_kubernetes_service_annotation_prometheus_io_scrape # 重命名采集目标协议 - action: replace regex: (https?) source_labels: - __meta_kubernetes_service_annotation_prometheus_io_scheme target_label: __scheme__ # 重命名采集目标指标URL路径 - action: replace regex: (.+) source_labels: - __meta_kubernetes_service_annotation_prometheus_io_path target_label: __metrics_path__ # 重命名采集目标地址 - action: replace regex: ([^:]+)(?::\d+)?;(\d+) replacement: $1:$2 source_labels: - __address__ - __meta_kubernetes_service_annotation_prometheus_io_port target_label: __address__ # 将K8s标签(.*)作为新标签名,原有值不变 - action: labelmap regex: __meta_kubernetes_service_label_(.+) # 生成命名空间标签 - action: replace source_labels: - __meta_kubernetes_namespace target_label: kubernetes_namespace # 生成Service名称标签 - action: replace source_labels: - __meta_kubernetes_service_name target_label: kubernetes_name - job_name: kubernetes-pods kubernetes_sd_configs: - role: pod # 发现所有Pod为目标 # 重命名采集目标协议 relabel_configs: - action: keep regex: true source_labels: - __meta_kubernetes_pod_annotation_prometheus_io_scrape # 重命名采集目标指标URL路径 - action: replace regex: (.+) source_labels: - __meta_kubernetes_pod_annotation_prometheus_io_path target_label: __metrics_path__ # 重命名采集目标地址 - action: replace regex: ([^:]+)(?::\d+)?;(\d+) replacement: $1:$2 source_labels: - __address__ - __meta_kubernetes_pod_annotation_prometheus_io_port target_label: __address__ # 将K8s标签(.*)作为新标签名,原有值不变 - action: labelmap regex: __meta_kubernetes_pod_label_(.+) # 生成命名空间标签 - action: replace source_labels: - __meta_kubernetes_namespace target_label: kubernetes_namespace # 生成Service名称标签 - action: replace source_labels: - __meta_kubernetes_pod_name target_label: kubernetes_pod_name alerting: alertmanagers: - static_configs: - targets: ["alertmanager:80"] 1.2 prometheus部署文件 prometheus-deploy.yaml apiVersion: apps/v1 kind: Deployment metadata: name: prometheus namespace: ops labels: k8s-app: prometheus spec: replicas: 1 selector: matchLabels: k8s-app: prometheus template: metadata: labels: k8s-app: prometheus spec: serviceAccountName: prometheus initContainers: - name: "init-chown-data" image: "busybox:latest" # imagePullPolicy: "IfNotPresent" command: ["chown", "-R", "65534:65534", "/data"] volumeMounts: - name: prometheus-data mountPath: /data subPath: "" containers: - name: prometheus-server-configmap-reload image: "jimmidyson/configmap-reload:v0.1" # imagePullPolicy: "IfNotPresent" args: - --volume-dir=/etc/config - --webhook-url=http://localhost:9090/-/reload volumeMounts: - name: config-volume mountPath: /etc/config readOnly: true - mountPath: /etc/localtime name: timezone resources: limits: cpu: 10m memory: 100Mi requests: cpu: 10m memory: 100Mi - name: prometheus-server image: "prom/prometheus:v2.20.0" # imagePullPolicy: "IfNotPresent" args: - --config.file=/etc/config/prometheus.yml - --storage.tsdb.path=/data - --web.console.libraries=/etc/prometheus/console_libraries - --web.console.templates=/etc/prometheus/consoles - --web.enable-lifecycle ports: - containerPort: 9090 readinessProbe: httpGet: path: /-/ready port: 9090 initialDelaySeconds: 30 timeoutSeconds: 30 livenessProbe: httpGet: path: /-/healthy port: 9090 initialDelaySeconds: 30 timeoutSeconds: 30 resources: limits: cpu: 500m memory: 800Mi requests: cpu: 200m memory: 400Mi volumeMounts: - name: config-volume mountPath: /etc/config - name: prometheus-data mountPath: /data subPath: "" - name: prometheus-rules mountPath: /etc/config/rules - mountPath: /etc/localtime name: timezone volumes: - name: config-volume configMap: name: prometheus-config - name: prometheus-rules configMap: name: prometheus-rules - name: prometheus-data persistentVolumeClaim: claimName: prometheus - name: timezone hostPath: path: /usr/share/zoneinfo/Asia/Shanghai --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: prometheus namespace: ops spec: storageClassName: "managed-nfs-storage" accessModes: - ReadWriteMany resources: requests: storage: 10Gi --- apiVersion: v1 kind: Service metadata: name: prometheus namespace: ops spec: type: NodePort ports: - name: http port: 9090 protocol: TCP targetPort: 9090 nodePort: 30089 selector: k8s-app: prometheus --- apiVersion: v1 kind: ServiceAccount metadata: name: prometheus namespace: ops --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: name: prometheus rules: - apiGroups: - "" resources: - nodes - nodes/metrics - services - endpoints - pods verbs: - get - list - watch - apiGroups: - "" resources: - configmaps verbs: - get - nonResourceURLs: - "/metrics" verbs: - get --- apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: name: prometheus roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole name: prometheus subjects: - kind: ServiceAccount name: prometheus namespace: ops 1.3 prometheus配置报警规则 prometheus-rules.yaml apiVersion: v1 kind: ConfigMap metadata: name: prometheus-rules namespace: ops data: general.rules: | groups: - name: general.rules rules: - alert: InstanceDown expr: up == 0 for: 1m labels: severity: error annotations: summary: "Instance {{ $labels.instance }} 停止工作" description: "{{ $labels.instance }} job {{ $labels.job }} 已经停止5分钟以上." node.rules: | groups: - name: node.rules rules: - alert: NodeFilesystemUsage expr: | 100 - (node_filesystem_free{fstype=~"ext4|xfs"} / node_filesystem_size{fstype=~"ext4|xfs"} * 100) > 80 for: 1m labels: severity: warning annotations: summary: "Instance {{ $labels.instance }} : {{ $labels.mountpoint }} 分区使用率过高" description: "{{ $labels.instance }}: {{ $labels.mountpoint }} 分区使用大于80% (当前值: {{ $value }})" - alert: NodeMemoryUsage expr: | 100 - (node_memory_MemFree+node_memory_Cached+node_memory_Buffers) / node_memory_MemTotal * 100 > 20 for: 1m labels: severity: warning annotations: summary: "Instance {{ $labels.instance }} 内存使用率过高" description: "{{ $labels.instance }}内存使用大于80% (当前值: {{ $value }})" - alert: NodeCPUUsage expr: | 100 - (avg(irate(node_cpu_seconds_total{mode="idle"}[5m])) by (instance) * 100) > 60 for: 1m labels: severity: warning annotations: summary: "Instance {{ $labels.instance }} CPU使用率过高" description: "{{ $labels.instance }}CPU使用大于60% (当前值: {{ $value }})" - alert: KubeNodeNotReady expr: | kube_node_status_condition{condition="Ready",status="true"} == 0 for: 1m labels: severity: error annotations: message: '{{ $labels.node }} 已经有10多分钟没有准备好了.' pod.rules: | groups: - name: pod.rules rules: - alert: PodCPUUsage expr: | sum(rate(container_cpu_usage_seconds_total{image!=""}[1m]) * 100) by (pod_name, namespace) > 5 for: 5m labels: severity: warning annotations: summary: "命名空间: {{ $labels.namespace }} | Pod名称: {{ $labels.pod_name }} CPU使用大于80% (当前值: {{ $value }})" - alert: PodMemoryUsage expr: | sum(container_memory_rss{image!=""}) by(pod_name, namespace) / sum(container_spec_memory_limit_bytes{image!=""}) by(pod_name, namespace) * 100 != +inf > 80 for: 5m labels: severity: error annotations: summary: "命名空间: {{ $labels.namespace }} | Pod名称: {{ $labels.pod_name }} 内存使用大于80% (当前值: {{ $value }})" - alert: PodNetworkReceive expr: | sum(rate(container_network_receive_bytes_total{image!="",name=~"^k8s_.*"}[5m]) /1000) by (pod_name,namespace) > 30000 for: 5m labels: severity: warning annotations: summary: "命名空间: {{ $labels.namespace }} | Pod名称: {{ $labels.pod_name }} 入口流量大于30MB/s (当前值: {{ $value }}K/s)" - alert: PodNetworkTransmit expr: | sum(rate(container_network_transmit_bytes_total{image!="",name=~"^k8s_.*"}[5m]) /1000) by (pod_name,namespace) > 30000 for: 5m labels: severity: warning annotations: summary: "命名空间: {{ $labels.namespace }} | Pod名称: {{ $labels.pod_name }} 出口流量大于30MB/s (当前值: {{ $value }}/K/s)" - alert: PodRestart expr: | sum(changes(kube_pod_container_status_restarts_total[1m])) by (pod,namespace) > 0 for: 1m labels: severity: warning annotations: summary: "命名空间: {{ $labels.namespace }} | Pod名称: {{ $labels.pod }} Pod重启 (当前值: {{ $value }})" - alert: PodFailed expr: | sum(kube_pod_status_phase{phase="Failed"}) by (pod,namespace) > 0 for: 5s labels: severity: error annotations: summary: "命名空间: {{ $labels.namespace }} | Pod名称: {{ $labels.pod }} Pod状态Failed (当前值: {{ $value }})" - alert: PodPending expr: | sum(kube_pod_status_phase{phase="Pending"}) by (pod,namespace) > 0 for: 1m labels: severity: error annotations: summary: "命名空间: {{ $labels.namespace }} | Pod名称: {{ $labels.pod }} Pod状态Pending (当前值: {{ $value }})" e 二.alertmanager搭建 2.1 alertmanager配置文件alertmanger-configmap.yaml 注:邮箱需要自己去网易邮箱申请并且取得授权管理密码 apiVersion: v1 kind: ConfigMap metadata: name: alertmanager-config namespace: ops data: alertmanager.yml: |- global: # 在没有报警的情况下声明为已解决的时间 resolve_timeout: 5m # 配置邮件发送信息 smtp_smarthost: 'smtp.163.com:465' smtp_from: 'xxx@163.com' smtp_auth_username: 'xxx@163.com' smtp_auth_password: 'GMZFNPIWGYEPMGDK' smtp_hello: '163.com' smtp_require_tls: false # 所有报警信息进入后的根路由,用来设置报警的分发策略 route: # 这里的标签列表是接收到报警信息后的重新分组标签,例如,接收到的报警信息里面有许多具有 cluster=A 和 alertname=LatncyHigh 这样的标签的报警信息将会批量被聚合到一个分组里面 group_by: ['alertname', 'cluster'] # 当一个新的报警分组被创建后,需要等待至少group_wait时间来初始化通知,这种方式可以确保您能有足够的时间为同一分组来获取多个警报,然后一起触发这个报警信息。 group_wait: 30s # 当第一个报警发送后,等待'group_interval'时间来发送新的一组报警信息。 group_interval: 5m # 如果一个报警信息已经发送成功了,等待'repeat_interval'时间来重新发送他们 repeat_interval: 5m # 默认的receiver:如果一个报警没有被一个route匹配,则发送给默认的接收器 receiver: default # 上面所有的属性都由所有子路由继承,并且可以在每个子路由上进行覆盖。 routes: - receiver: email group_wait: 10s match: team: node receivers: - name: 'default' email_configs: - to: 'xxx@xx.com' send_resolved: true - name: 'email' email_configs: - to: 'xxx@qq.com' send_resolved: true 2.2 alertmanager template文件alertmanager-template.yaml #自定义告警模板 apiVersion: v1 kind: ConfigMap metadata: name: alertmanager-template-volume namespace: ops data: email.tmpl: | {{ define "email.html" }} {{ range .Alerts }} <pre> ========start========== 告警程序: prometheus_alert_email 告警级别: {{ .Labels.severity }} 级别 告警类型: {{ .Labels.alertname }} 故障主机: {{ .Labels.instance }} 告警主题: {{ .Annotations.summary }} 告警详情: {{ .Annotations.description }} 处理方法: {{ .Annotations.console }} 触发时间: {{ .StartsAt.Format "2006-01-02 15:04:05" }} ========end========== </pre> {{ end }} {{ end }} 2.3alertmanager部署文件alertmanager-deploy.yaml apiVersion: apps/v1 kind: Deployment metadata: name: alertmanager namespace: ops spec: replicas: 1 selector: matchLabels: k8s-app: alertmanager version: v0.14.0 template: metadata: labels: k8s-app: alertmanager version: v0.14.0 spec: containers: - name: prometheus-alertmanager image: "prom/alertmanager:v0.14.0" imagePullPolicy: "IfNotPresent" args: - --config.file=/etc/config/alertmanager.yml - --storage.path=/data - --web.external-url=/ ports: - containerPort: 9093 readinessProbe: httpGet: path: /#/status port: 9093 initialDelaySeconds: 30 timeoutSeconds: 30 volumeMounts: - name: config-volume mountPath: /etc/config #自定义告警模板 - name: config-template-volume mountPath: /etc/config/template - name: storage-volume mountPath: "/data" subPath: "" - mountPath: /etc/localtime name: timezone resources: limits: cpu: 10m memory: 200Mi requests: cpu: 10m memory: 100Mi - name: prometheus-alertmanager-configmap-reload image: "jimmidyson/configmap-reload:v0.1" imagePullPolicy: "IfNotPresent" args: - --volume-dir=/etc/config - --webhook-url=http://localhost:9093/-/reload volumeMounts: - name: config-volume mountPath: /etc/config readOnly: true resources: limits: cpu: 10m memory: 200Mi requests: cpu: 10m memory: 100Mi volumes: - name: config-volume configMap: name: alertmanager-config - name: config-template-volume configMap: name: alertmanager-template-volume - name: storage-volume persistentVolumeClaim: claimName: alertmanager - name: timezone hostPath: path: /usr/share/zoneinfo/Asia/Shanghai --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: alertmanager namespace: ops spec: storageClassName: managed-nfs-storage accessModes: - ReadWriteOnce resources: requests: storage: "2Gi" --- apiVersion: v1 kind: Service metadata: name: alertmanager namespace: ops labels: kubernetes.io/cluster-service: "true" addonmanager.kubernetes.io/mode: Reconcile kubernetes.io/name: "Alertmanager" spec: type: "NodePort" ports: - name: http port: 80 protocol: TCP targetPort: 9093 nodePort: 30093 selector: k8s-app: alertmanager 三 安装监控界面grafana 3.1 grafana部署文件 apiVersion: apps/v1 kind: Deployment metadata: name: grafana namespace: ops spec: replicas: 1 selector: matchLabels: app: grafana template: metadata: labels: app: grafana spec: containers: - name: grafana image: grafana/grafana:7.1.0 ports: - containerPort: 3000 protocol: TCP resources: limits: cpu: 100m memory: 256Mi requests: cpu: 100m memory: 256Mi volumeMounts: - name: grafana-data mountPath: /var/lib/grafana subPath: grafana - mountPath: /etc/localtime name: timezone securityContext: fsGroup: 472 runAsUser: 472 volumes: - name: grafana-data persistentVolumeClaim: claimName: grafana - name: timezone hostPath: path: /usr/share/zoneinfo/Asia/Shanghai --- apiVersion: v1 kind: PersistentVolumeClaim metadata: name: grafana namespace: ops spec: storageClassName: "managed-nfs-storage" accessModes: - ReadWriteMany resources: requests: storage: 5Gi --- apiVersion: v1 kind: Service metadata: name: grafana namespace: ops spec: type: NodePort ports: - port : 80 targetPort: 3000 nodePort: 30030 selector: app: grafana 3.2 数据源和监控模板 数据源填写配置prometheus.ops:9090 模板可以下载下面3个 k8s工作节点监控 k8s集群资源监控 k8s资源对象状态监控 k8s监控模板 提取码: aexi 操作结果展示 1.prometheus效果 4.2 alertmanager显示效果 4.3报警结果 4.4 grafana监控图部分图 文章参考了https://i4t.com/4197.html

优秀的个人博客,低调大师

为何绝大数多少消费者仍然对5G感?

尽管许多城市已经部署了下一代网络,让部分消费体验到了5G网络带来的非凡体验,但某些城市却出于某些原因而暂缓5G网络的部署建设工作。尽管目前运营商大肆宣传,但许多客户似乎仍未意识到5G的优势。 下一代蜂窝技术5G为居民、企业主以及希望使自己的城市成为有竞争力的“智慧城市”的城市官员提供了巨大的潜力。但是目前,许多客户并不了解其潜在优势。 今天,5G将为客户带来更高质量的远程医疗访问和较少故障的在线课程,视频下载将花费更少的时间。未来,5G可能会让居民在到达之前找到一个开放的停车位,或者使与设备直接通信的物联网蓬勃发展。 “市场上一些与5G兼容的设备都存在过度炒作,但许多地区5G尚不具备正常的服务能力。” 由于5G网络前期基础设施部署投入耗资巨大,除了个别大型城市中的重点区域有相对较好的信号覆盖之外,绝大多数地区仍处于5G信号覆盖盲区。而且,相对高昂资费以及缺乏5G杀手级应用的出现,普通消费者升级5G的热情并不高。 购买新的5G兼容手机的消费者,如果居住在已经拥有5G的社区中,则可以期望获得更快的下载速度和更高质量的视频。如果他们居住地还没有5G网络覆盖,他们甚至可能不会注意到它的存在。他们的手机将继续在4G上运行。 5G并不仅仅是为了改进日常生活通讯交流而生,5G的真正用武之地是在未来的万物互联,这才是发挥5G高速率、高带宽、高容量的特性。 什么是5G IoT,它将如何改变连接性? 5G将从根本上改变我们世界网络连接的方式。不久之后,全球社会将跨行业、市场和地区适应新的技术生活方式。这一新技术标准不仅仅承诺对现有移动通信技术的进一步发展。 数字化,社会和经济的全面变化将在生活的几乎所有领域发生。到目前为止,主要目标是全面扩展常规网络的基础设施条件,以确保几乎所有移动设备的网络可用性。在未来的几年中,除了在5G IoT中持续进行联网之外,重点还将放在以前所未有的最佳方式满足联网社会不断增长的需求。 无限连接到网络未来 全球数据量持续增长,使5G不可或缺。由于数据的巨大增长,从中期来看,现有技术将不能再满足物联网世界的需求。德国是数据量发展的一个很好的例子。 2017年,德国的数据量达到10亿千兆字节,已经是2015年的两倍。 根据这些发现,专家估计,到2020年,全世界的物联网数量将在50到5000亿之间。这证明了5G网络的必要性,这为我们的全球经济提供了巨大的潜力:如此高的数据量与IoT设备的数量相结合,以及只有在5G的帮助下才能实现有关IoT网络的个性化需求。 为什么5G注定要用于物联网? 得益于所谓的“3G”蜂窝标准,智能手机成为可能,因为3G是当时生产智能手机的主要驱动力。第四代移动网络标准是在某个时候创建的。多亏了“ LTE”,数据传输速率大大提高了。 直到今天,LTE仍是最流行和最常用的网络。对于网络而言,高达每秒100兆位的速度是没有问题的,并且在经济和社会的许多领域中已经成为现实。甚至可以修改LTE带宽,以最终达到每秒4000兆位的下载速度。 然而,展望未来,LTE不足以满足新技术的标准和期望。 LTE的设计和优化主要针对智能手机,而5G将成为所有物联网的移动标准。 5G IoT在各个方面都达到了新的高度。新网络中的数据吞吐量应达到每秒20吉比特,并允许更短的响应时间。相比之下,首款具有1G网络连接的手机要比5G网络少八百万倍。 使用5G,还可以实时传输数据。这意味着将可以同时访问全球1000亿个移动设备。换句话说,连接密度约为每平方公里一百万个设备。同时,新技术带来了相对运动速度的提高。这意味着最高时速500公里时,连接质量将更加稳定,这将带来巨大的好处,特别是对于铁路旅客而言。 无论智能手机如何,在其他应用领域中不可避免地要增加数据量。这些数字不仅听起来很大,而且真的很大。由于这些原因以及其他许多原因,5G IoT将成为连接的新关键技术。 多元化和创新的应用领域 例如,除了物联网,工业4.0也将从5G技术中受益匪浅。机器、系统、机器人和人之间的持续数据交换将成为工业生产的组成部分。连接的设备和部件的数量将大大增加。例如,工业机器人的控制单元可以实时处理,并且错误概率最终可以降低到最小。例如,无人驾驶快递服务将因此能够始终在机器的装卸点准时取货。 业界有许多示例说明如何使用5G优化运营流程。新技术带来了前所未有的机会和想法,因为以前在技术上是不可能的。 这些想法和机会也正在适应其他经济部门,而与行业无关。例如,具有所谓的智能农业的农业。借助数字化使用,有关动物健康、杂草或害虫在哪里或土壤中的水分状况如何的数据和见解都成为可能。这样的智能网络将在未来成倍增长和成熟。借助5G IoT连通性,智能农业创造了传统农业尚无法做到的事情:它在提高食品生产效率的同时创造了更大程度的生态。 在数字化环境中驱动连接 网络连接领域的数字化转型已经变得显而易见,并且潜力被明确定义。根据有关5G网络中带宽或设备密度的令人印象深刻的数字,给人的印象是连通性发挥了其最大潜力。 良好运行的连接性是设置物联网网络(无论大小)的最重要基础。借助5G技术,也许还有世界上从未有人想到过的想法、方法和措施。可能是由于5G连通性,无数新的商业理念被带入市场,继续革新着我们的数字世界。 5G技术何时会达到市场成熟还有待观察。但是,可以肯定的是:技术在起作用,世界已经为数字未来的另一次飞跃做好了准备。 5G能否推动智能家居市场的创新? 在5G网络服务大规模普及应用之前,我们还有很长的路要走,但是我们可以讨论5G的未来含义,以及一旦广泛使用后5G如何改善设备功能。 5G的影响在于可以通过基于云的系统在智能家居设备之间传输的数据量增加。通过利用云的海量计算能力及其处理更大容量数据的能力,我们可以接收更深入的分析,这些分析可以通过使智能家居设备更快、更智能来改善智能家居设备。 那么,5G会促进智能家居市场的创新吗? 当前的智能家居视频监控系统可以显示使用5G进行创新的示例。当前的产品允许您使用运动检测等功能。就目前而言,这是一种监视的基本形式,它监视图像的变化并在发生异常情况时通知所有者。 一旦5G发挥作用,捕获的视频数据就可以发送到云中,进行更详细的分析,并且可以使系统推断出该运动是来自人类,物体还是动物。面部识别也可以在这里发挥作用,在向警察报告事件时提供更无缝的服务。 同样,我们可以使用家庭中各种设备的数据来加强安全措施。与存在或热探测器结合使用的摄像机可以通过为“更大的画面”提供更多的数据点来进行操作,从而消除小错误-合并使用所有智能设备。 我们不能仅仅依靠智能设备为我们做出决策,但是我们可以做的就是改善设备处理,以便在我们介入时,我们已经掌握了所有必要的信息,可以评估适当的行动呼吁。 5G将让房主在自己的房屋内做什么? 目前,使用4G的智能家居采用整合Wi-Fi、蓝牙和其他网络协议的方式以分散的方式运行。与4G不同,5G将与低功耗设备一起使用,使其可用于更广泛的连接产品。这意味着我们将能够连接所有设备,以允许所有设备之间进行集成通信。 例如,你的冰箱和其他厨房电器可以连接在一起,与家庭系统一起协同工作,以创建一个完全自动化的家庭。如果你的冰箱已连接互联网,则如果断电,你可能会收到通知,但是由于家里的其他所有设备也都已连接,因此你可以立即确定是电源故障还是产品故障。更快的连接性意味着用户可以快速利用其智能设备提供的数据,例如可以监控水位并允许进行行为更改以限制用水的用水传感器。 车辆同样适用。未来的汽车将是自动驾驶的,并包括一个集成的行车记录仪,然后可以将其连接到你的安全系统,以在你的房屋外围提供现场增强的安全性,在潜在的入侵者到达你的前门时提醒你。

优秀的个人博客,低调大师

kafka 生产发送消息失败响应,或者Error while fetching metadata with correlation id

今天在使用代码编写kafka 生产者发送消息的时候,因为我的手误出现的搞笑的事情。 同样的代码和kafka 在不久前执行过,是没有问题的。 代码如下 package streaming.utils import java.util import java.util.{Date, Properties, UUID} import com.alibaba.fastjson.JSONObject import org.apache.commons.lang3.time.FastDateFormat import org.apache.kafka.clients.producer.{KafkaProducer, ProducerRecord} import scala.util.Random /** * Author: Michael PK QQ: 1990218038 * * Kafka数据生产者 */ object ProducerApp { def main(args: Array[String]): Unit = { val props = new Properties props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer") props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer") props.put("bootstrap.servers", ParamsConf.brokers) props.put("request.required.acks","1") val topic = ParamsConf.topic val producer = new KafkaProducer[String,String](props) val random = new Random() val dateFormat = FastDateFormat.getInstance("yyyyMMddHHmmss") for(i <- 1 to 100){ val time = dateFormat.format(new Date())+"" val userid = random.nextInt(1000)+"" val courseid = random.nextInt(500)+"" val fee = random.nextInt(400)+"" val result = Array("0","1") // 0未成功支付,1成功支付 val flag = result(random.nextInt(2)) var orderid = UUID.randomUUID().toString val map = new util.HashMap[String, Object]() map.put("time", time) map.put("userid",userid) map.put("courseid",courseid) map.put("fee", fee) map.put("flag", flag) map.put("orderid",orderid) val json = new JSONObject(map) producer.send(new ProducerRecord[String,String](topic(0),json.toJSONString)) } println("PK Kafka生产者生产数据完毕...") } } 代码很简单。只是用来模拟生产数据而已。 一直以来的使用的 都是 2.0 版本的 kafka client <dependency> <groupId>org.apache.kafka</groupId> <artifactId>kafka-clients</artifactId> <version>2.0.0</version> </dependency> 但是今天执行的上面的代码的时候。 就 不能发生消息了,也没有错误的提示。 程序也没有关闭。 通过 debug 发现卡在 doSend 代码里面了 private Future<RecordMetadata> doSend(ProducerRecord<K, V> record, Callback callback) { TopicPartition tp = null; try { throwIfProducerClosed(); // first make sure the metadata for the topic is available ClusterAndWaitTime clusterAndWaitTime; try { clusterAndWaitTime = waitOnMetadata(record.topic(), record.partition(), maxBlockTimeMs); } catch (KafkaException e) { if (metadata.isClosed()) throw new KafkaException("Producer closed while send in progress", e); throw e; } 虽然它抛出了异常,但是 不能进入 if (metadata.isClosed()) 逻辑里面 外层并没有捕获它的异常。通过debug 这个 异常 e 是 Failed to update metadata after 60000 ms. 考虑到它的版本也服务器版本不一样 就试着 减低版本看看 <dependency> <groupId>org.apache.kafka</groupId> <artifactId>kafka-clients</artifactId> <version>1.1.0</version> </dependency> 执行的时候不停的提示: lientId=producer-1] Error while fetching metadata with correlation id 参考 https://blog.csdn.net/luozhonghua2014/article/details/80369469 https://www.jianshu.com/p/2db7abddb9e6 https://jingyan.baidu.com/article/86f4a73ed181b837d6526930.html 并且测试过了 :telnet 192.168.0.205 9092是没有问题的 在 服务器本地上面使用 命令生产消费消息是可以的。 这样就奇怪了。 于是我换成了 另一个 主题进行测试发现是没有问题的。。。 同时注意到了原来是 我写的 主题名称 后面带有空格 低级错误啊!但是后面的空格真的是空格?我自己测试手敲空格,经过测试没有问题的。也就是如果是 主题后面有空格是可以的。 那就是主题名称 后面带上了什么不可内容。 我想起来了,我是通过复制这个主题名称 的,估计复制多了什么其他内容。

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

用户登录
用户注册