Actuator帮助我们监控和管理Spring Boot 应用。
一. SpringBoot Actuator
1. 集成Actuator
1.1 build.gradle
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-actuator', version: springBootVersion
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-security', version: springBootVersion
1.2 applicaiton.properties
management.security.enabled=false
management.endpoints.web.exposure.include=*
management.endpoint.health.showDetails=always
1.3 代码
1.3.1 actuator代码
import org.springframework.boot.actuate.autoconfigure.security.servlet.EndpointRequest; import org.springframework.boot.actuate.health.HealthEndpoint; import org.springframework.boot.actuate.info.InfoEndpoint; import org.springframework.context.annotation.Configuration; import org.springframework.core.annotation.Order; import org.springframework.security.config.annotation.web.builders.HttpSecurity; import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; @Configuration @Order(1) class ActuatorWebSecurityConfiguration extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { http.antMatcher("/actuator/**") .authorizeRequests() // NOSONAR .requestMatchers(EndpointRequest.to(HealthEndpoint.class, InfoEndpoint.class)) .permitAll() .anyRequest() .authenticated() .and() .formLogin(); }
//以下时需要账号密码登录方式,
//另外需要额外在applicaiton.properties中配置登录账号密码:spring.security.user.name=actuator spring.security.user.password=actuator
// http
// .authorizeRequests()
// .requestMatchers(EndpointRequest.toAnyEndpoint())
// .hasRole("ADMIN")
// .antMatchers("/")
// .permitAll()
// .antMatchers("/**")
// .authenticated()
// .and()
// .httpBasic();
// }
// }
}
1.3.2 actuator集成mongo监控代码(监控多个mongo clusters)
自定义健康监控:CustomHealthIndicator
package com.test.ark.mls.actuator; import com.test.ark.mls.config.PrimaryMongoConfig; import com.test.ark.mls.config.SecondaryMongoConfig; import com.mongodb.client.MongoDatabase; import lombok.extern.slf4j.Slf4j; import org.bson.Document; import org.json.JSONObject; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.actuate.health.AbstractHealthIndicator; import org.springframework.boot.actuate.health.Health; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.stereotype.Component; @Component @Slf4j public class CustomHealthIndicator extends AbstractHealthIndicator { @Autowired private PrimaryMongoConfig primaryMongoConfig; @Autowired private SecondaryMongoConfig secondaryMongoConfig; @Autowired @Qualifier(value="primaryMongoTemplate") private MongoTemplate primaryMongoTemplate; @Autowired @Qualifier(value="secondMongoTemplate") private MongoTemplate secondMongoTemplate; public CustomHealthIndicator(PrimaryMongoConfig primaryMongoConfig) { this.primaryMongoConfig = primaryMongoConfig; } @Override protected void doHealthCheck(Health.Builder builder) throws Exception { MongoDatabase database = primaryMongoTemplate.getDb(); Document doc = new Document(); doc.append("dbStats", 1); doc.append("scale", 1); Document collStatsResults = database.runCommand(doc); JSONObject jsonValue = new JSONObject(collStatsResults.toJson()); MongoDatabase database2 = secondMongoTemplate.getDb(); Document doc2 = new Document(); doc2.append("dbStats", 1); doc2.append("scale", 1); Document collStatsResults2 = database2.runCommand(doc2); JSONObject jsonValue2 = new JSONObject(collStatsResults2.toJson()); builder .up() .withDetail( "PrimaryMongoAddress", primaryMongoConfig.getUri()) .withDetail("PrimaryMongoStats", jsonValue.toString()) .withDetail( "SecondMongoAddress", secondaryMongoConfig.getUri()) .withDetail("SecondMongoStats", jsonValue2.toString()); } }
PrimaryMongoConfig
@Configuration @Data @Immutable @EqualsAndHashCode(callSuper = false) @Slf4j public class PrimaryMongoConfig extends AbstractMongoConfig { @Value("${spring.data.mongodb.database}") private String database; @Value("${spring.data.mongodb.uri}") private String uri; @Autowired private CyberarkUtil cyberarkUtil; @Primary @Override @Bean(name="primaryMongoTemplate") public MongoTemplate getMongoTemplate() throws Exception { String newUri = uri.replace("password", cyberarkUtil.retrievePassword()); return new MongoTemplate(mongoDatabaseFactory(newUri, database)); } public boolean pingPrimaryMongo(MongoTemplate mongoTemplate) { try { mongoTemplate.find(new Query(), Map.class); log.info("ping mongo success"); return true; } catch (Exception e) { log.error("ping mongo with error: ", e); return false; } } @Bean public GridFsTemplate gridFsTemplate(MappingMongoConverter mappingMongoConverter) throws Exception { String newUri = uri.replace("password", cyberarkUtil.retrievePassword()); return new GridFsTemplate(mongoDatabaseFactory(newUri, database), mappingMongoConverter); } }
SecondMongoConfig
@Configuration @Data @Immutable @EqualsAndHashCode(callSuper = false) @Slf4j public class SecondaryMongoConfig extends AbstractMongoConfig { @Value("${spring.data.mongodb.database2}") private String database; @Value("${spring.data.mongodb.uri2}") private String uri; @Value("${spring.data.mongodb.password2}") private String password; @Override @Bean(name = "secondMongoTemplate") public MongoTemplate getMongoTemplate() throws Exception { String newUri = uri.replace("password", password); return new MongoTemplate(mongoDatabaseFactory(newUri, database)); } }
AbstractMongoConfig
import com.mongodb.ConnectionString; import com.mongodb.client.MongoClients; import org.springframework.data.mongodb.MongoDatabaseFactory; import org.springframework.data.mongodb.core.MongoTemplate; import org.springframework.data.mongodb.core.SimpleMongoClientDatabaseFactory; public abstract class AbstractMongoConfig { public MongoDatabaseFactory mongoDatabaseFactory(String uri, String database) throws Exception { ConnectionString connectionString = new ConnectionString(uri); return new SimpleMongoClientDatabaseFactory(MongoClients.create(connectionString), database); } public abstract MongoTemplate getMongoTemplate() throws Exception; }
MongoDB properties
spring.data.mongodb.uri=mongodb://FID1:password1@mongoHost1:port1/?replicaSet=MGORPST_3004&ssl=true&sslInvalidHostNameAllowed=true
spring.data.mongodb.uri2=mongodb://FID2:password2@mongoHost2:port2/marketdata?ssl=true&authSource=admin
spring.data.mongodb.database1=test1
spring.data.mongodb.database2=test2
management.security.enabled=false //不启用登录actuator页面验证
management.endpoints.web.exposure.include=* //放开所有endpoint(不包含shutdown),如果management.endpoints.web.exposure.include=beans,mappings之放开actuator/beans和actuator/mappings
management.endpoint.health.showDetails=always
调用 http://localhost:8081/actuator/health结果

