SpringBoot应用监控
利用SpringBoot作为微服务单元的实例化技术选型时,不可避免的要面对一个问题,就是如何实时监控应用的运行状况数据,比如:健康度,运行指标,日志信息,线程状况等等
SpringBoot提供三种应用监控的方式,分别为HTTP、JMX、SSH协议
HTTP方式
监控和管理端点
SpringBoot项目版本为2.1.1.RELEASE
pom引入
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
配置文件application.properties
#actuator端口
management.server.port=9001
#修改访问路径 2.0之前默认是/ 2.0默认是 /actuator 可以通过这个属性值修改
management.endpoints.web.base-path=/monitor
#开放所有页面节点 默认只开启了health、info两个节点
management.endpoints.web.exposure.include=*
#显示健康具体信息 默认不会显示详细信息
management.endpoint.health.show-details=always
#默认关闭,可以通过shutdown关闭服务
management.endpoint.shutdown.enabled=true
#使用security保护端点,因为监控的会有很多敏感的信息,设置之后会需要登陆之后才能进入应用监控
spring.security.user.name=admin
spring.security.user.password=admin
spring.security.user.roles=SUPERUSER
查看测试各个端点
{
"_links":{
"self":{
"href":"http://localhost:9001/monitor",
"templated":false
},
"auditevents":{
"href":"http://localhost:9001/monitor/auditevents",
"templated":false
},
"beans":{
"href":"http://localhost:9001/monitor/beans",
"templated":false
},
"caches-cache":{
"href":"http://localhost:9001/monitor/caches/{cache}",
"templated":true
},
"caches":{
"href":"http://localhost:9001/monitor/caches",
"templated":false
},
"health":{
"href":"http://localhost:9001/monitor/health",
"templated":false
},
"health-component":{
"href":"http://localhost:9001/monitor/health/{component}",
"templated":true
},
"health-component-instance":{
"href":"http://localhost:9001/monitor/health/{component}/{instance}",
"templated":true
},
"conditions":{
"href":"http://localhost:9001/monitor/conditions",
"templated":false
},
"shutdown":{
"href":"http://localhost:9001/monitor/shutdown",
"templated":false
},
"configprops":{
"href":"http://localhost:9001/monitor/configprops",
"templated":false
},
"env":{
"href":"http://localhost:9001/monitor/env",
"templated":false
},
"env-toMatch":{
"href":"http://localhost:9001/monitor/env/{toMatch}",
"templated":true
},
"info":{
"href":"http://localhost:9001/monitor/info",
"templated":false
},
"loggers":{
"href":"http://localhost:9001/monitor/loggers",
"templated":false
},
"loggers-name":{
"href":"http://localhost:9001/monitor/loggers/{name}",
"templated":true
},
"heapdump":{
"href":"http://localhost:9001/monitor/heapdump",
"templated":false
},
"threaddump":{
"href":"http://localhost:9001/monitor/threaddump",
"templated":false
},
"metrics":{
"href":"http://localhost:9001/monitor/metrics",
"templated":false
},
"metrics-requiredMetricName":{
"href":"http://localhost:9001/monitor/metrics/{requiredMetricName}",
"templated":true
},
"scheduledtasks":{
"href":"http://localhost:9001/monitor/scheduledtasks",
"templated":false
},
"httptrace":{
"href":"http://localhost:9001/monitor/httptrace",
"templated":false
},
"mappings":{
"href":"http://localhost:9001/monitor/mappings",
"templated":false
}
}
}
以上就会本服务可以监控的所有端点以及其访问地址
其中端点shutdown不支持get请求,必须是post请求,可以用于关闭服务,可以通过postman模拟请求
返回结果如下,则表示服务已经关闭
{
"message":"Shutting down, bye..."
}
开启制定的端点
#如开启info
management.endpoint.info.enabled=true
定制端点访问路径
#如上面提到的实例,每个端点访问路径都要拼接/monitor
management.endpoints.web.base-path=/monitor
/health端点
健康端点用于监控运行的服务实例状态,当服务实例下线或者因为其他的原因变得异常时,将会及时通知运维人员。
可以通过配置是否显示健康的具体信息
#显示健康具体信息 默认不会显示详细信息
management.endpoint.health.show-details=always
若不配置,访问 http://192.168.0.115:9001/monitor/health,结果如下:
{"status":"UP"}
若配置了,结果如下:
{"status":"UP","details":{"myHealth":{"status":"UP","details":{"code":0,"version":"v2.0"}},"diskSpace":{"status":"UP","details":{"total":213754310656,"free":209042399232,"threshold":10485760}}}}
/metrics端点
/metrics端点报告各种应用程序度量信息,比如内存用量、HTTP请求计数、线程信息、垃圾回收信息等。
springboot2.x版本访问 http://localhost:9001/monitor/metrics ,结果如下:
{
"names":[
"jvm.memory.max",
"jvm.threads.states",
"jvm.gc.memory.promoted",
"tomcat.cache.hit",
"tomcat.servlet.error",
"tomcat.cache.access",
"jvm.memory.used",
"jvm.gc.max.data.size",
"jvm.gc.pause",
"jvm.memory.committed",
"system.cpu.count",
"logback.events",
"tomcat.global.sent",
"jvm.buffer.memory.used",
"tomcat.sessions.created",
"jvm.threads.daemon",
"system.cpu.usage",
"jvm.gc.memory.allocated",
"tomcat.global.request.max",
"tomcat.global.request",
"tomcat.sessions.expired",
"jvm.threads.live",
"jvm.threads.peak",
"tomcat.global.received",
"process.uptime",
"tomcat.sessions.rejected",
"process.cpu.usage",
"tomcat.threads.config.max",
"jvm.classes.loaded",
"jvm.classes.unloaded",
"tomcat.global.error",
"tomcat.sessions.active.current",
"tomcat.sessions.alive.max",
"jvm.gc.live.data.size",
"tomcat.servlet.request.max",
"tomcat.threads.current",
"tomcat.servlet.request",
"jvm.buffer.count",
"jvm.buffer.total.capacity",
"tomcat.sessions.active.max",
"tomcat.threads.busy",
"process.start.time"
]
}
具体查看每个组件的详细信息可以通过访问 http://localhost:9001/monitor/metrics/{requiredMetricName}
比如查看jvm最大内存访问 http://localhost:9001/monitor/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":3430940671
}
],
"availableTags":[
{
"tag":"area",
"values":[
"heap",
"nonheap"
]
},
{
"tag":"id",
"values":[
"Compressed Class Space",
"PS Survivor Space",
"PS Old Gen",
"Metaspace",
"PS Eden Space",
"Code Cache"
]
}
]
}
/info端点
/info端点用来返回一些应用自定义的信息。默认情况下,该端点只会返回一个空的json内容。我们可以在application.properties配置文件中通过info前缀来设置一些属性
例如:在application.properties文件中配置如下自定义信息信息
info.appname=mydemo
info.appversion=v1.0
访问地址 http://localhost:9001/monitor/info
{"appname":"mydemo","appversion":"v1.0"}
/mapping端口
/mapping端口描述全部的URI路径,以及它们和控制器(包含Actuator端点)的映射关系罗列出应用程序发布的全部接口,bean属性标识了该映射关系的请求处理器;method属性标识了该映射关系的具体处理类和处理函数。
访问地址 http://localhost:9001/monitor/mappings,结果如下:
{
"contexts":{
"application":{
"mappings":{
"dispatcherServlets":{
"dispatcherServlet":[
{
"handler":"ResourceHttpRequestHandler [class path resource [META-INF/resources/], class path resource [resources/], class path resource [static/], class path resource [public/], ServletContext resource [/], class path resource []]",
"predicate":"/**/favicon.ico",
"details":null
},
{
<!--自定义的接口全路径-->
"handler":"public java.lang.String com.springboot.actuator.api.IndexController.getIndex()",
<!--接口定义路径-->
"predicate":"{ /api/index}",
"details":{
"handlerMethod":{
<!--接口所在的类-->
"className":"com.springboot.actuator.api.IndexController",
<!---方法名称-->
"name":"getIndex",
<!--返回值类型-->
"descriptor":"()Ljava/lang/String;"
},
"requestMappingConditions":{
"consumes":[
],
"headers":[
],
"methods":[
],
"params":[
],
"patterns":[
"/api/index"
],
"produces":[
]
}
}
},
{
"handler":"public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.error(javax.servlet.http.HttpServletRequest)",
"predicate":"{ /error}",
"details":{
"handlerMethod":{
"className":"org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController",
"name":"error",
"descriptor":"(Ljavax/servlet/http/HttpServletRequest;)Lorg/springframework/http/ResponseEntity;"
},
"requestMappingConditions":{
"consumes":[
],
"headers":[
],
"methods":[
],
"params":[
],
"patterns":[
"/error"
],
"produces":[
]
}
}
},
{
"handler":"public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)",
"predicate":"{ /error, produces [text/html]}",
"details":{
"handlerMethod":{
"className":"org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController",
"name":"errorHtml",
"descriptor":"(Ljavax/servlet/http/HttpServletRequest;Ljavax/servlet/http/HttpServletResponse;)Lorg/springframework/web/servlet/ModelAndView;"
},
"requestMappingConditions":{
"consumes":[
],
"headers":[
],
"methods":[
],
"params":[
],
"patterns":[
"/error"
],
"produces":[
{
"mediaType":"text/html",
"negated":false
}
]
}
}
},
{
"handler":"ResourceHttpRequestHandler ["classpath:/META-INF/resources/webjars/"]",
"predicate":"/webjars/**",
"details":null
},
{
"handler":"ResourceHttpRequestHandler ["classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/", "/"]",
"predicate":"/**",
"details":null
}
]
},
"servletFilters":[
{
"servletNameMappings":[
],
"urlPatternMappings":[
"/*"
],
"name":"webMvcMetricsFilter",
"className":"org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter"
},
{
"servletNameMappings":[
],
"urlPatternMappings":[
"/*"
],
"name":"requestContextFilter",
"className":"org.springframework.boot.web.servlet.filter.OrderedRequestContextFilter"
},
{
"servletNameMappings":[
],
"urlPatternMappings":[
"/*"
],
"name":"Tomcat WebSocket (JSR356) Filter",
"className":"org.apache.tomcat.websocket.server.WsFilter"
},
{
"servletNameMappings":[
],
"urlPatternMappings":[
"/*"
],
"name":"hiddenHttpMethodFilter",
"className":"org.springframework.boot.web.servlet.filter.OrderedHiddenHttpMethodFilter"
},
{
"servletNameMappings":[
],
"urlPatternMappings":[
"/*"
],
"name":"characterEncodingFilter",
"className":"org.springframework.boot.web.servlet.filter.OrderedCharacterEncodingFilter"
},
{
"servletNameMappings":[
],
"urlPatternMappings":[
"/*"
],
"name":"httpTraceFilter",
"className":"org.springframework.boot.actuate.web.trace.servlet.HttpTraceFilter"
},
{
"servletNameMappings":[
],
"urlPatternMappings":[
"/*"
],
"name":"springSecurityFilterChain",
"className":"org.springframework.boot.web.servlet.DelegatingFilterProxyRegistrationBean$1"
},
{
"servletNameMappings":[
],
"urlPatternMappings":[
"/*"
],
"name":"formContentFilter",
"className":"org.springframework.boot.web.servlet.filter.OrderedFormContentFilter"
}
],
"servlets":[
{
"mappings":[
],
"name":"default",
"className":"org.apache.catalina.servlets.DefaultServlet"
},
{
"mappings":[
"/"
],
"name":"dispatcherServlet",
"className":"org.springframework.web.servlet.DispatcherServlet"
}
]
},
"parentId":null
}
}
}
/env端点
/env端点它用来获取应用所有可用的环境属性报告。包括:环境变量、JVM属性、应用的配置配置、命令行中的参数。从下面该端点返回的示例片段中,我们可以看到它不仅返回了应用的配置属性,还返回了系统属性、环境变量等丰富的配置信息,其中也包括了应用还没有使用的配置。所以它可以帮助我们方便地看到当前应用可以加载的配置信息,并配合@ConfigurationProperties注解将它们引入到我们的应用程序中来进行使用。另外,为了配置属性的安全,对于一些类似密码等敏感信息,该端点都会进行隐私保护,但是我们需要让属性名中包含:password、secret、key这些关键词,这样该端点在返回它们的时候会使用*来替代实际的属性值。
{
"activeProfiles":[
],
"propertySources":[
{
"name":"server.ports",
"properties":{
"local.management.port":{
"value":9001
},
"local.server.port":{
"value":8080
}
}
},
{
"name":"servletContextInitParams",
"properties":{
}
},
{
"name":"systemProperties",
"properties":{
"com.sun.management.jmxremote.authenticate":{
"value":"false"
},
"java.runtime.name":{
"value":"Java(TM) SE Runtime Environment"
},
"spring.output.ansi.enabled":{
"value":"always"
},
"sun.boot.library.path":{
"value":"C:\JAVA\JDK\jre\bin"
},
"java.vm.version":{
"value":"25.112-b15"
},
"java.vm.vendor":{
"value":"Oracle Corporation"
},
"java.vendor.url":{
"value":"http://java.oracle.com/"
},
.....
"user.dir":{
"value":"G:\project\study-demo\springboot-actuator"
},
"java.runtime.version":{
"value":"1.8.0_112-b15"
},
.....
"user.name":{
"value":"旭东"
},
.....
{
"name":"applicationConfig: [classpath:/application.properties]",
"properties":{
"server.port":{
"value":"8080",
"origin":"class path resource [application.properties]:2:13"
},
"server.servlet.context-path":{
"value":"/demo",
"origin":"class path resource [application.properties]:3:29"
},
"management.server.port":{
"value":"9001",
"origin":"class path resource [application.properties]:6:24"
},
"management.endpoints.web.base-path":{
"value":"/monitor",
"origin":"class path resource [application.properties]:8:36"
},
"management.endpoints.web.exposure.include":{
"value":"*",
"origin":"class path resource [application.properties]:10:43"
},
"management.endpoint.health.show-details":{
"value":"always",
"origin":"class path resource [application.properties]:12:41"
},
"management.endpoint.shutdown.enabled":{
"value":"true",
"origin":"class path resource [application.properties]:14:38"
},
"spring.security.user.name":{
"value":"admin",
"origin":"class path resource [application.properties]:17:27"
},
"spring.security.user.password":{
"value":"******",
"origin":"class path resource [application.properties]:18:31"
},
"spring.security.user.roles":{
"value":"SUPERUSER",
"origin":"class path resource [application.properties]:19:28"
},
"info.appname":{
"value":"mydemo",
"origin":"class path resource [application.properties]:21:14"
},
"info.appversion":{
"value":"v1.0",
"origin":"class path resource [application.properties]:22:17"
}
}
}
]
}
自定义端点
@Data
@ToString
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode
public class MyEndpoint {
private Boolean enable;
private String name;
private String age;
}
@Configuration
@ComponentScan("com.springboot.actuator")
public class MyEndpointConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnEnabledEndpoint
public MyEndpointConfig getMyEndpoint(){
return new MyEndpointConfig();
}
}
@Component
@Endpoint(id = "myendpoint",enableByDefault = false)
public class MyEndpointConfig {
private Map<String,MyEndpoint> myEndpointMap = new ConcurrentHashMap<>();
{
myEndpointMap.put("cxd",new MyEndpoint(true,"哈哈","28"));
myEndpointMap.put("hn",new MyEndpoint(true,"呵呵","27"));
}
@ReadOperation
public Map<String,MyEndpoint> getMyEndpoints(){
return myEndpointMap;
}
@ReadOperation
public MyEndpoint getMyEndpoint(@Selector String key){
return myEndpointMap.get(key);
}
@WriteOperation
public void configureMyEndpoint(String name,MyEndpoint myEndpoint){
myEndpointMap.put(name, myEndpoint);
}
@DeleteOperation
public void deleteMyEndpoint(String name){
myEndpointMap.remove(name);
}
}
访问:http://localhost:9001/monitor/myendpoint
{"hn":{"enable":true,"name":"呵呵","age":"27"},"cxd":{"enable":true,"name":"哈哈","age":"28"}}
自定义Health
@Component("myHealth")
public class MyHealthIndicator extends AbstractHealthIndicator {
@Override
protected void doHealthCheck(Health.Builder builder) throws Exception {
int code = 0;
if (code != 0){
builder.down().withDetail("code",code).withDetail("version","v1.0").build();
}else {
builder.up().withDetail("code",code).withDetail("version","v2.0").build();
}
}
}
访问:http://localhost:9001/monitor/health
{"status":"UP","details":{"myHealth":{"status":"UP","details":{"code":0,"version":"v2.0"}},"diskSpace":{"status":"UP","details":{"total":213754310656,"free":209042505728,"threshold":10485760}}}}