1.1 Spring Boot Actuator 概述
Spring Actuator 是 Spring Boot 给我提供的重要的 module,方便我们去构建我们的微服务架构,帮我们解决对微服务应用监控系统监控和运维。
1.2 Spring Boot Actuator 术语
Spring Boot 中关于监控有两个术语:Endpoint 和 Actuator。
Endpoint
中文含义为端点。在 Spring Boot 中 Endpoint 负责对外暴露服务,负责实现一个具体的业务监控指标,提供具体实现,类似于接口,其端点映射到一个 URL,可以采用 HTTP 或者 JMX 协议进行访问,从而实现监控与应用的交互。
Actuator
中文含义为执行器。在 Spring Boot 中 Actuator 负责对 Endpoint 进行自动装配、协议解析、服务调度,是 Endpoint 的功能集合。它集成了很多的监控功能于一体,由于做了大量的底层工作,使得对 Endpoint 的访问和扩展非常方便。
1.3 Spring Boot Actuator 内置Endpoint
HTTP方法 | 路径 | 描述 | 是否敏感信息 |
---|---|---|---|
GET | /autoconfig | 查看自动配置的使用情况, 显示一个auto-configuration的报告,该报告展示所有auto-configuration候选者及它们被应用或未被应用的原因 | true |
GET | /configprops | 查看配置属性,包括默认配置, 显示一个所有@ConfigurationProperties的整理列表 | true |
GET | /beans | bean及其关系列表, 显示一个应用中所有Spring Beans的完整列表 | true |
GET | /dump | 打印线程栈 | true |
GET | /env | 查看所有环境变量 | true |
GET | /env/{name} | 查看具体变量值 | true |
GET | /health | 查看应用健康指标, 当使用一个未认证连接访问时显示一个简单的’status’,使用认证连接访问则显示全部信息详情 | false |
GET | /info | 查看应用信息 | false |
GET | /mappings | 查看所有url映射, 即所有@RequestMapping路径的整理列表 | true |
GET | /metrics | 查看应用基本指标 | true |
GET | /metrics/{name} | 查看具体指标 | true |
POST | /shutdown | 关闭应用,允许应用以优雅的方式关闭(默认情况下不启用) | true |
GET | /trace | 查看基本追踪信息,默认为最新的一些HTTP请求 | true |
实际上官方提供了 Endpoint 有很多,我们可以在 official documentation 上面看到完整的列表。
1.4 Spring Boot Actuator 快速开始
为了快速入手,建立一个 Hello Actuator 的 Web 工程。
建立模块
- module 名称:springboot-endpoints
- module 性质:Web 工程
引入依赖包
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
</dependencies>
配置文件
- application.yml
#spring配置
spring:
#应用配置
application:
name: springboot-endpoints
profiles:
active: web,server,actuator
#控制台配置
output:
ansi:
enabled: always
#配置应用信息
info:
app:
name: "springboot-endpoints"
description: "springboot-endpoints"
version: "0.0.1-SNAPSHOT"
- application-server.yml
#服务配置
server:
port: 8080
servlet:
context-path: /springboot-endpoints
- application-actuator.yml
management:
server:
# 指定监端点暴露监听端口
port: 10101
配置日志
<configuration>
<!--引用 starter 中 Log.be 的配置-->
<springProperty scope="context" name="APP_NAME" source="spring.application.name" defaultValue="mon-be"/>
<conversionRule conversionWord="clr"
converterClass="org.springframework.boot.logging.logback.ColorConverter"/>
<!-- log.be 输出格式 -->
<property name="CONSOLE_LOG_PATTERN" value="%clr(%d{yyyy-MM-dd HH:mm:ss}]|[%-4level]|[%X{trace_id}]|[%X{biz_id}]|[%X{httpUri}]|[%thread]|[%logger{30}:\(%line\)]|[-- %msg %n)"/>
<property name="FILE_LOG_PATTERN" value="${FILE_LOG_PATTERN:-%d{yyyy-MM-dd HH:mm:ss}]|[%-4level]|[%X{trace_id}]|[%X{biz_id}]|[%X{httpUri}]|[%thread]|[%logger{30} %line]|[-- %msg %ex%n}"/>
<include resource="org/springframework/boot/logging/logback/base.xml" />
<!--自定义配置-->
<logger name="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping" level="Trace"/>
<logger name="org.springframework.boot.actuate.endpoint.web.servlet.WebMvcEndpointHandlerMapping" level="Trace"/>
<logger name="org.springframework.web.servlet.DispatcherServlet" level="Trace"/>
</configuration>
启动服务
启动成功之后我们可以看到控制台的日志如下通过Http访问
http://localhost:10101/actuator
{
_links: {
self: {
href: "http://localhost:10101/actuator",
templated: false
},
health-path: {
href: "http://localhost:10101/actuator/health/{*path}",
templated: true
},
health: {
href: "http://localhost:10101/actuator/health",
templated: false
},
info: {
href: "http://localhost:10101/actuator/info",
templated: false
}
}
}
通过JMX访问
访问应用 info端点
http://localhost:10101/actuator/info
{
app: {
name: "springboot-endpoints",
description: "springboot-endpoints",
version: "0.0.1-SNAPSHOT"
}
}
访问应用 health端点
http://localhost:10101/actuator/health
{
status: "UP"
}
访问应用指标端点
http://localhost:10101/actuator/metrics
默认配置下,Spring Boot Actuator Http 服务只暴露了监控检测,应用信息两个端点,info端点 health端点 ,其他端点需要配置
{
timestamp: "2022-08-21T01:52:57.026+00:00",
status: 404,
error: "Not Found",
message: "",
path: "/actuator/metrics"
}
小结
- 依托 Spring Boot 实现监控需求只需要引入 spring-boot-starter-actuator 模块,如果不进行端点功能扩展,Actuator 模块本身就内置了很多关于监控端点的实现,在使用的时候某些端点需要稍加配置,有的则直接使用;
- 采用 HTTP 协议访问监控端点,其访问根路径是 /actuator,由于监控端点和业务功能是集成在一个微服务里面,建议业务访问的 REST 接口不要采用 Actuator 作为 URL 的前缀;
- Spring Boot 内置的监控端点其输出内容是可以扩展的,比如 /actuator/info 这个端点就可以在配置文件中进一步配置丰富其输出内容。
1.5 Spring Boot Actuator 端点全局配置
Spring Boot 2.0 默认没有开启全部端点。通过修改 application-actuator.yml 配置
management:
server:
# 指定监端点暴露监听端口
port: 10101
#配置不同类型(web)
endpoints:
# 配置web方式暴露端点
web:
# 配置通过http方式暴露端点的根路径
base-path: "/actuator"
# 配置http方式暴露端点
exposure:
#配置暴露的端点,配置"*"暴露全部
include: '*'
#配置不暴露的端点
exclude:
# 配置jms方式暴露端点
jmx:
# 配置jms方式暴露端点
exposure:
#配置暴露的端点,配置"*"暴露全部
include: '*'
#配置不暴露的端点
exclude:
启动服务
启动成功之后我们可以看到控制台的日志如下
通过Http访问
http://localhost:10101/actuator
{
_links: {
self: {
href: "http://localhost:10101/actuator",
templated: false
},
beans: {
href: "http://localhost:10101/actuator/beans",
templated: false
},
caches-cache: {
href: "http://localhost:10101/actuator/caches/{cache}",
templated: true
},
caches: {
href: "http://localhost:10101/actuator/caches",
templated: false
},
health: {
href: "http://localhost:10101/actuator/health",
templated: false
},
health-path: {
href: "http://localhost:10101/actuator/health/{*path}",
templated: true
},
info: {
href: "http://localhost:10101/actuator/info",
templated: false
},
conditions: {
href: "http://localhost:10101/actuator/conditions",
templated: false
},
configprops: {
href: "http://localhost:10101/actuator/configprops",
templated: false
},
env: {
href: "http://localhost:10101/actuator/env",
templated: false
},
env-toMatch: {
href: "http://localhost:10101/actuator/env/{toMatch}",
templated: true
},
logfile: {
href: "http://localhost:10101/actuator/logfile",
templated: false
},
loggers: {
href: "http://localhost:10101/actuator/loggers",
templated: false
},
loggers-name: {
href: "http://localhost:10101/actuator/loggers/{name}",
templated: true
},
heapdump: {
href: "http://localhost:10101/actuator/heapdump",
templated: false
},
threaddump: {
href: "http://localhost:10101/actuator/threaddump",
templated: false
},
metrics-requiredMetricName: {
href: "http://localhost:10101/actuator/metrics/{requiredMetricName}",
templated: true
},
metrics: {
href: "http://localhost:10101/actuator/metrics",
templated: false
},
scheduledtasks: {
href: "http://localhost:10101/actuator/scheduledtasks",
templated: false
},
mappings: {
href: "http://localhost:10101/actuator/mappings",
templated: false
}
}
}
注意:通过 HTTP 协议,可以使用 *
打开所有的端点,但在具体访问某些端点的时候,由于安全和敏感性等原因,还需要增加额外的配置才行。比如用来关闭应用的端点 /actuator/shutdown,还需要增加如下额外配置,在实际访问的时候,还必须使用 post 方法才可以。
通过修改 application-actuator.yml 配置
management:
server:
# 指定监端点暴露监听端口
port: 10101
#配置不同类型(web)
endpoints:
# 配置web方式暴露端点
web:
# 配置通过http方式暴露端点的根路径
base-path: "/actuator"
# 配置http方式暴露端点
exposure:
#配置暴露的端点,配置"*"暴露全部
include: '*'
#配置不暴露的端点
exclude:
# 配置jms方式暴露端点
jmx:
# 配置jms方式暴露端点
exposure:
#配置暴露的端点,配置"*"暴露全部
include: '*'
#配置不暴露的端点
exclude:
#配置指定端点
endpoint:
# 配置shutdown 端点
shutdown:
#启用shutdown
enabled: true
1.6 Spring Boot Actuator 原生端端点
应用 info端点
配置应用信息
#配置应用信息
info:
app:
name: "springboot-endpoints"
description: "springboot-endpoints"
version: "0.0.1-SNAPSHOT"
访问应用 info端点
http://localhost:10101/actuator/info
{
app: {
name: "springboot-endpoints",
description: "springboot-endpoints",
version: "0.0.1-SNAPSHOT"
}
}
应用日志端点
查询应用所有日志
http://localhost:10101/actuator/loggers
{
levels: [
"OFF",
"ERROR",
"WARN",
"INFO",
"DEBUG",
"TRACE"
],
loggers: {
ROOT: {
configuredLevel: "INFO",
effectiveLevel: "INFO"
},
...
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping: {
configuredLevel: "TRACE",
effectiveLevel: "TRACE"
},
org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping: {
configuredLevel: null,
effectiveLevel: "INFO"
},
org.springframework.boot.actuate.endpoint.web.servlet.AbstractWebMvcEndpointHandlerMapping$WebMvcEndpointHandlerMethod: {
configuredLevel: null,
effectiveLevel: "INFO"
},
org.springframework.boot.actuate.endpoint.web.servlet.ControllerEndpointHandlerMapping: {
configuredLevel: null,
effectiveLevel: "INFO"
},
....
}
}
查询应用指定日志
{
configuredLevel: "TRACE",
effectiveLevel: "TRACE"
}
修改应用日志级别
- 发送一个Post请求,这里请求路径中填写日志名称,参数configuredLevel添加日志修改后日志级别
- 通过请求重新查看日志修改后日志级别
{
configuredLevel: "INFO",
effectiveLevel: "INFO"
}
为什么是 configuredLevel ? 这里留个悬念,具体可以查看 LoggersEndpoint
应用健康检测端点
1.7 Spring Boot Actuator 自定义端点
参考官方文档说明,如果要实现一个端点,需要用 @Endpoint 注解进行标注,在对象内部定义方法,实现端点的具体功能。
阅读官方文档
打开官方文档《自定义端点 Endpoint-“53.7 Implementing Custom Endpoints”》,关键部分摘录如下:
[^]: If you add a @Bean annotated with@Endpoint, any methods annotated with@ReadOperation,@WriteOperation, or@DeleteOperationare automatically exposed over JMX and, in a web application, over HTTP as well. Endpoints can be exposed over HTTP using Jersey, Spring MVC, or Spring WebFlux.You can also write technology-specific endpoints by using @JmxEndpointor @WebEndpoint. These endpoints are restricted to their respective technologies. For example,@WebEndpointis exposed only over HTTP and not over JMX. … …
翻译
[^]: 如果添加用 @Endpoint 注解的 @Bean,那么使用 @ReadOperation、@WriteOperation 或 @DeleteOperation 注解来定义的方法会自动通过 JMX 协议对外公开。当然在 Web 应用中,最好通过 HTTP 协议暴露服务,我们可以使用 Jersey、Spring MVC 或 Spring WebFlux 通过 HTTP 公开端点,使其具备 HTTP 访问能力。您也可以直接使用 @JmxEndpoint 或 @WebEndpoint 注解定义端点,那么这些端点就具备对应协议的访问能力,这些端点仅限于各自的技术。例如,@WebEndpoint 只通过 HTTP 协议访问而不通过 JMX 访问。
自定义应用运行时间的 Endpoint
**自定义应用运行时间的 Endpoint **
package com.wuhao.example.endpoint;
import org.springframework.boot.actuate.endpoint.annotation.DeleteOperation;
import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
import org.springframework.boot.actuate.endpoint.annotation.Selector;
import org.springframework.boot.actuate.endpoint.annotation.WriteOperation;
import org.springframework.boot.actuate.endpoint.web.annotation.WebEndpoint;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
/**
* 自定义端点:返回程序已经持续运行的时间
*
* @Author: wuhao.w
* @Date: 2020/8/17 20:47
*/
@Component
@WebEndpoint(id = "runtime")
public class RuntimeEndpoint {
public long starttime = 0L;
public Map runtimeMap = new HashMap<>();
public RuntimeEndpoint() {
starttime = System.currentTimeMillis();
runtimeMap.put("startTime", starttime);
runtimeMap.put("currentTime", System.currentTimeMillis());
runtimeMap.put("interval", 0);
}
@ReadOperation
public Map runtime() {
long currentTime = System.currentTimeMillis();
long interval = currentTime - starttime;
runtimeMap.put("currentTime", currentTime);
runtimeMap.put("interval", interval);
return runtimeMap;
}
@ReadOperation
public Map runtimeKey(@Selector String name) {
Map runtimeKey = new HashMap<>();
runtimeKey.put(name, runtimeMap.get(name));
return runtimeKey;
}
@WriteOperation
public void writeRuntimeKey(@Selector String name, String runtimepoint) {
runtimeMap.put(name, runtimepoint);
}
@DeleteOperation
public void deleteRuntimeKey(@Selector String name) {
runtimeMap.remove(name);
}
}
@WebEndpoint 注解添加到自定义端点类上包装成具备 HTTP 访问能力端点 (配置端点类)
-
@WebEndpoint 标注的类的方法中添加如下三个注解完成 端点配置 (配置端点类)
@ReadOperation : 对应HTTP GET请求,查询端点数据
@WriteOperation : 对应HTTP POST请求,修改端点数据
DeleteOperation : 对应HTTP DELETE请求,删除端点数据
@Selector 标注@ReadOperation方法中描述端点数据的子集 (配置端点类)
自定义端点装配类
package com.wuhao.springboot.endpoint;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* 自定义端点装配类
*
* @Author: wuhao.w
* @Date: 2022/8/21 12:48
*/
@Configuration
public class RuntimeEndpointAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public RuntimeEndpoint runtimeEndpoint() {
return new RuntimeEndpoint();
}
}
建立 META-INF/spring.factories 配置文件
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.wuhao.springboot.endpoint.RuntimeEndpointAutoConfiguration
启动工程访问
http://localhost:10101/actuator/
{
_links: {
self: {
href: "http://localhost:10101/actuator",
templated: false
},
runtime-arg0: {
href: "http://localhost:10101/actuator/runtime/{arg0}",
templated: true
},
runtime: {
href: "http://localhost:10101/actuator/runtime",
templated: false
},
....
}
}
这里出现了两个新的端点分别对应
@ReadOperation
public Map runtime() {
long currentTime = System.currentTimeMillis();
long interval = currentTime - starttime;
runtimeMap.put("currentTime", currentTime);
runtimeMap.put("interval", interval);
return runtimeMap;
}
@ReadOperation
public Map runtimeKey(@Selector String name) {
Map runtimeKey = new HashMap<>();
runtimeKey.put(name, runtimeMap.get(name));
return runtimeKey;
}
@ReadOperation
访问 http://localhost:10101/actuator/runtime
- 查看应用运行时间端点全部数据
{
currentTime: 1661055951216,
startTime: 1661054901799,
interval: 1049417
}
访问 http://localhost:10101/actuator/runtime/runtimeKey?name=startTime
- 查看应用运行时间端点指定数据
{
startTime: 1661054901799
}
@WriteOperation
访问 http://localhost:10101/actuator/runtime/writeRuntimeKey
- 修改应用运行时间端点数据
http://localhost:10101/actuator/runtime/runtimeKey?name=lastCheckTime
{
lastCheckTime: "2021-06-21 00:00:00"
}
@DeleteOperation
访问 http://localhost:10101/actuator/runtime/deleteRuntimeKey?name=lastCheckTime
访问 http://localhost:10101/actuator/runtime/
{
currentTime: 1661057004163,
startTime: 1661054901799,
interval: 2102364
}