跳至主要內容

Actuator服务监控

wangdx大约 21 分钟

Actuator 监控简介

微服务开发与调用

  • 在实际项目开发中,利用微服务可以有效的进行项目结构的拆分,将不同功能的微服务部署到不同的物理主机之中,这样当某一个微服务出现问题时就很难进行准确的定位,所以在 SpringBoot 中提供了 Actuator 组件来实现每一个微服务的状态监控,而要想使用 Actuator 组件只需要在项目中引入“spring-boot-starter-actuator”依赖库即可。

微服务监控

  • Actuator 组件是 SpringBoot 所提供的原生监控模块,提供有许多的监控端点(EndPoint),每一个端点代表不同的监控项,例如:应用配置信息、健康指标、度量指标等,这些信息都可以通过 HTTP 请求的形式获取,为了与数据操作接口有所区分一般都会另外启动一个监控端口,监控接口配置需要通过 application.yml 文件配置:

Actuator 监控端点

  • 开发者可以通过原生端点提供的信息获取程序运行时的内部状态,这些原生端点可以分为以下三类:
    • 应用配置类:可以查看微服务应用在运行期间的相关静态信息(自动配置、Beans、环境、映射等)
    • 度量指标类:获取微服务运行期间的动态信息(堆栈、请求链、健康);
    • 操作控制类:可以对微服务应用进行远程控制(例如:关闭)。
1、
project('microboot-web') { // 子模块
    dependencies { // 配置子模块依赖
        compile(project(':microboot-common')) // 引入其他子模块
        compile('org.springframework.boot:spring-boot-starter-actuator')
        compile(libraries.'hibernate-validator') // 引入依赖库
    }
}

2、
localhost/actuator

3、
server:
  port: 80
management:
  endpoints:
    web:
      exposure:
        include: "*" # 开放全部的微服务监控端口
      base-path: /actuator # 监控服务的父路径


4、
http://localhost/actuator

Actuator 接口访问

1、
localhost/actuator

2、
localhost/actuator/beans
localhost/actuator/mappings


3、
http://localhost/actuator/conditions

4、
localhost/actuator/env

heapdump 信息和 info

获取 heapdump

  • 在 Actuator 提供的监控服务中可以直接通过“/heapdump”的路径获取当前的 SpringBoot 运行的 JVM 堆内存信息,所有的信息内容都会以一个“heapdump 进制文件的形式下载

VisualVM 内存分析

  • 在 heapdump 文件中包含有对象信息、类信息、JM 可达对象、线程栈以及本地变量随后开发者就可以利用 JDK 所提供的“VisualVM”的工具进行堆内存使用分析,帮助开发者方便的找到内存泄漏的原因、重复引用的 JAR 文件或者是类程序、分析集合的使用以及类加载器的使用等信息,这样就可以清楚的掌握应用程序所使用的内存情况,从而更加合理的使用 JVM 内存空间

visualvmopen in new window

jdk 11 使用 jvisualVM visualGCopen in new window

微服务 info 信息

  • 在 SpringBoot 进行 info 信息设计时并没有进行任何的信息描述格式的定义,开发者可以依据自己的需要进行信息定义,例如:组织信息、微服务描述、开发者姓名、职位等内容。
1、
localhost/actuator/heapdump

2、
http://visualvm.github.io/

3、
spring:
  profiles:
    active: dev
management:
  info:
    env:
      enabled: true  # info端口默认时关闭需要开启
  endpoint:
    env:
      show-values: always
  endpoints:
    web:
      exposure:
        include: "*" # 开放全部的微服务监控端口
      base-path: /yix_act # 监控服务的父路径
info:
  app:
    name: 沐言科技 —— SpringBoot微服务
    group: com.yootk
    version: 2.4.3
    describe: 基于SpringBoot实现了微服务的定义,用于实现XXOO功能
    creator:
      name: 李兴华
      poistion: 教学总监

4、
localhost/actuator/info

5、
package com.yootk.actuator;

import org.springframework.boot.actuate.info.Info;
import org.springframework.boot.actuate.info.InfoContributor;
import org.springframework.stereotype.Component;

@Component // 进行组件的注册
public class MicroServiceInfoContributor implements InfoContributor { // 自定义Info构建器
    @Override
    public void contribute(Info.Builder builder) {
        builder.withDetail("company.name", "muyan-yootk"); // 添加info项
        builder.withDetail("company.url", "www.yootk.com"); // 添加info项
    }
}


