在 Java 世界中,Spring 框架已经是最主流的开发框架了。但传统 Spring 框架存在一个明显的问题,那就是 缺少系统监控功能。如果想要获取一个 Spring 应用程序的线程工作状态以及 JVM 性能指标等各种运行时数据,我们就不得不借助一些第三方工具,这在云原生时代无疑加重了系统运维管理的成本。
好在,Spring Boot 框架诞生了,它不仅继承了 Spring 框架原有的优良特性,而且又引入了一个创新型的技术组件,即 Spring Boot Actuator,该组件可以用来实现内嵌的系统监控功能,完美地解决了原有 Spring 框架缺少系统监控功能的问题。让我们一起来看一下吧!
Spring Boot Actuator
Actuator 组件是 Spring Boot 中承载系统监控功能的组件,该组件通过一系列 HTTP 端点提供监控能力。Spring Boot 的强大之处就在于为开发人员内置了一组非常简单而实用的原生监控端点。在接下来的内容中,我们先介绍一些常用的 Actuator 端点。
原生 Actuator 端点
说到监控端点,你可能会觉得这个概念有点儿抽象。实际上,所谓的监控端点,就是一个普通的 HTTP 请求地址。当我们启动一个 Spring Boot 应用程序,可以访问 http://localhost:8080/actuator 这个 HTTP 地址来获取所有可用的端点信息。
json
{
"_links":{
"self":{
"href":"http://localhost:8080/actuator",
"templated":false
},
"health-path":{
"href":"http://localhost:8080/actuator/health/{*path}",
"templated":true
},
"health":{
"href":"http://localhost:8080/actuator/health",
"templated":false
},
"info":{
"href":"http://localhost:8080/actuator/info",
"templated":false
}
}
}
可以看到,这些都是 HATEOAS 风格的 HTTP 端点信息。我们在这里找到了两个非常常用的端点,即 health 端点和 info 端点。以 health 端点为例,我们可以通过该端点进一步获取系统的健康状态信息。
json
{
"status":"UP",
"components":{
"diskSpace":{
"status":"UP",
"details":{
"total":201649549312,
"free":3434250240,
"threshold":10485760
}
},
"ping":{
"status":"UP"
}
}
}
可以看到,这里展示了当前系统的磁盘空间系统以及网络连接信息。事实上,在 Spring Boot Actuator 中包含了一组类似 health 端点的监控端点。我们可以把这些端点按照各自提供的功能进行分类,包括应用配置、度量指标和操作控制这三大类。

图 1 Spring Boot Actuator 的三大类原生端点
其中,应用配置类端点的作用就是提供各种 Spring Boot 应用程序相关的配置信息,典型的包括/beans、/env、/info 等端点。通过这些端点,开发人员可以获取应用程序中所包含的 JavaBean 信息、环境变量信息以及各种自定义的配置信息等。

图 2 常见的应用配置类端点
顾名思义,度量指标类的监控端点一方面用来获取内存信息、线程信息等各种重要的度量指标,同时也可以正确反映应用程序的健康指标信息,这部分的常见端点有/metrics、/threaddump 和/health 端点等。

