学习一项新技术最大的困难是什么? 是资料。让人高兴的是找到了一本系统学习Spring Cloud的教程,《Spring Cloud微服务实战》, 接下来的学习目标将以此书顺序演进。
虽然Springboot 2.0刚刚发布,鉴于当下资料都是基于1.x的,对于初学者,站在前人的肩膀上会少踩坑。因此,接下来都将采用 1.5.10.RELEASE
。
上一节, SpringCloud入门1-服务注册与发现(Eureka)
中已经尝试使用了 spring-boot-starter-actuator
模块中的健康检查端点,接下来将系统的学习该模块的功能。
Spring Boot includes a number of additional features to help you monitor and manage your application when it’s pushed to production. You can choose to manage and monitor your application using HTTP endpoints, with JMX or even by remote shell (SSH or Telnet). Auditing, health and metrics gathering can be automatically applied to your application.
测试代码: https://github.com/Ryan-Miao/actuator-demo
如同上一节中所引入的方式一样。创建一个springboot项目,并添加依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
actuator 美 [‘æktʃʊˌeɪtə]
接下来,启动项目。
. ____ _ __ _ _ /// / ___'_ __ _ _(_)_ __ __ _ / / / / ( ( )/___ | '_ | '_| | '_ // _` | / / / / /// ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_/__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v1.5.10.RELEASE) 2018-03-04 12:15:52.611 INFO 18468 --- [ main] com.test.actuator.ActuatorApplication : Starting ActuatorApplication on DESKTOP-4MOUU2Q with PID 18468 (D:/workspace/learn/springcloud/actuator-demo/target/classes started by Ryan in D:/workspace/learn/springcloud/actuator-demo) 2018-03-04 12:15:52.615 INFO 18468 --- [ main] com.test.actuator.ActuatorApplication : No active profile set, falling back to default profiles: default 2018-03-04 12:15:52.709 INFO 18468 --- [ main] ationConfigEmbeddedWebApplicationContext : Refreshing org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@1efee8e7: startup date [Sun Mar 04 12:15:52 CST 2018]; root of context hierarchy 2018-03-04 12:15:55.125 INFO 18468 --- [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat initialized with port(s): 8080 (http) 2018-03-04 12:15:55.140 INFO 18468 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2018-03-04 12:15:55.140 INFO 18468 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet Engine: Apache Tomcat/8.5.27 2018-03-04 12:15:55.265 INFO 18468 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 2018-03-04 12:15:55.265 INFO 18468 --- [ost-startStop-1] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 2588 ms 2018-03-04 12:15:55.512 INFO 18468 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean : Mapping servlet: 'dispatcherServlet' to [/] 2018-03-04 12:15:55.512 INFO 18468 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'metricsFilter' to: [/*] 2018-03-04 12:15:55.512 INFO 18468 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'characterEncodingFilter' to: [/*] 2018-03-04 12:15:55.512 INFO 18468 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'hiddenHttpMethodFilter' to: [/*] 2018-03-04 12:15:55.512 INFO 18468 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'httpPutFormContentFilter' to: [/*] 2018-03-04 12:15:55.512 INFO 18468 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'requestContextFilter' to: [/*] 2018-03-04 12:15:55.512 INFO 18468 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'webRequestLoggingFilter' to: [/*] 2018-03-04 12:15:55.512 INFO 18468 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'applicationContextIdFilter' to: [/*] 2018-03-04 12:15:55.926 INFO 18468 --- [ main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@1efee8e7: startup date [Sun Mar 04 12:15:52 CST 2018]; root of context hierarchy 2018-03-04 12:15:56.004 INFO 18468 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.BasicErrorController.error(javax.servlet.http.HttpServletRequest) 2018-03-04 12:15:56.004 INFO 18468 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse) 2018-03-04 12:15:56.051 INFO 18468 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler] 2018-03-04 12:15:56.051 INFO 18468 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler] 2018-03-04 12:15:56.097 INFO 18468 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler] 2018-03-04 12:15:56.387 INFO 18468 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/env/{name:.*}],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EnvironmentMvcEndpoint.value(java.lang.String) 2018-03-04 12:15:56.387 INFO 18468 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/env || /env.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke() 2018-03-04 12:15:56.387 INFO 18468 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/info || /info.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke() 2018-03-04 12:15:56.387 INFO 18468 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/health || /health.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.HealthMvcEndpoint.invoke(javax.servlet.http.HttpServletRequest,java.security.Principal) 2018-03-04 12:15:56.387 INFO 18468 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/mappings || /mappings.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke() 2018-03-04 12:15:56.387 INFO 18468 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/trace || /trace.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke() 2018-03-04 12:15:56.387 INFO 18468 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/dump || /dump.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke() 2018-03-04 12:15:56.403 INFO 18468 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/loggers/{name:.*}],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.LoggersMvcEndpoint.get(java.lang.String) 2018-03-04 12:15:56.403 INFO 18468 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/loggers/{name:.*}],methods=[POST],consumes=[application/vnd.spring-boot.actuator.v1+json || application/json],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.LoggersMvcEndpoint.set(java.lang.String,java.util.Map<java.lang.String, java.lang.String>) 2018-03-04 12:15:56.403 INFO 18468 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/loggers || /loggers.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke() 2018-03-04 12:15:56.403 INFO 18468 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/autoconfig || /autoconfig.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke() 2018-03-04 12:15:56.403 INFO 18468 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/metrics/{name:.*}],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.MetricsMvcEndpoint.value(java.lang.String) 2018-03-04 12:15:56.403 INFO 18468 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/metrics || /metrics.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke() 2018-03-04 12:15:56.403 INFO 18468 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/auditevents || /auditevents.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public org.springframework.http.ResponseEntity<?> org.springframework.boot.actuate.endpoint.mvc.AuditEventsMvcEndpoint.findByPrincipalAndAfterAndType(java.lang.String,java.util.Date,java.lang.String) 2018-03-04 12:15:56.403 INFO 18468 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/configprops || /configprops.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke() 2018-03-04 12:15:56.403 INFO 18468 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/beans || /beans.json],methods=[GET],produces=[application/vnd.spring-boot.actuator.v1+json || application/json]}" onto public java.lang.Object org.springframework.boot.actuate.endpoint.mvc.EndpointMvcAdapter.invoke() 2018-03-04 12:15:56.403 INFO 18468 --- [ main] o.s.b.a.e.mvc.EndpointHandlerMapping : Mapped "{[/heapdump || /heapdump.json],methods=[GET],produces=[application/octet-stream]}" onto public void org.springframework.boot.actuate.endpoint.mvc.HeapdumpMvcEndpoint.invoke(boolean,javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse) throws java.io.IOException,javax.servlet.ServletException 2018-03-04 12:15:56.512 INFO 18468 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup 2018-03-04 12:15:56.512 INFO 18468 --- [ main] o.s.c.support.DefaultLifecycleProcessor : Starting beans in phase 0 2018-03-04 12:15:56.747 INFO 18468 --- [ main] s.b.c.e.t.TomcatEmbeddedServletContainer : Tomcat started on port(s): 8080 (http) 2018-03-04 12:15:56.747 INFO 18468 --- [ main] com.test.actuator.ActuatorApplication : Started ActuatorApplication in 4.615 seconds (JVM running for 5.11)
可以看到url mapping多了几个。接下来,分别看看这几个接口的内容。
访问这几个接口的时候发现啥也没有,然后看到控制台提示,这些敏感信息需要加密。所以,必须引入security模块。
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency>
设置用户名密码:
security.user.name=admin security.user.password=123456
然后,重启,访问 http://localhost:8080/health 得到
{ "status": "UP", "diskSpace": { "status": "UP", "total": 126702579712, "free": 71547412480, "threshold": 10485760 } }
Actuator endpoints可以让我们监控和与我们的应用(application)交互。Spring Boot actuator包含了大量的内置endpoints, 当然,我们也可以添加我们自己的。
如何暴露端口取决与你采用的技术。大多数应用采用HTTP监控,这时,endpoint的ID就映射为一个接口URL。比如,默认 health
映射为 /health
. (2.0貌似不是这样,后面再看)
自带的内置endpoint大概有这些(sensitive是指需要密码):
ID | Description | Sensitive Default |
---|---|---|
actuator | 提供端点基本信息,”discovery page” | true |
auditevents | 提供审计事件信息 | true |
autoconfig | 提供auto-configuration报告,显示所有的自动配置项以及生效和不生效的原因 | true |
beans | 显示应用中所有的bean | true |
configprops |
显示整理过的 @ConfigurationProperties
|
true |
dump | 显示线程dump信息 | true |
env |
显示Spring的 ConfigurableEnvironment
属性 |
true |
flyway | 显示应用的所有Flyway database迁移 | true |
health | 显示系统健康状态(登陆后显示详细信息) | false |
info | 显示一些自定义信息 | false |
loggers | 显示和更改应用的logger配置 | true |
liquibase | 显示所有的Liquibase database迁移 | true |
metrics | 显示当前的metrics信息 | true |
mappings |
显示所有 @RequestMapping
|
true |
shutdown | 允许应用程序正常关机(默认情况下未启用)。 | true |
trace | 显示跟踪信息(默认情况下是最近的100个HTTP请求) | true |
如果是Spring MVC, 还有以下几个:
ID | Description | Sensitive Default |
---|---|---|
docs |
显示actuator的文档。需要引入 spring-boot-actuator-docs
|
false |
heapdump |
返回 hprof
文件 |
true |
jolokia | 通过HTTP公开JMX bean(当Jolokia位于类路径中时)。 | true |
logfile | 返回日志文件的内容(如果logging.file或logging.path属性已设置)。支持使用HTTP范围头来检索部分日志文件的内容。 | true |
根据endpoint的作用,可以将原生endpoint分为3类
由于Spring Boot为了改善传统Spring应用复杂的配置,采用了包扫描和自动化配置机制来加载原来集中与XML的内容。虽然这样做让我们代码变得简洁,但整个应用的实例创建和依赖关系等信息都被离散到了各个配置类的注解上。这使我们分析整个应用中资源和实例的各种关系变得非常困难。而这类endpoint可以帮助我们轻松获取一系列关于Spring配置内容的详细报告,比如自动化配置的报告、Bean创建的报告、环境属性的报告等。
/env
: 对于密码属性,属性名中包含password、secret、key这些关键词,会返回**代替value。
/info
: 自定义
info.app.name=spring boot demo info.app.version=1.0
/metrics
: 返回内存信息、线程信息、垃圾回收信息等。
{ "mem": 437985, "mem.free": 342068, "processors": 4, "instance.uptime": 19821025, "uptime": 19812936, "systemload.average": -1, "heap.committed": 382464, "heap.init": 131072, "heap.used": 40395, "heap": 1846272, "nonheap.committed": 56848, "nonheap.init": 2496, "nonheap.used": 55521, "nonheap": 0, "threads.peak": 25, "threads.daemon": 21, "threads.totalStarted": 28, "threads": 23, "classes": 6792, "classes.loaded": 6843, "classes.unloaded": 51, "gc.ps_scavenge.count": 8, "gc.ps_scavenge.time": 98, "gc.ps_marksweep.count": 3, "gc.ps_marksweep.time": 322, "httpsessions.max": -1, "httpsessions.active": 0, "gauge.response.actuator": 224, "gauge.response.loggers": 23, "gauge.response.env": 12, "gauge.response.docs.star-star": 19, "gauge.response.autoconfig": 67, "gauge.response.trace": 17, "gauge.response.metrics": 8, "gauge.response.heapdump.root": 2656, "gauge.response.docs": 3, "gauge.response.configprops": 167, "gauge.response.star-star": 8, "counter.status.302.docs": 2, "counter.status.200.actuator": 1, "counter.status.200.loggers": 1, "counter.status.200.docs.star-star": 4, "counter.status.200.metrics": 2, "counter.status.200.configprops": 1, "counter.status.404.star-star": 5, "counter.status.200.autoconfig": 1, "counter.status.200.heapdump.root": 1, "counter.status.200.env": 1, "counter.status.200.trace": 1 }
对于gauge. 和counter. 的统计, 这里有一 个特殊的内容请求 star-star,它代表了对静态资源的访问。 这两类度量指标非常有用,我们不仅可以使用它默认的统计指标,还可以在程序中轻松地增加自定义 统计 值。只需要通过注org.springframework.boot.actuate.metrics.CounterService和org.springframework.boot.actuate.metrics.GaugeService 来实现自定义的统计指标信息。比如我们可以像下面这样自定义实现对hello接口的访问次数统计。
@RestController public class HelloController { @Qualifier("counterService") @Autowired private CounterService counterService; @RequestMapping(value = "hello", method = RequestMethod.GET) public String hello(){ counterService.increment("test.hello.count"); return counterService.toString(); } }
/metrics端点可以提供应用运行状态的完整度量指标报告,这项功能 非常实用,但是对千监控系 统中的各项监控功能,它们的监控内容、 数据收集频率都有所不同,如果每次都通过全量获取报告的方式来收集,略显粗暴。 所以,我们还可以通过/metrics/{name}接口来更细粒度地获取度量信息 , 比如可以通过访问/metrics/mem.free来获取当前可用内存数量。
/health: 在spring-boot-s七arter-ac七uator模块中自带实现了一 些常用资源的健康指标检测器。这些检测器 都通过Hea巨hindicator接口实现,并且会根据依赖关系的引入实现自动化装配, 比如下面列出的这些。
CassandraHealthIndicator,DiskSpaceHealthIndicator,DataSourceHealthIndicator,ElasticsearchHealthIndicator,JmsHealthIndicator,MailHealthIndicator,MongoHealthIndicator,RabbitHealthIndicator,RedisHealthIndicator,SolrHealthIndicator
可以通过 management.health.defaults.enabled
来控制上述健康检查是否生效。
也可以自定义,
@Component public class RocketMQHealthIndicator implements HealthIndicator { @Override public Health health() { int errorCode = check(); if (errorCode != 0) { return Health.down().withDetail("Error Code", errorCode).build(); } return Health.up().build(); } private int check() { // 自定义健康检查 return 0; } }
/dump: 该端点用来暴露程序运行中的线程信息。它使用 java.lang.rnanagernent.ThreadMXBean 的 durnpAllThreads 方法来返回所有含有同步信息的活动线程详情。
/trace: 该端点用来返回基本的 HTTP 跟踪信息。 默认情况下, 跟踪信息的存储采用org.springfrarnework.boo七.ac七uate.trace.InMernoryTraceRepository实现的内存方式, 默认始终保留最近的100条请求记录。
上述的自带属性可以通过配置文件修改默认配置。key前缀为 endpoints.*
比如,修改 beans
和 shutdown
endpoints.beans.id=springbeans endpoints.beans.sensitive=false endpoints.shutdown.enabled=true
默认所有的endpoint可用,除了 shutdown
。当然,可以是全部关闭,然后单独打开。
endpoints.enabled=false endpoints.info.enabled=true
可以设置全局属性
endpoints.sensitive=true endpoints.info.sensitive=false
如果想在别的域名下调用,则需要设置允许跨域。当然可以自己手动写跨域拦截。但actuator也提供了配置文件。
endpoints.cors.allowed-origins=http://example.com endpoints.cors.allowed-methods=GET,POST
配置 @Bean
的 Endpoint
对象即可。
@Bean public Endpoint myEndpoint(){ return new Endpoint<Map<String,Object>>() { @Override public String getId() { return "my-endpoint"; } @Override public boolean isEnabled() { return true; } @Override public boolean isSensitive() { return false; } @Override public Map<String,Object> invoke() { final Map<String, Object> dataToDisplay = new HashMap<>(); dataToDisplay.put("some", "thing"); dataToDisplay.put("you", "want"); return dataToDisplay; } }; }