6、
http://localhost/actuator/info

health 服务信息

微服务健康监控

  • 使用微服务概念进行项目拆分后,开发者就必须随时监控每一个微服务的健康状态,当某一个微服务出现了故障之后,需要及时的将故障信息反馈给微服务管理者,所以在 Actuator 中提供了“/health"健康状态查询。
  • 由于当前的微服务没有引入任何的其他服务组件,所以此时默认返回的微服务的状态信息为(如果存在故障则返回“DOWN”),但是在一个微服务项目中经常会引入大量的其他服务组件,例如:Redis 缓存组件、MYSQL 数据库服务或消息组件等,实际上这些服务的健康与否也直接影响到最终的健康状态
1、
localhost/actuator/health

2、
package com.yootk.actuator;

import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.stereotype.Component;

@Component // 依然定义为组件
public class MicroHealthIndicator implements HealthIndicator {
    // 现在是一个独立的Bean组件,就可以直接注入其他依赖
    @Override
    public Health health() { // 返回健康状态
        int errorCode = 100; // 假设存在有一个错误编码
        if (errorCode != 0) {   // 触发返回错误的条件
            return Health.down().withDetail("MicroServiceErrorCode", errorCode)
                    .withException(new Exception("服务故障!")).build(); // 当前微服务不健康
        }
        return Health.up().build(); // 当前微服务健康
    }
}


3、
localhost/actuator/health

4、
management:
  endpoint:
    health:
      show-details: always # 显示异常信息
      show-components: always # 显示组件信息

远程关闭

远程关闭

  • 微服务编写完成后常规的做法是将其打包为*.jar 文件,或者将其放在指定的容器之中运行,这样往往是通过外部的命令进行微服务的启动与关闭
1、
	curl -X POST "http://localhost/actuator/shutdown"

2、
server:
  port: 80
management:
  server:
    port: 9090
  endpoint:
    shutdown:
      enabled: true # 可以直接进行远程关闭
    health:
      show-details: always # 显示异常信息
      show-components: always # 显示组件信息
  endpoints:
    web:
      exposure:
        include: "*" # 开放全部的微服务监控端口
      base-path: /actuator # 监控服务的父路径

自定义 Endpoint

数据操作注解

  • 在引入了 Actuator 模块之后,除了可以获得内置 EndPoint 获取数据信息之外,开发者也可以根据需要配置自己的 EndPoint 处理类,而此类中需要利用“@Endpoint”注解来配置访问路径,而对应监控数据的操作形式还提供有三种不同的注解
1、
package com.yootk.actuator;

import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.boot.actuate.endpoint.annotation.Selector;
import org.springframework.context.annotation.Configuration;

import java.util.HashMap;
import java.util.Map;

@Configuration // 配置类
@Endpoint(id="muyan-endpoint") // 定义接口名称
public class YootkEndPoint {
    @ReadOperation
    public Map<String, Object> endpoint(@Selector String select) {  // 获取一些参数数据
        Map<String, Object> map = new HashMap<>();
        map.put("author", "李兴华");
        map.put("message", "沐言科技:www.yootk.com");
        map.put("select", select);
        return map;
    }
}


2、
localhost:9090/actuator/muyan-endpoint/lee

Lombok 日志注解

1、
package com.yootk.action;

import com.yootk.common.action.abs.AbstractBaseAction;
import com.yootk.vo.Message;
import org.slf4j.Logger; // 使用的是SLF4J标准
import org.slf4j.LoggerFactory;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController // 直接基于Rest架构进行处理,省略了@ResponseBody注解
@RequestMapping("/message/*") // 添加父路径
@Validated // 启用当前的JSR303注解
public class MessageAction extends AbstractBaseAction { // 控制层的实现类
    private static final Logger LOGGER = LoggerFactory.getLogger(MessageAction.class); // 获取日志实例
    @RequestMapping("echo") // 子路径
    public Object echo(Message message) { // 进行请求参数的接收以及请求内容的回应
        LOGGER.info("接收到了请求的参数:message = {}", message); // SLF4J的时候可以直接使用占位符
        message.setTitle("【ECHO】" + message.getTitle());
        message.setContent("【ECHO】" + message.getContent());
        return message;
    }
}
http://localhost/message/echo?title=沐言科技&content=www.yootk.com&pubdate=1998-09-19
2、
package com.yootk.action;