图 3 常见的度量指标类端点
相比这两类端点,操作控制类的端点则数量较少,常见的只有用来对应用程序执行关闭操作的/shutdown 端点。
如果 Spring Boot Actuator 默认提供的端点信息不能满足需求,我们还可以对其进行修改和扩展。常见实现方案有两种,一种是扩展现有的监控端点,另一种是自定义新的监控端点。
扩展 Actuator 端点
接下来,我们来关注一下如何在现有的监控端点上添加定制化功能。我们同样以前面已经介绍的/health 端点为例展开讨论。
在 Spring Boot 中,Health 端点用于检查正在运行的应用程序健康状态。Health 端点信息的丰富程度取决于当下应用程序所处的环境,一个现实环境下的 Health 端点信息如下所示。通过这些信息,我们可以判断该环境中包含了 MySQL 数据库。
json
{
"status":"UP",
"components":{
"db":{
"status":"UP",
"details":{
"database":"MySQL",
"result":1,
"validationQuery":"/* ping */ SELECT 1"
}
},
"diskSpace":{
"status":"UP",
"details":{
"total":201649549312,
"free":3491287040,
"threshold":10485760
}
},
"ping":{
"status":"UP"
}
}
}
现在,我们希望在 Health 端点中暴露某个应用程序的当前运行时状态。这时候就可以自定义一个 CustomHealthIndicator 端点。我们明确,健康状态信息是由 HealthIndicator 接口从 Spring 的 ApplicationContext 中进行获取的,所以这个 CustomHealthIndicator 需要实现 HealthIndicator 接口。
scss
@Component
publicclass CustomHealthIndicator implements HealthIndicator {
@Override
public Health health() {
try {
URL url = new URL("http://localhost:8083/health/");
HttpURLConnection conn = (HttpURLConnection)
url.openConnection();
int statusCode = conn.getResponseCode();
if (statusCode >= 200 && statusCode < 300) {
return Health.up().build();
} else {
return Health.down().withDetail("HTTP Status Code", statusCode).build();
}
} catch (IOException e) {
return Health.down(e).build();
}
}
}
我们需要提供 health() 方法的具体实现并返回一个 Health 结果对象。该结果对象应该包括一个状态,并且可以根据需要添加任何细节信息。
以上代码用一种简单而直接的方式判断某个服务是否正在运行。我们构建一个 HTTP 请求,然后根据 HTTP 响应得出健康诊断的结论。如果 HTTP 响应的状态码处于 200~300 之间,我们就认为该服务正在运行,Health.up().build() 方法将返回一种 UP 响应,如下所示。
json
{
"status": "UP",
"details": {
"custom":{
"status": "UP"
}
...
}
}
如果状态码不是处于这个区间(例如返回的是 404 代表服务不可用)就返回一个 DOWN 响应并给出具体的状态码,如下所示。
css
{
"status": "DOWN",
"details": {
"custom":{
"status": "DOWN",
"details": {
"HTTP Status Code": "404"
}
},
...
}
}
如果 HTTP 请求直接抛出了异常,我们同样返回一个 Down 响应,同时把异常信息一起返回,效果如下所示:
json
{
"status": "DOWN",
"details": {
"custom":{
"status": "DOWN",
"details": {
"error": "java.net.ConnectException: Connection refused: connect"
}
},
...
}
}
显然,通过扩展 Health 端点为我们实时监控系统中各个服务的正常运行状态提供了很好的支持,你可以根据需要构建一系列有用的 HealthIndicator 实现类并添加报警等监控手段。
自定义 Actuator 端点
除了对现有的监控端点进行动态扩展,有时候我们还可以根据业务场景的需要创建新的监控端点。这里举一个简单的例子。现在,假设我们的需求是获取当前操作系统的计算机名称,那么就可以实现这样一个新的 CustomEndpoint。
typescript
@Configuration
@Endpoint(id = "computername", enableByDefault=true)
public class CustomEndpoint {
@ReadOperation
public Map<String, Object> getMySystemInfo() {
Map<String,Object> result= new HashMap<>();
Map<String, String> map = System.getenv();
result.put("computername",map.get("COMPUTERNAME"));
return result;
}
}
可以看到,CustomEndpoint 通过 System.getenv() 方法获取了系统的环境变量,然后再通过环境变量获取了计算机名称。现在,让我们执行这个 CustomEndpoint 端点,得到的结果是这样的。
json
{
"computername":"LAPTOP-EQB59J5P"
}
Spring Boot Admin
Spring Boot 还基于 Actuator 组件为开发人员提供了可视化的系统监控组件,这就是 Spring Boot Admin。通过 Admin 组件,我们可以获取系统运行时的各项关键指标,并通过友好的交互界面进行动态管理。
Spring Boot Admin 会消费前面介绍到的各种 Actuator 的端点信息并将这些信息进行统计和聚合,它的基本原理是这样的。

图 4 Spring Boot Admin 基本原理图
从上图中,我们首先需要明确存在一个服务器组件 Admin Server,它负责从各个 Admin Client 所暴露的 Actuator 端点中收集各种监控信息。注意,这里的 Admin Server 和 Admin Client 本质上都是一个个 Spring Boot 应用程序。然后,Admin Server 会对这些监控信息进行加工处理,并最终通过 Web UI 以可视化的效果展示给开发人员。
Spring Boot Admin 的功能非常强大,包括显示健康状态、JVM、内存等度量明细信息,以及线程、HTTP 跟踪等监控信息。基于 Admin Server,这些功能都通过可视化的 UI 界面进行展示。这里,我截取了几张效果图。这是 Admin Server 监控信息的主界面。

图 5 Admin Server 监控信息主界面
在这里,我们看到了熟悉的"Health"信息。然后,我们注意到在界面的左下角有一个"JVM"选项,点击该选项可以获取与 JVM 相关的监控信息。

图 6 Admin Server 中的 JVM 监控信息
最后,我们来看一下非常有用的"Thread Dump"可视化功能。Admin Server 通过这一功能提供了一个连续性的可视化 Dump 快照信息监控界面。

图 7 Admin Server 中的 Thread Dump 信息
总结
好了,以上就是我这节课想要和你分享的内容,最后我们来对今天的内容进行一个简单的梳理吧!
今天我们主要介绍了基于 Spring 产生的 Spring Boot 框架。其中,Spring Boot 内置的 Actuator 组件为开发人员管理应用程序的运行时状态提供了更加直接且高效的手段。
在今天的内容中,我们引入了 Actuator 组件并介绍了该组件所提供的一系列核心端点。更为重要的是,我们还重点分析了如何对 Actuator 端点进行扩展以及创建自定义 Actuator 端点的实现方法。这些实现方法都可以直接应用到日常开发过程中。而作为延伸,在今天内容的最后,我们还分析了 Spring Boot Admin 这一组件提供的强大可视化监控效果。