{ "status": "UP", "components": { "custom": { "status": "UP", "details": { "PrimaryMongoAddress": "mongodb://FID:password@host:port/?replicaSet=MGORPST_3004&ssl=true&sslInvalidHostNameAllowed=true", "PrimaryMongoStats": "{\"objects\":178082860,\"scaleFactor\":1,\"dataSize\":534560218484,\"indexSize\":13836587008,\"operationTime\":{\"$timestamp\":{\"t\":1671342061,\"i\":5}},\"totalSize\":378842005504,\"$clusterTime\":{\"clusterTime\":{\"$timestamp\":{\"t\":1671342061,\"i\":5}},\"signature\":{\"keyId\":7129165744469704705,\"hash\":{\"$binary\":{\"base64\":\"DlyyV0xsnvCfsDtJ0j\\/0zEdH9XI=\",\"subType\":\"00\"}}}},\"indexes\":388,\"collections\":102,\"storageSize\":365005418496,\"fsUsedSize\":716543451136,\"avgObjSize\":3001.7499633822144,\"ok\":1,\"db\":\"test\",\"views\":0,\"fsTotalSize\":951036878848}", "SecondMongoAddress": "mongodb://FID:password@host:port/marketdata?ssl=true&authSource=admin", "SecondMongoStats": "{\"objects\":131977,\"scaleFactor\":1,\"dataSize\":312007082,\"indexSize\":13578240,\"operationTime\":{\"$timestamp\":{\"t\":1671342063,\"i\":1}},\"totalSize\":81588224,\"$clusterTime\":{\"clusterTime\":{\"$timestamp\":{\"t\":1671342063,\"i\":1}},\"signature\":{\"keyId\":7151448915269648390,\"hash\":{\"$binary\":{\"base64\":\"uSUyLlJpD0NMq5fCMjcH5l1uKAk=\",\"subType\":\"00\"}}}},\"indexes\":136,\"collections\":40,\"storageSize\":68009984,\"fsUsedSize\":15365464064,\"avgObjSize\":2364.101942005046,\"ok\":1,\"db\":\"test\",\"views\":2,\"fsTotalSize\":48449372160}" } }, "diskSpace": { "status": "UP", "details": { "total": 343595282432, "free": 98476376064, "threshold": 10485760, "exists": true } }, "mongo": { "status": "UP", "components": { "getReactiveMongoTemplate": { "status": "UP", "details": { "version": "4.4.11" } }, "reactiveMongoTemplate": { "status": "UP", "details": { "version": "4.4.11" } } } }, "ping": { "status": "UP" } } }
2. Actuator详述
访问http://localhost:8099/actuator
,查看暴露出来的端点:
注意如果在上例ActuatorWebSecurityConfiguration.java中设置为requestMatchers(EndpointRequest.toAnyEndpoint())则会暴露actuator下所有endpoint,如果设置为requestMatchers(EndpointRequest.to(HealthEndpoint.class, InfoEndpoint.class))则只会暴露health和info这两个端点。比如health提供应用健康情况基础信息;metrics提供一些有用应用程序指标(jvm内存使用、cpu使用等),比如访问 http://localhost:8081/actuator/metrics/jvm.memory.max得到
{"name":"jvm.memory.max","description":"The maximum amount of memory in bytes that can be used for memory management","baseUnit":"bytes","measurements":[{"statistic":"VALUE","value":6.671564799E9}],"availableTags":[{"tag":"area","values":["heap","nonheap"]},{"tag":"id","values":["Compressed Class Space","PS Old Gen","PS Survivor Space","Metaspace","PS Eden Space","Code Cache"]}]}
另外每个端点都可以通过配置来单独启停