import com.yootk.common.action.abs.AbstractBaseAction;
import com.yootk.vo.Message;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger; // 使用的是SLF4J标准
import org.slf4j.LoggerFactory;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController // 直接基于Rest架构进行处理,省略了@ResponseBody注解
@RequestMapping("/message/*") // 添加父路径
@Validated // 启用当前的JSR303注解
@Slf4j // 直接进行日志的启用
// 当使用了以上的注解之后就会自动的在当前的程序类里面出现有一个log的日志对象
public class MessageAction extends AbstractBaseAction { // 控制层的实现类
    @RequestMapping("echo") // 子路径
    public Object echo(Message message) { // 进行请求参数的接收以及请求内容的回应
        log.info("接收到了请求的参数:message = {}", message); // SLF4J的时候可以直接使用占位符
        message.setTitle("【ECHO】" + message.getTitle());
        message.setContent("【ECHO】" + message.getContent());
        return message;
    }
}

SpringBoot 日志配置

日志级别

  • 在 SpringBoot 进行日志信息输出时一般都会存在有四种日志级别,按照由高到低的顺序为:ERROR(错误)、WARN(警告)、、INFO(信息)、DEBUG(调试),不同的日志级别的输出需要调用不同的处理方法。
1、
package com.yootk.action;

import com.yootk.common.action.abs.AbstractBaseAction;
import com.yootk.vo.Message;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.Logger; // 使用的是SLF4J标准
import org.slf4j.LoggerFactory;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController // 直接基于Rest架构进行处理,省略了@ResponseBody注解
@RequestMapping("/message/*") // 添加父路径
@Validated // 启用当前的JSR303注解
@Slf4j // 直接进行日志的启用
// 当使用了以上的注解之后就会自动的在当前的程序类里面出现有一个log的日志对象
public class MessageAction extends AbstractBaseAction { // 控制层的实现类
    @RequestMapping("echo") // 子路径
    public Object echo(Message message) { // 进行请求参数的接收以及请求内容的回应
        log.error("接收到了请求的参数:message = {}", message);
        log.warn("接收到了请求的参数:message = {}", message);
        log.info("接收到了请求的参数:message = {}", message);
        log.debug("接收到了请求的参数:message = {}", message);
        log.trace("接收到了请求的参数:message = {}", message);
        message.setTitle("【ECHO】" + message.getTitle());
        message.setContent("【ECHO】" + message.getContent());
        return message;
    }
}
// http://localhost/message/echo?title=沐言科技&content=www.yootk.com&pubdate=1998-09-19
// https://localhost/message/echo?title=沐言科技&content=www.yootk.com&pubdate=1998-09-19

2、
logging:
  level: debug

3、
logging:
  level: # 需要配置Map集合
    root: debug # 基本的日志级别

4、
http://localhost/message/echo?title=沐言科技&content=www.yootk.com&pubdate=1998-09-19

5、
logging:
  level: # 需要配置Map集合
    root: info # 基本的日志级别
    com.yootk.action: trace # 针对于开发包进行的日志级别配置

6、
logging:
  level: # 需要配置Map集合
    root: info # 基本的日志级别
    com.yootk.action: trace # 针对于开发包进行的日志级别配置
  file: # 定义日志文件
    path: muyan-logs # 日志的保存路径
  pattern:
    file: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level [%logger] %msg%n"  # 文件格式
    console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level [%logger] %msg%n"  # 控制台格式
%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd'T'HH:mm:ss.SSSXXX}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}

%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd'T'HH:mm:ss.SSSXXX}} ${LOG_LEVEL_PATTERN:-%5p} ${PID:- } --- [%t] %-40.40logger{39} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}

整合 Logback 日志配置文件

SpringBoot 日志配置

  • 在 SpringBoot 中默认使用了 Logback 组件进行日志管理,虽然可以通过 application.yml 进行日志的配置,但是对于一些更加细致的日志配置是无法通过 application.yml 进行配置的(例如:按天进行日志归档、自动删除等),所以在实际开发中较为常见的做法是编写具体的日志配置文件,而在 SpringBoot 中默认的日志文件名称为“logback-spring.xm!”
