Appearance
Spring In Action 6th:使用 JMX 监控 Spring
1. 使用 Actuator MBean
默认情况下,所有 Actuator 端点都会作为 MBeans 被暴露。但是,从 Spring Boot 2.2 开始,JMX 本身默认是禁用的。要在你的 Spring Boot 应用程序中启用 JMX,你可以将 spring.jmx.enabled 设置为 true。在 application.yml 中,这会是这样的:
YAML
spring:
jmx:
enabled: true1
2
3
2
3
设置了该属性后,Spring 对 JMX 的支持就启用了。有了它,Actuator 的所有端点都被暴露为 MBeans。你可以使用任何你希望的 JMX 客户端来连接 Actuator 端点 MBeans。比如使用 JConsole,它随 Java 开发套件一起提供,你会发现 Actuator MBeans 列在 org.springframework.boot 域下,如图 1.1 所示:

Actuator MBean 端点的一个好处是它们默认都是暴露的,无需像处理 HTTP 那样显式地包含它们。然而,你可以通过设置 management.endpoints.jmx.exposure.include 和 management.endpoints.jmx.exposure.exclude 来缩小选择范围。例如,要将 Actuator 端点 MBeans 限制为仅 /health、/info、/bean 和 /conditions 端点,可以这样设置 management.endpoints.jmx.exposure.include:
YAML
management:
endpoints:
jmx:
exposure:
include: health,info,bean,conditions1
2
3
4
5
2
3
4
5
或者,如果你只想排除几个,你可以这样设置 management.endpoints.jmx.exposure.exclude:
YAML
management:
endpoints:
jmx:
exposure:
exclude: env,metrics1
2
3
4
5
2
3
4
5
在这里,你使用 management.endpoints.jmx.exposure.exclude 来排除 /env 和 /metrics 端点。所有其他的 Actuator 端点仍然会被暴露为 MBeans。
要在 JConsole 中调用 Actuator MBeans 的管理操作,展开左侧树中的端点 MBean,然后选择 Operations 下的所需操作。
例如,如果你想查看 tacos.ingredients 包的日志级别,展开 Loggers MBean 并点击名为 loggerLevels 的操作,如图 1.2 所示。在右上角的表单中,用包名(例如 org.springframework.web)填充 Name 字段,然后点击 loggerLevels 按钮。

点击 loggerLevels 按钮后,会弹出一个对话框,显示来自 /loggers 端点 MBean 的响应。它可能看起来有点像图 1.3。

/loggers 端点 MBean 的日志级别虽然 JConsole 的用户界面有点笨拙,但你应该能够掌握它,并用它以大致相同的方式探索任何 Actuator 端点。如果你不喜欢 JConsole,那没关系——还有很多其他的 JMX 客户端可以选择。
2. 创建自己的 MBean
Spring 使得将你想要的任何 bean 暴露为 JMX MBean 变得简单。你只需要用 @ManagedResource 注解 bean 类,然后用 @ManagedOperation 或 @ManagedAttribute 注解任何方法或属性。Spring 会处理剩下的事情。
例如,假设你想提供一个 MBean 来跟踪通过 Taco Cloud 订购了多少 tacos。你可以定义一个服务 bean,保持对创建的 tacos 数量的运行计数。以下示例代码显示了这样一个服务可能是什么样子:
Java
@Service
@ManagedResource
public class TacoCounter
extends AbstractRepositoryEventListener<Taco> {
private AtomicLong counter;
public TacoCounter(TacoRepository tacoRepo) {
tacoRepo
.count()
.subscribe(initialCount -> {
this.counter = new AtomicLong(initialCount);
});
}
@Override
protected void onAfterCreate(Taco entity) {
counter.incrementAndGet();
}
@ManagedAttribute
public long getTacoCount() {
return counter.get();
}
@ManagedOperation
public long increment(long delta) {
return counter.addAndGet(delta);
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
TacoCounter 类用 @Service 注解,这样它就会被组件扫描捕获,并且一个实例将会被注册为 Spring 应用上下文中的一个 bean。但是它也用 @ManagedResource 注解,表明这个 bean 也应该是一个 MBean。作为一个 MBean,它将会暴露一个属性和一个操作。getTacoCount() 方法用 @ManagedAttribute 注解,这样它就会被暴露为一个 MBean 属性,而 increment() 方法用 @ManagedOperation 注解,将其暴露为一个 MBean 操作。图 2.1 展示了 TacoCounter MBean 在 JConsole 中的样子。
TacoCounter 还有另一个小把戏,尽管它与 JMX 无关。因为它扩展了 AbstractRepositoryEventListener,所以当通过 TacoRepository 保存一个 Taco 时,它会被通知任何持久化事件。在这个特定的情况下,每当创建并保存一个新的 Taco 对象时,onAfterCreate() 方法将会被调用,并且它会将计数器增加一。此外 AbstractRepositoryEventListener 还提供了几个方法来处理对象被创建、保存或删除之前和之后的事件。

TacoCounter 的操作和属性使用 MBean 操作和属性主要是一个拉取操作。也就是说,即使 MBean 属性的值发生了变化,你也不会知道,直到你通过 JMX 客户端查看该属性。让我们换个角度,看看你如何可以从 MBean 向 JMX 客户端推送通知。
3. 发送通知
MBeans 可以通过 Spring 的 NotificationPublisher 向感兴趣的 JMX 客户端推送通知。NotificationPublisher 有一个 sendNotification() 方法,当给定一个 Notification 对象时,它会将通知发布给订阅了 MBean 的任何 JMX 客户端。
要使 MBean 能够发布通知,它必须实现 NotificationPublisherAware 接口,该接口要求实现一个 setNotificationPublisher() 方法。例如,假设你想为每创建 100 个 tacos 发布一个通知。你可以更改 TacoCounter 类,使其实现 NotificationPublisherAware 并使用注入的 NotificationPublisher 来为每创建 100 个 tacos 发送通知。
以下示例代码显示了必须对 TacoCounter 进行的更改以启用此类通知:
Java
@Service
@ManagedResource
public class TacoCounter
extends AbstractRepositoryEventListener<Taco>
implements NotificationPublisherAware {
private AtomicLong counter;
private NotificationPublisher np;
@Override
public void setNotificationPublisher(NotificationPublisher np) {
this.np = np;
}
...
@ManagedOperation
public long increment(long delta) {
long before = counter.get();
long after = counter.addAndGet(delta);
if ((after / 100) > (before / 100)) {
Notification notification = new Notification(
"taco.count", this,
before, after + "th taco created!");
np.sendNotification(notification);
}
return after;
}
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
在 JMX 客户端中,你需要订阅 TacoCounter MBean 以接收通知。然后,随着 tacos 的创建,客户端将在每满 100 计数时接收通知。图 3.1 显示了通知在 JConsole 中可能的显示方式:

TacoCounter MBean 的 JConsole,每创建 100 个 tacos 就会接收到一个通知通知是一种很好的方式,可以让应用程序主动向监控客户端发送数据和警报,而无需客户端轮询管理的属性或调用管理的操作。
4. 总结
大多数 Actuator 端点都可以作为 MBeans 使用,可以使用任何 JMX 客户端进行消费;
Spring 自动启用 JMX 以监控 Spring 应用程序上下文中的 beans;
通过使用
@ManagedResource注解,Spring beans 可以被暴露为 MBeans。通过在 bean 类上使用@ManagedOperation和@ManagedAttribute注解,它们的方法和属性可以被暴露为管理操作和属性;Spring beans 可以使用
NotificationPublisher向 JMX 客户端发布通知;