一文吃透SpringBoot Admin

551 阅读2分钟

1.SpringBoot Admin介绍

生产上对 Web 应用 的监控是十分必要的。我们可以近乎实时来对应用的健康、性能等其他指标进行监控来及时应对一些突发情况。避免一些故障的发生。对于 Spring Boot 应用来说我们可以通过一个轻量级的监控工具 Spring Boot Admin (SBA) 来进行监控。

2.快速搭建

2.1 SpringBoot Admin Server

pom.xml
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency><dependency>
    <groupId>de.codecentric</groupId>
    <artifactId>spring-boot-admin-starter-server</artifactId>
    <version>2.5.1</version>
</dependency><dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency>
yml
server:
  port: 8888
spring:
  application:
    name: admin-server
启动类
@EnableAdminServer
@SpringBootApplication
public class AdminServerApplication {
​
    public static void main(String[] args) {
        SpringApplication.run(AdminServerApplication.class, args);
    }
​
}

2.2 SpringBoot Admin Client

pom
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency><dependency>
    <groupId>de.codecentric</groupId>
    <artifactId>spring-boot-admin-starter-client</artifactId>
    <version>2.5.1</version>
</dependency><dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <scope>test</scope>
</dependency><dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.24</version>
</dependency><dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
    <version>3.1.2</version>
</dependency>
yml
spring:
  application:
    name: admin-client
  boot:
    admin:
      client:
        url: http://localhost:8888  # 连接Admin Serve地址
server:
  port: 9999

management:
  endpoints:
    web:
      exposure:
        include: '*'
  endpoint:
    health:
      show-details: always

# 只有设置了该属性, SBA才会有日志控制面板
logging:
  file:
    name: D:/logs/output.log  # 地址指向logback中的输出的日志文件
logback-spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <property name="APP_Name" value="adminTest"/>
    <contextName>${APP_Name}</contextName>
    <!--定义日志文件的存储地址 勿在 LogBack 的配置中使用相对路径,请根据需求配置路径-->
    <property name="LOG_HOME" value="D:/logs"/>
​
    <!-- 彩色日志依赖的渲染类 -->
    <conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
    <conversionRule conversionWord="wex"
                    converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter"/>
    <conversionRule conversionWord="wEx"
                    converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter"/>
    <!-- 彩色日志格式 -->
    <property name="CONSOLE_LOG_PATTERN"
              value="adminTest >> ${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(LN:%L){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/>
​
    <!-- 控制台输出 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <pattern>%d [%thread][%X{traceId}] %-5p [%c] [%F:%L] - %msg%n</pattern>
            <charset>utf8</charset>
        </encoder>
    </appender>
​
    <!-- 按照每天生成日志文件 -->
    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_HOME}/output.log</file>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <!--日志文件输出的文件名-->
            <FileNamePattern>${LOG_HOME}/output-%d{yyyy-MM-dd}.log</FileNamePattern>
            <!--日志文件保留天数-->
            <MaxHistory>30</MaxHistory>
        </rollingPolicy>
​
        <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
            <!--格式化输出:%d表示日期,%thread表示线程名,%-5level:级别从左显示5个字符宽度%msg:日志消息,%n是换行符-->
            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%X{traceId}] [%thread] %-5level %logger{50}:%L - %msg%n</pattern>
        </encoder>
    </appender>
​
    <!-- 日志输出级别,注意:如果不写<appender-ref ref="FILE" /> ,将导致springboot Admin找不到文件,无法查看日志 -->
    <root level="INFO">
        <appender-ref ref="STDOUT"/>
        <appender-ref ref="FILE"/>
    </root>
</configuration>

输入 http://localhost:8888 就可以访问 admin 监控后台, 如果需要安全相关配置, 可以接入 Spring Security, 就可以解决安全性问题.

通过上面的搭建, 我们可以初步了解到SBA的功能和作用, 在此基础上, 我们还可以接入 TraceId 来保证全链路日志

3. 全链路日志

@Slf4j
public class TraceUtils {
​
    private static final String TRACE_ID = "traceId";
​
    public static void createTraceId() {
        String traceId = MDC.get(TRACE_ID);
        if (StringUtils.isBlank(traceId)) {
            traceId = UUID.randomUUID().toString().replaceAll("-", "").toLowerCase();
            log.debug("create traceId :{}", traceId);
            MDC.put(TRACE_ID, traceId);
        }
    }
​
    public static void destroyTraceId() {
        MDC.remove(TRACE_ID);
    }
}
​
​
public class TraceIdInterceptor implements HandlerInterceptor {
​
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        TraceUtils.createTraceId();
        return true;
    }
​
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        TraceUtils.destroyTraceId();
    }
}
​
​
@Component
public class OpenFeignRequestInterceptor implements RequestInterceptor {
​
    private static final String TRACE_ID = "traceId";
​
    @Override
    public void apply(RequestTemplate requestTemplate) {
        String traceId = MDC.get(TRACE_ID);
        requestTemplate.header(TRACE_ID, traceId);
    }
}
​
​
@Configuration
public class MvcConfig implements WebMvcConfigurer {
​
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new TraceIdInterceptor()).addPathPatterns("/**");
    }
}
​

需要在所有服务中均加上这种上述代码(可以封装到Starter中), 即可实现 TraceId 全链路传递, 但是这种方式有部分弊端, 如果在子线程或线程池中进行RPC调用, 这种情况TraceId就会丢失, 这个问题也可以解决, 后续文章输出相应的解决方案.

4. 总结

今天主要介绍使用 Spring Boot AdminSpring Boot 应用进行监控。 这里需要说明的是对一些小型应用 Spring Boot Admin 可以完全胜任监控功能,也非常简单好用。 但是对于大型分布式集群应用来说我个人不建议使用 Spring Boot Admin ,需要其它更加专业的 APM 监控,比如开源的 Apache SkywalkingPrometheus + Grafana 等等, 收费的有阿里云ARMS企业级应用监控。