<?xml version="1.0" encoding="UTF-8"?>
<configuration scan="true" scanPeriod="60 seconds" debug="false">
    <contextName>logback</contextName>
    <property name="appName" value="actuator"/>
    <!-- 定义控制台输出匹配格式 -->
    <substitutionProperty name="logging.pattern.console"
                          value="%clr(%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr([%X{requestId}]) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%ewtpc}"/>
    <!-- 定义日志文件输出匹配格式 -->
    <substitutionProperty name="logging.pattern.file"
                          value="%d{${LOG_DATEFORMAT_PATTERN:-yyyy-MM-dd HH:mm:ss.SSS}} ${LOG_LEVEL_PATTERN:-%5p} %clr([%X{requestId}]) ${PID:- } --- [%t] %-40.40logger{39} : %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%ewtpc}"/>
    <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
    <conversionRule conversionWord="wtpc"
                    converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
    <conversionRule conversionWord="ewtpc"
                    converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>

    <appender name="console" class="ch.qos.logback.core.ConsoleAppender">   <!-- 控制台输出 -->
        <layout class="ch.qos.logback.classic.PatternLayout"> <!-- 使用layout节点 -->
            <pattern>${logging.pattern.console}</pattern> <!-- 格式引用 -->
        </layout>
    </appender>
    <!-- 将每天的日志保存在一个文件之中 -->
    <appender name="file" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <Prudent>true</Prudent>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 设置日志保存路径,本次按照月份创建日志目录,而后每天的文件归档到一组 -->
            <FileNamePattern>./logs/%d{yyyy-MM}/${appName}_%d{yyyy-MM-dd}.log</FileNamePattern>
            <MaxHistory>30</MaxHistory><!-- 删除超过365天的日志文件 -->
        </rollingPolicy>
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>INFO</level> <!-- 保存ERROR及以上级别的日志 -->
        </filter>
        <encoder>
            <Pattern>${logging.pattern.file}</Pattern>  <!-- 格式引用 -->
        </encoder>
    </appender>
    <appender name="druidSqlFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <Prudent>true</Prudent>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!-- 设置日志保存路径,本次按照月份创建日志目录,而后每天的文件归档到一组 -->
            <FileNamePattern>./logs/%d{yyyy-MM}/druid_${appName}_%d{yyyy-MM-dd}.log</FileNamePattern>
            <MaxHistory>30</MaxHistory><!-- 删除超过365天的日志文件 -->
        </rollingPolicy>
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>ERROR</level> <!-- 保存ERROR及以上级别的日志 -->
        </filter>
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %-5level %logger Line:%-3L - %msg%n</pattern>
            <charset>utf-8</charset>
        </encoder>
    </appender>

    <logger name="com.alibaba.druid.filter.stat.StatFilter" level="ERROR">
        <appender-ref ref="druidSqlFile"/>
    </logger>

    <logger name="com.yix.action" level="DEBUG"/>  <!-- 局部日志级别 -->

    <root level="INFO"> <!-- 全局日志级别 -->
        <appender-ref ref="console"/>
        <appender-ref ref="file"/>
    </root>
</configuration>

动态修改日志级别

项自运行时的日志等级

  • 项目中引入日志的目的是为了便于程序的执行监控,这样在程序出现问题时可以通过日志记录准确的进行错误定位,而为了避免产生过多无用的日志信息,一般可以将日志等级调高,例如:调整到 WARN 日志级别

动态调整日志等级

  • 随着项目业务逻辑的不断完善,仅仅是依靠错误日志信息实际上也很难准确的发现具体的错误位置,而为了获取更多的错误信息,就需要手工将日志级别调整到“DEBUG'而后再继续等待错误的出现并解决它,这样一来对于程序的开发或者是“TRACE”人员就会非常的繁琐。为了简化这样繁琐的代码调试步骤,从 SpringBoot 1.5 版本开始,开发者可以直接通过 Actuator 监控模块在不重启服务的情况下,实现日志级别的动态修改
1、
project('microboot-web') { // 子模块
    dependencies { // 配置子模块依赖
        compile(project(':microboot-common')) // 引入其他子模块
        compile('org.springframework.boot:spring-boot-starter-actuator')
    }
}

2、
localhost/message/echo?title=沐言科技&content=www.yootk.com&pubdate=1998-09-19

3、
localhost:9090/actuator/loggers

4、
http://localhost:9090/actuator/loggers/com.yix.action.MessageAction

5、

curl -X POST http://localhost:9090/actuator/loggers/com.yix.action.MessageAction -H "Content-Type:application/json" --data "{\"configuredLevel\": \"TRACE\"}"

MDC 全链路跟踪

传统日志处理形式

  • 个完整的用户请求处理一般都会包含有控制层、业务层以及数据层,而完善的日志处理中就需要清楚的记录不同层的操作状态。但是如果按照之前所讲解的形式,现在假设有多个用户进行并发访问时,那么最终所能够记录下来的只有在不同层之间的操作信息