{ "_links": { "self": { "href": "http://localhost:8081/actuator", "templated": false }, "beans": { "href": "http://localhost:8081/actuator/beans", "templated": false }, "caches-cache": { "href": "http://localhost:8081/actuator/caches/{cache}", "templated": true }, "caches": { "href": "http://localhost:8081/actuator/caches", "templated": false }, "health": { "href": "http://localhost:8081/actuator/health", "templated": false }, "health-path": { "href": "http://localhost:8081/actuator/health/{*path}", "templated": true }, "info": { "href": "http://localhost:8081/actuator/info", "templated": false }, "conditions": { "href": "http://localhost:8081/actuator/conditions", "templated": false }, "configprops": { "href": "http://localhost:8081/actuator/configprops", "templated": false }, "configprops-prefix": { "href": "http://localhost:8081/actuator/configprops/{prefix}", "templated": true }, "env": { "href": "http://localhost:8081/actuator/env", "templated": false }, "env-toMatch": { "href": "http://localhost:8081/actuator/env/{toMatch}", "templated": true }, "loggers": { "href": "http://localhost:8081/actuator/loggers", "templated": false }, "loggers-name": { "href": "http://localhost:8081/actuator/loggers/{name}", "templated": true }, "heapdump": { "href": "http://localhost:8081/actuator/heapdump", "templated": false }, "threaddump": { "href": "http://localhost:8081/actuator/threaddump", "templated": false }, "metrics-requiredMetricName": { "href": "http://localhost:8081/actuator/metrics/{requiredMetricName}", "templated": true }, "metrics": { "href": "http://localhost:8081/actuator/metrics", "templated": false }, "scheduledtasks": { "href": "http://localhost:8081/actuator/scheduledtasks", "templated": false }, "mappings": { "href": "http://localhost:8081/actuator/mappings", "templated": false } } }
HTTP方法 | Endpoint | 描述 |
---|---|---|
GET | /actuator | 查看有哪些 Actuator endpoint 是開放的 |
GET | /actuator/auditevent | 查看 audit 的事件,例如認證進入、訂單失敗,需要搭配 Spring security 使用,sample code |
GET | /actuator/beans | 查看運行當下裡面全部的 bean,以及他們的關係 |
GET | /actuator/conditions | 查看自動配置的結果,記錄哪些自動配置條件通過了,哪些沒通過 |
GET | /actuator/configprops | 查看注入帶有 @ConfigurationProperties 類的 properties 值為何(包含默認值) |
GET | /actuator/env (常用) | 查看全部環境屬性,可以看到 SpringBoot 載入了哪些 properties,以及這些 properties 的值(但是會自動* 掉帶有 key、password、secret 等關鍵字的 properties 的值,保護安全資訊) |
GET | /actuator/flyway | 查看 flyway DB 的 migration 資訊 |
GET | /actuator/health (常用) | 查看當前 SpringBoot 運行的健康指標,值由 HealthIndicator 的實現類提供(所以可以自定義一些健康指標資訊,加到這裡面) |
GET | /actuator/heapdump | 取得 JVM 當下的 heap dump,會下載一個檔案 |
GET | /actuator/info | 查看 properties 中 info 開頭的屬性的值,沒啥用 |
GET | /actuator/mappings | 查看全部的 endpoint(包含 Actuator 的),以及他們和 Controller 的關係 |
GET | /actuator/metrics(常用) | 查看有哪些指標可以看(ex: jvm.memory.max、system.cpu.usage),要再使用/actuator/metrics/{metric.name} 分別查看各指標的詳細資訊 |
GET | /actuator/scheduledtasks | 查看定時任務的資訊 |
POST | /actuator/shutdown | 唯一一個需要 POST 請求的 endpoint,關閉這個 SpringBoot 程式 |
放开所有endpoint
management.endpoints.web.exposure.include=*
只放开beans和mappings
management.endpoints.web.exposure.include=beans,mappings
exclude可以用来关闭某些endpoints,比如下边语句关闭beans这个endpoint
management.endpoints.web.exposure.exclude=beans
2.1 health
2.2 metrics端口含义
端点用来返回当前应用的各类重要度量指标,比如:内存信息、线程信息、垃圾回收信息、tomcat、数据库连接池等。
序号 | 参数 | 参数说明 | 是否监控 | 监控手段 | 重要度 |
---|
JVM | |||||
1 | jvm.memory.max | JVM 最大内存 | |||
2 | jvm.memory.committed | JVM 可用内存 | 是 | 展示并监控堆内存和 Metaspace | 重要 |
3 | jvm.memory.used | JVM 已用内存 | 是 | 展示并监控堆内存和 Metaspace | 重要 |
4 | jvm.buffer.memory.used | JVM 缓冲区已用内存 | |||
5 | jvm.buffer.count | 当前缓冲区数 | |||
6 | jvm.threads.daemon | JVM 守护线程数 | 是 | 显示在监控页面 | |
7 | jvm.threads.live | JVM 当前活跃线程数 | 是 | 显示在监控页面;监控达到阈值时报警 | 重要 |
8 | jvm.threads.peak | JVM 峰值线程数 | 是 | 显示在监控页面 | |
9 | jvm.classes.loaded | 加载 classes 数 | |||
10 | jvm.classes.unloaded | 未加载的 classes 数 | |||
11 | jvm.gc.memory.allocated | GC 时,年轻代分配的内存空间 | |||
12 | jvm.gc.memory.promoted | GC 时,老年代分配的内存空间 | |||
13 | jvm.gc.max.data.size | GC 时,老年代的最大内存空间 | |||
14 | jvm.gc.live.data.size | FullGC 时,老年代的内存空间 | |||
15 | jvm.gc.pause | GC 耗时 | 是 | 显示在监控页面 | |
TOMCAT | |||||
16 | tomcat.sessions.created | tomcat 已创建 session 数 | |||
17 | tomcat.sessions.expired | tomcat 已过期 session 数 | |||
18 | tomcat.sessions.active.current | tomcat 活跃 session 数 | |||
19 | tomcat.sessions.active.max | tomcat 最多活跃 session 数 | 是 | 显示在监控页面,超过阈值可报警或者进行动态扩容 | 重要 |
20 | tomcat.sessions.alive.max.second | tomcat 最多活跃 session 数持续时间 | |||
21 | tomcat.sessions.rejected | 超过 session 最大配置后,拒绝的 session 个数 | 是 | 显示在监控页面,方便分析问题 | |
22 | tomcat.global.error | 错误总数 | 是 | 显示在监控页面,方便分析问题 | |
23 | tomcat.global.sent | 发送的字节数 | |||
24 | tomcat.global.request.max | request 最长时间 | |||
25 | tomcat.global.request | 全局 request 次数和时间 | |||
26 | tomcat.global.received | 全局 received 次数和时间 | |||
27 | tomcat.servlet.request | servlet 的请求次数和时间 | |||
28 | tomcat.servlet.error | servlet 发生错误总数 | |||
29 | tomcat.servlet.request.max | servlet 请求最长时间 | |||
30 | tomcat.threads.busy | tomcat 繁忙线程 | 是 | 显示在监控页面,据此检查是否有线程夯住 | |
31 | tomcat.threads.current | tomcat 当前线程数(包括守护线程) | 是 | 显示在监控页面 | 重要 |
32 | tomcat.threads.config.max | tomcat 配置的线程最大数 | 是 | 显示在监控页面 | 重要 |
33 | tomcat.cache.access | tomcat 读取缓存次数 | |||
34 | tomcat.cache.hit | tomcat 缓存命中次数 | |||
CPU | |||||
35 | system.cpu.count | CPU 数量 | |||
36 | system.load.average.1m | load average | 是 | 超过阈值报警 | 重要 |
37 | system.cpu.usage | 系统 CPU 使用率 | |||
38 | process.cpu.usage | 当前进程 CPU 使用率 | 是 | 超过阈值报警 | |
39 | http.server.requests | http 请求调用情况 | 是 | 显示 10 个请求量最大,耗时最长的 URL;统计非 200 的请求量 | 重要 |
40 | process.uptime | 应用已运行时间 | 是 | 显示在监控页面 | |
41 | process.files.max | 允许最大句柄数 | 是 | 配合当前打开句柄数使用 | |
42 | process.start.time | 应用启动时间点 | 是 | 显示在监控页面 | |
43 | process.files.open | 当前打开句柄数 | 是 | 监控文件句柄使用率,超过阈值后报警 | 重要 |
2.3. threaddump
方便日常定位问题时候查看线程情况。主要展示线程名、线程id、线程状态、是否等待锁资源、线程堆栈等信息
2.4. headdump
访问http://localhost:8081/actuator/headdump会自动生成一个jvm堆文件headdump,使用visualvm打开此文件可以查看内存快照
2.5. shutdown
这个端口默认不开启,如果想开启需要
management.endpoint.shutdown.enabled=true
二. SpringBoot Admin
springBoot Admin提供了springboot actuator监控的可视化界面
SpringBoot Admin是由两组applicaiton构成:applicaiton-server(提供监控可视化界面),application-client(被监控的服务注册到applicaiton-server上)
1. applicaiton server
1.1 依赖
依赖如下,注意点:①由于applicaiton servert提供监控可视化界面,是一个web应用,所以需要导入spring-boot-web
②spring-boot-admin-starter-server版本需要和springboot版本一致
implementation group: 'de.codecentric', name: 'spring-boot-admin-starter-server', version: springBootVersion
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-web', version: springBootVersion
1.2 Springboot 启动类添加注解@EnableAdminServer
@SpringBootApplication
@EnableAdminServer
public class Application { //NOSONAR
public static void main(String[] args) throws JobParametersInvalidException, JobExecutionAlreadyRunningException, JobRestartException, JobInstanceAlreadyCompleteException {
SpringApplication.run(Application.class, args); //NOSONAR
}
}
1.3 appliciton.properties配置文件
server.port=8082 这个端口号需要在applicaiton-client中指定
1.4 启动application-server服务
http://localhost:8082/applications
由于还没有服务注册上来此时应用数时0
2. application client
2.1 依赖
implementation group: 'de.codecentric', name: 'spring-boot-admin-starter-client', version: springBootVersion
2.2 配置文件
management.security.enabled=true
management.endpoints.web.exposure.include=*
management.endpoint.health.showDetails=always
server.port=8081
spring.applicaiton.name=admin
spring.boot.admin.client.url=http://localhost:8082 --指定applicaition-server地址:port
spring.boot.admin.client.instance.preferIp=true
appliction-client起起来后,再访问http://localhost:8082/applications,可见已经有服务注册上来了。
点进去可以将actuator监控内容通过可视化界面形式展示出来
另外springboot admin可以配置账号密码登录、发送邮件等功能,这里不再 赘述
参考:
https://www.cnblogs.com/caoweixiong/p/15325382.html
https://blog.csdn.net/weixin_46666822/article/details/124483078
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人