SpringBoot生产监控
一、健康监控简介
1、介绍
开发完成后,生产就绪需要做哪些工作呢?我认为,以下三方面的工作最重要。
- 提供健康检测接口。传统采用 ping 的方式对应用进行探活检测并不准确。有的时候,应用的关键内部或外部依赖已经离线,导致其根本无法正常工作,但其对外的 Web 端口或管理端口是可以 ping 通的。我们应该提供一个专有的监控检测接口,并尽可能触达一些内部组件。
- 暴露应用内部信息。应用内部诸如线程池、内存队列等组件,往往在应用内部扮演了重要的角色,如果应用或应用框架可以对外暴露这些重要信息,并加以监控,那么就有可能在诸如 OOM 等重大问题暴露之前发现蛛丝马迹,避免出现更大的问题。
- 建立应用指标 Metrics 监控。Metrics 可以翻译为度量或者指标,指的是对于一些关键信息以可聚合的、数值的形式做定期统计,并绘制出各种趋势图表。这里的指标监控,包括两个方面:一是,应用内部重要组件的指标监控,比如 JVM 的一些指标、接口的 QPS 等;二是,应用的业务数据的监控,比如电商订单量、游戏在线人数等。
2、SpringBoot准备工作
Spring Boot 有一个 Actuator 模块,封装了诸如健康检测、应用内部信息、Metrics 指标等生产就绪的功能。今天这一讲后面的内容都是基于 Actuator 的,因此我们需要先完成 Actuator 的引入和配置
1 | <dependency> |
Actuator 自带了很多开箱即用提供信息的端点(Endpoint),可以通过** JMX 或 Web **两种方式进行暴露。考虑到有些信息比较敏感,这些内置的端点默认不是完全开启的,你可以通过官网查看这些默认值。在这里,为了方便后续 Demo,我们设置所有端点通过 Web 方式开启。
默认情况下,Actuator 的 Web 访问方式的根地址为 /actuator
,可以通过management.endpoints.web.base-path
参数进行修改
1 | management: |
现在访问http://localhost:45678/admin
可以查看 Actuator 的所有功能 URL
3、其他
大部分端点提供的是只读信息,比如查询 Spring 的 Bean、ConfigurableEnvironment、定时任务、SpringBoot 自动配置、Spring MVC 映射等;少部分端点还提供了修改功能,比如优雅关闭程序、下载线程 Dump、下载堆 Dump、修改日志级别等。
我们可以访问这里,查看所有这些端点的功能,详细了解它们提供的信息以及实现的操作。此外推荐一个很好的工具, Spring Boot 管理工具Spring Boot Admin,它把大部分 Actuator 端点提供的功能封装为了 Web UI。可以参考:Spring Boot Admin服务监控
二、健康检测触达关键组件
1、内置组件健康详情
健康检测接口可以让监控系统或发布工具知晓应用的真实健康状态,比 ping 应用端口更可靠。不过,要达到这种效果最关键的是,我们能确保健康检测接口可以探查到关键组件的状态。好在 Spring Boot Actuator
帮我们预先实现了诸如数据库、InfluxDB、Elasticsearch、Redis、RabbitMQ 等三方系统的健康检测指示器 HealthIndicator。
通过 Spring Boot 的自动配置,这些指示器会自动生效。当这些组件有问题的时候,HealthIndicator 会返回 DOWN
或 OUT_OF_SERVICE
状态,health 端点 HTTP 响应状态码也会变为 503,我们可以以此来配置程序健康状态监控报警。
1 | management: |
我们可以修改配置文件,把 management.endpoint.health.show-details 参数设置为 always,让所有用户都可以直接查看各个组件的健康情况(如果配置为 when-authorized,那么可以结合 management.endpoint.health.roles 配置授权的角色)。访问 health 端点可以看到,数据库、磁盘、RabbitMQ、Redis 等组件健康状态是 UP,整个应用的状态也是 UP
2、自定义组件健康详情
如果程序依赖一个很重要的三方服务,我们希望这个服务无法访问的时候,应用本身的健康状态也是 DOWN,首先创建User类以及配置bean
1 |
|
比如三方服务有一个 user 接口,出现异常的概率是 50%:
1 | 4j |
要实现这个 user 接口是否正确响应和程序整体的健康状态挂钩的话,很简单,只需定义一个 UserServiceHealthIndicator
实现 HealthIndicator
接口即可。
在 health 方法中,我们通过 RestTemplate 来访问这个 user 接口,如果结果正确则返回 Health.up()
,并把调用执行耗时和结果作为补充信息加入 Health 对象中。如果调用接口出现异常,则返回 Health.down()
,并把异常信息作为补充信息加入 Health 对象中:
1 |
|
此时访问http://localhost:45678/admin/health
即可发现UserService已经成功被检测
3、自定义多 HealthIndicator 聚合
我们再来看一个聚合多个 HealthIndicator 的案例,也就是定义一个 CompositeHealthContributor 来聚合多个 HealthContributor,实现一组线程池的监控
首先,在 ThreadPoolProvider 中定义两个线程池,其中 demoThreadPool 是包含一个工作线程的线程池,类型是 ArrayBlockingQueue,阻塞队列的长度为 10;还有一个 ioThreadPool 模拟 IO 操作线程池,核心线程数 10,最大线程数 50
1 | public class ThreadPoolProvider { |
然后,我们定义一个接口,来把耗时很长的任务提交到这个 demoThreadPool 线程池,以模拟线程池队列满的情况:
1 | "slowTask") ( |
做了这些准备工作后,让我们来真正实现自定义的 HealthIndicator
类,用于单一线程池的健康状态。我们可以传入一个 ThreadPoolExecutor
,通过判断队列剩余容量来确定这个组件的健康状态,有剩余量则返回 UP,否则返回 DOWN,并把线程池队列的两个重要数据,也就是当前队列元素个数和剩余量,作为补充信息加入 Health
1 | public class ThreadPoolHealthIndicator implements HealthIndicator { |
再定义一个 CompositeHealthContributor
,来聚合两个 ThreadPoolHealthIndicator
的实例,分别对应 ThreadPoolProvider
中定义的两个线程池:
1 |
|
启动后当看到一个 demoThreadPool
为 DOWN 导致父 threadPools
为 DOWN,进一步导致整个程序的 status
为 DOWN:
三、对外暴露应用内部重要组件的状态
1、内部状态数据暴露
除了可以把线程池的状态作为整个应用程序是否健康的依据外,我们还可以通过 Actuator 的 InfoContributor
功能,对外暴露程序内部重要组件的状态数据
1 |
|
访问 /admin/info 接口,可以看到这些数据
2、JMX MBean
如果开启了JMX,即spring.jmx.enabled=true
,可以通过 jconsole 工具,在 org.springframework.boot.Endpoint
中找到 Info
这个 MBean,然后执行 info 操作可以看到,我们刚才自定义的 InfoContributor
输出的有关两个线程池的信息:
四、指标 Metrics 快速定位
指标是指一组和时间关联的、衡量某个维度能力的量化数值。通过收集指标并展现为曲线图、饼图等图表,可以帮助我们快速定位、分析问题
五、总结
健康检测可以帮我们实现负载均衡的联动;应用信息以及 Actuaor 提供的各种端点,可以帮我们查看应用内部情况,甚至对应用的一些参数进行调整;而指标监控,则有助于我们整体观察应用运行情况,帮助我们快速发现和定位问题