MDC

  • 为了更加清楚的实现用户记录的日志处理,在良好的日志系统设计时一般都会使用 MDC(Mapped Diagnostic Context、映射调试上下文)全链路跟踪日志,在日志记录中保留有一个线程的唯一请求标记(本次假设该标记为“requestld”同时与该用户请求的所有相关日志在进行记录时都要准确的记录下这个 requestld,这样在最终进行日志分析时,就可以依据这个 requestld 进行用户操作链路的完整跟踪

MDC 集合操作

  • 为了便于用户实现 MDC 全链路跟踪,在 sIf4i 标准中提供了“orq.sf4i.MDC”程序类 MDC 可以理解为一个当前线程与 Map 的绑定集合,可以向 MDC 中为当前线程添加相应的键值对,这样在进行相关的操作处理时就可以依据当前线程获取指定 key 的数据项从而实现日志记录

1、
package com.yootk.service.impl;

import com.yootk.service.IMessageService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

@Service
@Slf4j
public class MessageServiceImpl implements IMessageService {
    @Override
    public String echo(String msg) {
        log.info("消息回应业务处理,传递的消息数据为:msg = {}", msg);
        return "【ECHO】" + msg;
    }
}


2、
package com.yootk.action;

import com.yootk.common.action.abs.AbstractBaseAction;
import com.yootk.service.IMessageService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController // 直接基于Rest架构进行处理,省略了@ResponseBody注解
@RequestMapping("/message/*") // 添加父路径
@Slf4j // 直接进行日志的启用
public class MessageAction extends AbstractBaseAction { // 控制层的实现类
    @Autowired
    private IMessageService message;
    @RequestMapping("echo") // 子路径
    public Object echo(String msg) { // 进行请求参数的接收以及请求内容的回应
        log.info("控制层接收到用户请求,请求参数为:msg = {}", msg); // 日志输出
        return this.message.echo(msg);
    }
}

3、
localhost/message/send?msg=沐言科技:www.yootk.com

4、
package com.yootk.interceptor;

import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.UUID;

@Slf4j
public class MDCInterceptor implements HandlerInterceptor { // 拦截器
    private final static String REQUEST_ID = "requestId"; // 名称不重要

    @Override
    public boolean preHandle(HttpServletRequest request,
                             HttpServletResponse response, Object handler)
            throws Exception {   // 每次请求前进行MDC绑定
        String forward = request.getHeader("X-Forwarded-For");
        String clientIp = request.getRemoteAddr();
        String uuid = UUID.randomUUID().toString();
        log.info("MDC操作记录开始:requestId = {}", uuid); // 日志输出
        log.info("requestId = {}, clientIp = {}, X-Forwarded-For = {}", uuid, clientIp, forward);
        MDC.put(REQUEST_ID, uuid); // 保存了MDC数据
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request,
                           HttpServletResponse response, Object handler, ModelAndView modelAndView)
            throws Exception {  // 每次请求后进行MDC清除
        String uuid = MDC.get(REQUEST_ID);
        log.info("MDC操作记录结束,requestId = {}", uuid);
        MDC.remove(REQUEST_ID);
    }
}

5、
package com.yootk.config;
import com.yootk.interceptor.MDCInterceptor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebInterceptorConfig implements WebMvcConfigurer {
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(this.getMDCInterceptor()).addPathPatterns("/**"); // 全链路追踪
    }
    @Bean
    public HandlerInterceptor getMDCInterceptor() {
        return new MDCInterceptor(); // 获取拦截器实例
    }
}

Actuator 可视化监控简介

Actuator 数据可视化

  • Actuator 虽然可以为管理者提供完整的 SpringBoot 环境监控数据,但是用户所能够获取到的数据内容,仅仅是当前的服务状态,却无法提供系统性的可视化监控。所以当某一个微服务执行性能下降时是很难通过当前的状态直观的发现微服务存在问题,最佳的做法是需要对这些监控数据进行持续的记录,并以监控图示的方式展现给用户

服务监控

  • 在实际的项目管理中,一般监控数据会由两个基本组成:微服务监控数据(Actuator)与服务主机数据(NodeExport),所以对于被监控的主机中就需要通过相应的服务进行配置,而所有捕获到的数据项可以按照获取的时间顺序存储在 Prometheus 数据文件中,最终再结合 Prometheus 提供的 WEB 控制台实现监控数据的可视化显示,而如果要想获取更加丰富的数据可视化的形式,,则可以借助 Gragana 工具。而除了监控数据的显示之外,最重要的就是服务的预警功能,例如:当微服务突然中断或者服务器资源占用超标时都应该及时的将警告信息发送给运维人员,进行及时调整。

1、
192.168.190.151	microboot-producer
192.168.190.152	microboot-prometheus
192.168.190.153	microboot-grafana

C:\Windows\System32\drivers\etc\hosts

2、
vi /etc/hostname

3、
vi /etc/sysconfig/network-scripts/ifcfg-ens33

4、
vi /etc/hosts

5、
reboot

NodeExporter

NodeExporter 组件

  • 项目生产环境中,需要将微服务部署到相应的服务主机之中,那么此时除了要进行微服务自身的状态监控之外,实际上也需要可以准确的获取当前服务主机的状态信息,例如 CPU 使用率、内存使用率、网络使用率、缓存使用率、磁盘使用率等等,那么就可以通过 NodeExporter 组件获取这些信息,同时这些信息也与 Prometheus 无缝衔接
1、
https://prometheus.io/

2、
tar xzvf /var/ftp/node_exporter-1.1.2.linux-amd64.tar.gz -C /usr/local/

3、
mv /usr/local/node_exporter-1.1.2.linux-amd64/ /usr/local/node_exporter

4、
vi /lib/systemd/system/node_exporter.service


5、
[Unit]
Description=Node_Exporter Service

[Service]
User=root
ExecStart=/usr/local/node_exporter/node_exporter
TimeoutStopSec=10
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

[Unit]
Description=Node Exporter
Wants=network-online.target
After=network-online.target

[Service]
ExecStart=/data/server/node_exporter/node_exporter  --web.listen-address=:9100 --collector.filesystem.mount-points-exclude="^/(dev|proc|run|boot|run/credentials/.+|sys|data/kubelet/.+|sys|data/docker.+|sys|var/lib/.+)($|/)"
StandardOutput=syslog
StandardError=syslog
SyslogIdentifier=node_exporter
[Install]
WantedBy=default.target
6、
systemctl daemon-reload



7、
systemctl disable node_exporter.service

8、
systemctl start node_exporter.service

9、
systemctl stop node_exporter.service

10、
netstat -nptl

11、
firewall-cmd --zone=public --add-port=9100/tcp --permanent

12
firewall-cmd --reload

Prometheus 监控数据

获取 Prometheus 监控信息

  • 如果要想将当前的微服务程序与 Prometheus 进行整合,那么微服务必须要提供有符合于 Prometheus 标准的监控数据信息,这样就需要在项目中引入“micrometer-registry-prometheus”依赖库。这样就会在当前的微服务中提供有一个名称为 7prometheus”的新的 Actuator 监控终端,监控者可以该终端获取当前微服务运行下的 JVM、日志、Tomcat 等监控数据
1、
// https://mvnrepository.com/artifact/io.micrometer/micrometer-registry-prometheus
implementation group: 'io.micrometer', name: 'micrometer-registry-prometheus', version: '1.6.5'
// https://mvnrepository.com/artifact/io.micrometer/micrometer-registry-prometheus
implementation group: 'io.micrometer', name: 'micrometer-registry-prometheus', version: '1.12.4'


2、
management:
  server:
    port: 9090
  endpoints:
    web:
      exposure:
        include: "*" # 开放全部的微服务监控端口
      base-path: /actuator # 监控服务的父路径


3、
gradle bootJar

4、
nohup java -jar /var/ftp/yootkboot-1.0.0-lee.jar > /usr/local/yootk.log 2>&1 &

mkdir -p /mnt/project/logs/actuator/
nohup /usr/local/jdk/jdk17/bin/java -jar /mnt/project/boot/actuator/actuator-1.0.0-lee.jar > /mnt/project/logs/actuator/actuator.log 2>&1 &

5、
firewall-cmd --zone=public --add-port=80/tcp --permanent
firewall-cmd --zone=public --add-port=9090/tcp --permanent


6、
firewall-cmd --reload


7、
microboot-producer:9090/actuatotr

8、
http://microboot-producer:9090/actuator

Prometheus 服务搭建

1、
wget https://github.com/prometheus/prometheus/releases/download/v2.26.0/prometheus-2.26.0.linux-amd64.tar.gz

2、
tar xzvf /var/ftp/prometheus-2.26.0.linux-amd64.tar.gz -C /usr/local/

3、
mv /usr/local/prometheus-2.26.0.linux-amd64/ /usr/local/prometheus

4、
vi /usr/local/prometheus/prometheus.yml

5、
global:
  scrape_interval:     15s # Set the scrape interval to every 15 seconds. Default is every 1 minute.
  evaluation_interval: 15s # Evaluate rules every 15 seconds. The default is every 1 minute.
  # scrape_timeout is set to the global default (10s).


scrape_configs:
  - job_name: 'prometheus'
    static_configs:
    - targets: ['microboot-prometheus:9999']
  - job_name: 'node'
    static_configs:
    - targets: ['microboot-producer:9100']
      labels:
        instance: microboot-producer-node
  - job_name: 'microboot'
    scrape_interval: 10s
    scrape_timeout: 5s
    metrics_path: '/actuator/prometheus'
    static_configs:
       - targets: ['microboot-producer:9090']

6、
http://microboot-producer:9100/metrics
http://microboot-producer:9090/actuator/prometheus

7、
/usr/local/prometheus/promtool check config /usr/local/prometheus/prometheus.yml
8、
vi /usr/lib/systemd/system/prometheus.service


9、
[Unit]
Description=prometheus server daemon

[Service]
User=root
ExecStart=/usr/local/prometheus/prometheus \
	--config.file=/usr/local/prometheus/prometheus.yml \
	--storage.tsdb.path=/www/data/prometheus/data \
	--web.listen-address=0.0.0.0:9101 --web.enable-lifecycle
TimeoutStopSec=10
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

[Unit]
Description=prometheus server daemon

[Service]
Restart=on-failure
ExecStart=/data/server/prometheus/prometheus
--config.file=/data/server/prometheus/prometheus.yml
--storage.tsdb.path=/data/prometheus/server/data
--storage.tsdb.retention.t
ime=120d  --web.enable-lifecycle
[Install]
WantedBy=multi-user.target
10、
systemctl daemon-reload

11、
systemctl enable prometheus

12、
systemctl start prometheus

13、
systemctl status prometheus

14、
netstat -nptl

15、firewall-cmd --zone=public --add-port=9999/tcp --permanent

16、firewall-cmd --reload

17、microboot-prometheus:9999

Grafana 可视化

Grafana

  • 虽然 Prometheus 提供了数据可视化的监控界面,但是其本身所提供的数据可视化的能力相对较弱,所以在实际的项目开发中都会基于 Prometheus 进行数据采集,而后再利用 Grafana 实现更友好、更贴近生产环境的监控数据可视化平。开发者可以直接通过 Grafana 官网下载所需要的组件,官网地址为:https://grafana.com/
1、
https://grafana.com/

2、
cd /var/ftp/

3、
wget https://dl.grafana.com/oss/release/grafana-7.5.4.linux-amd64.tar.gz

4、
mv /usr/local/grafana-7.5.4/ /usr/local/grafana

5、

vi /usr/lib/systemd/system/grafana.service

[Unit]
Description=Grafana Service

[Service]
User=root
ExecStart=/usr/local/grafana/bin/grafana-server \
	-config /usr/local/grafana/conf/defaults.ini -homepath /usr/local/grafana
TimeoutStopSec=10
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target


6、
systemctl daemon-reload
systemctl enable grafana

7、
firewall-cmd --zone=public --add-port=3000/tcp --permanent
firewall-cmd --reload


8、
http://microboot-prometheus:9999

9、

监控警报

项目警报管理

  • 在 Prometheus 工具中除了可以进行监控数据的存储之外,实际上还提供有服务的报警支持,利用获取到的监控参数进行相应的计算与判断,就可以在服务发生问题时或者准备发生问题时获取相应的预警信息,这样就可以将问题及时的反馈到运维人员或项目开发人员以方便及时修复

1、
xtyrperyvtncbbif

2、
wget https://github.com/prometheus/alertmanager/releases/download/v0.21.0/alertmanager-0.21.0.linux-amd64.tar.gz

3、
tar xzvf /var/ftp/alertmanager-0.21.0.linux-amd64.tar.gz -C /usr/local/

4、
mv /usr/local/alertmanager-0.21.0.linux-amd64 /usr/local/alertmanager

5、
vi /usr/local/alertmanager/alertmanager.yml

6、
global:
  resolve_timeout: 5m
  smtp_smarthost: 'smtp.qq.com:25'
  smtp_from: '784420216@qq.com'
  smtp_auth_username: '784420216@qq.com'
  smtp_auth_password: 'xtyrperyvtncbbif'
  smtp_require_tls: false

route:
  group_by: ['alertname']
  group_wait: 10s
  group_interval: 10s
  repeat_interval: 1h
  receiver: 'mail'
receivers:
- name: 'mail'
  email_configs:
  - to: '784420216@qq.com

7、
/usr/local/alertmanager/amtool check-config /usr/local/alertmanager/alertmanager.yml

8、
vi /usr/lib/systemd/system/alertmanager.service


[Unit]
Description=Grafana Service

[Service]
User=root
ExecStart=/usr/local/alertmanager/alertmanager \
	--config.file=/usr/local/alertmanager/alertmanager.yml
TimeoutStopSec=10
Restart=on-failure
RestartSec=5

[Install]
WantedBy=multi-user.target

9、
netstat -nptl

10、

firewall-cmd --zone=public --add-port=9093/tcp --permanent
firewall-cmd --zone=public --add-port=9094/tcp --permanent
firewall-cmd --reload

11、
alerting:
  alertmanagers:
  - static_configs:
    - targets:
       - microboot-prometheus:9093

rule_files:
  - "rules/*.yml"


12、
mkdir -p /usr/local/prometheus/rules

13、
vi /usr/local/prometheus/rules/microboot-acutator-rule.yml

14、
groups:
- name: microboot.actuator.rules
  rules:
  - alert: MicrobootInstanceDown
    expr: up{job="microboot"} == 0
    for: 1m
    labels:
      severity: warning
    annotations:
      description: "微服务 {{ $labels.instance }} 关闭"
      summary: "运行在 {{ $labels.instance }} 主机中的 {{ $labels.job }} 微服务已经关闭了!"

15、vi /usr/local/prometheus/rules/microboot-node.yml

16、
groups:
- name: microboot.node.rules
  rules:
  - alert: NodeCPUUsage
    expr: 100 - (avg(irate(node_cpu_seconds_total{mode="idle"}[5m])) by (instance) * 100) > 80
    for: 2m
    labels:
      severity: warning
    annotations:
      summary: "微服务运行主机 {{ $labels.instance }} 中的CPU使用率过高"
      description: "微服务运行主机 {{ $labels.instance }} 中的CPU使用大于80%,当前值: “{{ $value }}”"
  - alert: NodeMemoryUsage
    expr: 100 - (node_memory_MemFree_bytes+node_memory_Cached_bytes+node_memory_Buffers_bytes) / node_memory_MemTotal_bytes * 100 > 80
    for: 2m
    labels:
      severity: warning
    annotations:
      summary: "微服务运行主机 {{ $labels.instance }} 中的内存使用率过高"
      description: "微服务运行主机 {{ $labels.instance }} 内存使用大于 80%,当前值: {{ $value }}"
  - alert: NodeFilesystemUsage
    expr: 100 - (node_filesystem_free_bytes{fstype=~"ext4|xfs"} / node_filesystem_size_bytes{fstype=~"ext4|xfs"} * 100) > 90
    for: 2m
    labels:
      severity: warning
    annotations:
      summary: "微服务运行主机 {{ $labels.instance }}中的“{{ $labels.mountpoint }}” 分区使用过高"
      description: "微服务运行主机 {{ $labels.instance }} 中 {{ $labels.mountpoint }} 分区使用大于80%,当前值: {{ $value }}"


17、
systemctl restart prometheus

18、
http://microboot-prometheus:9999/targets

警报触发测试

微服务状态警报

  • 关闭当前系统中运行的 SpringBoot 应用,此时会触发“MicrobootInstanceDown 报警配置,稍等片刻后可以在 Prometheus 控制台中发现报警状态的已经处于'pending”状态,如果在此期间内问题始终没有修复,则警报会继续升级,最终当警报处于“firing”状态时就会自动的向绑定邮箱发送邮件
1、
nohup java -jar /var/ftp/yootkboot-1.0.0-lee.jar > /usr/local/yootk.log 2>&1 &

2、
wget https://download-ib01.fedoraproject.org/pub/epel/7/x86_64/Packages/s/stress-1.0.4-16.el7.x86_64.rpm

3、
rpm -ivh /var/ftp/stress-1.0.4-16.el7.x86_64.rpm

4、
stress -c 24 --timeout 600

5、

stress -m 200 --vm-bytes 2000M

6、
stress -d 200 --hdd-bytes 2g

demo


上次编辑于: