跳到主要内容

可观察性支持

ChatGPT-4o-mini 中英对照 Observability Support

Micrometer 定义了一个 观察概念,可以在应用程序中同时启用指标和追踪。指标支持提供了一种创建计时器、仪表或计数器的方法,以收集有关应用程序运行时行为的统计信息。指标可以帮助您跟踪错误率、使用模式、性能等。追踪提供了对整个系统的整体视图,跨越应用程序边界;您可以深入特定用户请求,并跟踪它们在各个应用程序中的完整完成过程。

Spring Framework 对其自身代码库的各个部分进行了插桩,以便在配置了 ObservationRegistry 的情况下发布观察结果。您可以了解更多关于 在 Spring Boot 中配置可观察性基础设施 的信息。

生成的观察列表

Spring Framework 提供了各种可观察性功能。如本节开头所述 在这一部分,观察可以根据配置生成计时器 Metrics 和/或 Traces。

表 1. Spring Framework 生成的观察结果

观察名称描述
"http.client.requests"HTTP 客户端交换所花费的时间
"http.server.requests"在框架级别上处理 HTTP 服务器交换的时间
"jms.message.publish"消息生产者向目标发送 JMS 消息所花费的时间
"jms.message.process"之前由消息消费者接收的 JMS 消息的处理时间
"tasks.scheduled.execution"执行 @Scheduled 任务的处理时间
备注

观察使用 Micrometer 的官方命名约定,但指标名称将自动转换为 监控系统后端所偏好的格式 (Prometheus, Atlas, Graphite, InfluxDB…​)。

Micrometer 观察概念

如果您对 Micrometer Observation 不熟悉,这里有一个您应该了解的概念的快速总结。

  • Observation 是在您的应用程序中发生某事的实际记录。这是由 ObservationHandler 实现处理的,以生成指标或跟踪。

  • 每个观察都有一个相应的 ObservationContext 实现;该类型保存提取其元数据所需的所有相关信息。在 HTTP 服务器观察的情况下,上下文实现可以保存 HTTP 请求、HTTP 响应、处理过程中抛出的任何异常等等。

  • 每个 Observation 持有 KeyValues 元数据。在 HTTP 服务器观察的情况下,这可能是 HTTP 请求方法、HTTP 响应状态等等。这些元数据由 ObservationConvention 实现贡献,这些实现应该声明它们支持的 ObservationContext 类型。

  • 如果 KeyValues 的可能值数量较少且有限,则称其为“低基数”(low cardinality)(HTTP 方法就是一个很好的例子)。低基数值仅贡献给指标。相反,“高基数”(high cardinality)值是无限的(例如,HTTP 请求 URI),仅贡献给跟踪。

  • ObservationDocumentation 记录特定领域中的所有观察,列出预期的键名称及其含义。

配置观察

全局配置选项可在 ObservationRegistry#observationConfig() 级别获得。每个被监测的组件将提供两个扩展点:

  • 设置 ObservationRegistry;如果未设置,观察将不会被记录,并且将无操作

  • 提供自定义 ObservationConvention 以更改默认观察名称和提取的 KeyValues

使用自定义观察约定

让我们以 Spring MVC 的 "http.server.requests" 指标仪表化和 ServerHttpObservationFilter 为例。此观察使用 ServerRequestObservationConventionServerRequestObservationContext;可以在 Servlet 过滤器上配置自定义约定。如果您希望自定义与观察一起生成的元数据,可以根据您的需求扩展 DefaultServerRequestObservationConvention

import io.micrometer.common.KeyValue;
import io.micrometer.common.KeyValues;

import org.springframework.http.server.observation.DefaultServerRequestObservationConvention;
import org.springframework.http.server.observation.ServerRequestObservationContext;

public class ExtendedServerRequestObservationConvention extends DefaultServerRequestObservationConvention {

@Override
public KeyValues getLowCardinalityKeyValues(ServerRequestObservationContext context) {
// here, we just want to have an additional KeyValue to the observation, keeping the default values
return super.getLowCardinalityKeyValues(context).and(custom(context));
}

private KeyValue custom(ServerRequestObservationContext context) {
return KeyValue.of("custom.method", context.getCarrier().getMethod());
}

}
java
import java.util.Locale;

import io.micrometer.common.KeyValue;
import io.micrometer.common.KeyValues;

import org.springframework.http.server.observation.ServerHttpObservationDocumentation;
import org.springframework.http.server.observation.ServerRequestObservationContext;
import org.springframework.http.server.observation.ServerRequestObservationConvention;

public class CustomServerRequestObservationConvention implements ServerRequestObservationConvention {

@Override
public String getName() {
// will be used as the metric name
return "http.server.requests";
}

@Override
public String getContextualName(ServerRequestObservationContext context) {
// will be used for the trace name
return "http " + context.getCarrier().getMethod().toLowerCase(Locale.ROOT);
}

@Override
public KeyValues getLowCardinalityKeyValues(ServerRequestObservationContext context) {
return KeyValues.of(method(context), status(context), exception(context));
}

@Override
public KeyValues getHighCardinalityKeyValues(ServerRequestObservationContext context) {
return KeyValues.of(httpUrl(context));
}

private KeyValue method(ServerRequestObservationContext context) {
// You should reuse as much as possible the corresponding ObservationDocumentation for key names
return KeyValue.of(ServerHttpObservationDocumentation.LowCardinalityKeyNames.METHOD, context.getCarrier().getMethod());
}

// status(), exception(), httpUrl()...

private KeyValue status(ServerRequestObservationContext context) {
return KeyValue.of(ServerHttpObservationDocumentation.LowCardinalityKeyNames.STATUS, String.valueOf(context.getResponse().getStatus()));
}

private KeyValue exception(ServerRequestObservationContext context) {
String exception = (context.getError() != null ? context.getError().getClass().getSimpleName() : KeyValue.NONE_VALUE);
return KeyValue.of(ServerHttpObservationDocumentation.LowCardinalityKeyNames.EXCEPTION, exception);
}

private KeyValue httpUrl(ServerRequestObservationContext context) {
return KeyValue.of(ServerHttpObservationDocumentation.HighCardinalityKeyNames.HTTP_URL, context.getCarrier().getRequestURI());
}

}
java

您还可以使用自定义 ObservationFilter 实现类似的目标 - 为观察添加或删除关键值。过滤器不会替代默认约定,而是作为后处理组件使用。

import io.micrometer.common.KeyValue;
import io.micrometer.observation.Observation;
import io.micrometer.observation.ObservationFilter;

import org.springframework.http.server.observation.ServerRequestObservationContext;

public class ServerRequestObservationFilter implements ObservationFilter {

@Override
public Observation.Context map(Observation.Context context) {
if (context instanceof ServerRequestObservationContext serverContext) {
context.setName("custom.observation.name");
context.addLowCardinalityKeyValue(KeyValue.of("project", "spring"));
String customAttribute = (String) serverContext.getCarrier().getAttribute("customAttribute");
context.addLowCardinalityKeyValue(KeyValue.of("custom.attribute", customAttribute));
}
return context;
}
}
java

您可以在 ObservationRegistry 上配置 ObservationFilter 实例。

@Scheduled 任务的监控

每次执行 @Scheduled 任务 创建一个观察。应用程序需要在 ScheduledTaskRegistrar 上配置 ObservationRegistry 以启用观察记录。这可以通过声明一个设置观察注册表的 SchedulingConfigurer bean 来完成:

import io.micrometer.observation.ObservationRegistry;

import org.springframework.scheduling.annotation.SchedulingConfigurer;
import org.springframework.scheduling.config.ScheduledTaskRegistrar;

public class ObservationSchedulingConfigurer implements SchedulingConfigurer {

private final ObservationRegistry observationRegistry;

public ObservationSchedulingConfigurer(ObservationRegistry observationRegistry) {
this.observationRegistry = observationRegistry;
}

@Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setObservationRegistry(this.observationRegistry);
}

}
java

它默认使用 org.springframework.scheduling.support.DefaultScheduledTaskObservationConvention,由 ScheduledTaskObservationContext 支持。您可以直接在 ObservationRegistry 上配置自定义实现。在调度方法执行期间,当前观察会在 ThreadLocal 上下文或 Reactor 上下文中恢复(如果调度方法返回 MonoFlux 类型)。

默认情况下,以下 KeyValues 被创建:

表 2. 低基数键

名称描述
code.function (必需)要调度执行的 Java Method 的名称。
code.namespace (必需)持有调度方法的 bean 实例的类的规范名称,或 "ANONYMOUS" 用于匿名类。
error (必需)在执行过程中抛出的异常的类名,或如果没有发生异常则为 "none"
exception (已弃用)重复 error 键,可能在未来被移除。
outcome (必需)方法执行的结果。可以是 "SUCCESS""ERROR""UNKNOWN"(例如,如果操作在执行过程中被取消)。

JMS 消息传递监控

Spring Framework 使用 Micrometer 提供的 Jakarta JMS 工具,如果 io.micrometer:micrometer-jakarta9 依赖在类路径上。 io.micrometer.jakarta9.instrument.jms.JmsInstrumentationjakarta.jms.Session 进行工具化,并记录相关观察。

该仪器将创建 2 种类型的观察:

  • "jms.message.publish" 当 JMS 消息被发送到代理时,通常使用 JmsTemplate

  • "jms.message.process" 当 JMS 消息被应用程序处理时,通常使用 MessageListener 或带有 @JmsListener 注解的方法。

备注

目前没有对 "jms.message.receive" 观察的仪器,因为测量等待接收消息的时间价值不大。这种集成通常会对 MessageConsumer#receive 方法调用进行仪器化。但一旦这些返回,处理时间就无法测量,追踪范围也无法传播到应用程序。

默认情况下,这两个观察共享相同的一组可能的 KeyValues

表 3. 低基数键

名称描述
error在消息操作期间抛出的异常的类名(或“无”)。
exception (已弃用)重复 error 键,可能在未来被移除。
messaging.destination.temporary (必需)目标是否为 TemporaryQueueTemporaryTopic(值:"true""false")。
messaging.operation (必需)正在执行的 JMS 操作的名称(值:"publish""process")。

表 4. 高基数键

名称描述
messaging.message.conversation_idJMS 消息的关联 ID。
messaging.destination.name当前消息发送到的目标名称。
messaging.message.id消息系统用作消息标识符的值。

JMS 消息发布监控

"jms.message.publish" 观察记录在向代理发送 JMS 消息时。这些观察测量发送消息所花费的时间,并通过传出的 JMS 消息头传播跟踪信息。

您需要在 JmsTemplate 上配置 ObservationRegistry 以启用观察:

import io.micrometer.observation.ObservationRegistry;
import jakarta.jms.ConnectionFactory;

import org.springframework.jms.core.JmsMessagingTemplate;
import org.springframework.jms.core.JmsTemplate;

public class JmsTemplatePublish {

private final JmsTemplate jmsTemplate;

private final JmsMessagingTemplate jmsMessagingTemplate;

public JmsTemplatePublish(ObservationRegistry observationRegistry, ConnectionFactory connectionFactory) {
this.jmsTemplate = new JmsTemplate(connectionFactory);
// configure the observation registry
this.jmsTemplate.setObservationRegistry(observationRegistry);

// For JmsMessagingTemplate, instantiate it with a JMS template that has a configured registry
this.jmsMessagingTemplate = new JmsMessagingTemplate(this.jmsTemplate);
}

public void sendMessages() {
this.jmsTemplate.convertAndSend("spring.observation.test", "test message");
}

}
java

它默认使用 io.micrometer.jakarta9.instrument.jms.DefaultJmsPublishObservationConvention,由 io.micrometer.jakarta9.instrument.jms.JmsPublishObservationContext 支持。

类似的观察也记录在使用 @JmsListener 注解的方法中,当响应消息从监听器方法返回时。

JMS 消息处理仪器

"jms.message.process" 观察记录在应用程序处理 JMS 消息时。它们测量处理消息所花费的时间,并通过传入的 JMS 消息头传播追踪上下文。

大多数应用程序将使用 @JmsListener 注解的方法 机制来处理传入的消息。您需要确保 ObservationRegistry 在专用的 JmsListenerContainerFactory 上配置:

import io.micrometer.observation.ObservationRegistry;
import jakarta.jms.ConnectionFactory;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jms.annotation.EnableJms;
import org.springframework.jms.config.DefaultJmsListenerContainerFactory;

@Configuration
@EnableJms
public class JmsConfiguration {

@Bean
public DefaultJmsListenerContainerFactory jmsListenerContainerFactory(ConnectionFactory connectionFactory, ObservationRegistry observationRegistry) {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setObservationRegistry(observationRegistry);
return factory;
}

}
java

一个 默认容器工厂是必需的,以启用注解支持,但请注意,@JmsListener 注解可以引用特定的容器工厂 bean 以满足特定目的。在所有情况下,只有在容器工厂上配置了观察注册表时,观察才会被记录。

类似的观察也在 JmsTemplate 中记录,当消息由 MessageListener 处理时。这些监听器是在会话回调中设置在 MessageConsumer 上的(见 JmsTemplate.execute(SessionCallback<T>))。

此观察默认使用 io.micrometer.jakarta9.instrument.jms.DefaultJmsProcessObservationConvention,由 io.micrometer.jakarta9.instrument.jms.JmsProcessObservationContext 支持。

HTTP 服务器监控

HTTP 服务器交换观察是为 Servlet 和 Reactive 应用程序创建的,名称为 "http.server.requests"

Servlet 应用程序

应用程序需要在其应用中配置 org.springframework.web.filter.ServerHttpObservationFilter Servlet 过滤器。它默认使用 org.springframework.http.server.observation.DefaultServerRequestObservationConvention,并由 ServerRequestObservationContext 支持。

这将仅在 Exception 未被 web 框架处理并上升到 Servlet 过滤器时,将观察记录为错误。通常,所有由 Spring MVC 的 @ExceptionHandlerProblemDetail 支持 处理的异常都不会与观察一起记录。您可以在请求处理的任何时刻,自己设置 ObservationContext 上的错误字段:

import jakarta.servlet.http.HttpServletRequest;

import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.filter.ServerHttpObservationFilter;

@Controller
public class UserController {

@ExceptionHandler(MissingUserException.class)
ResponseEntity<Void> handleMissingUser(HttpServletRequest request, MissingUserException exception) {
// We want to record this exception with the observation
ServerHttpObservationFilter.findObservationContext(request)
.ifPresent(context -> context.setError(exception));
return ResponseEntity.notFound().build();
}

static class MissingUserException extends RuntimeException {
}

}
java
备注

由于仪器是在 Servlet 过滤器级别进行的,观察范围仅涵盖此过滤器之后排序的过滤器以及请求的处理。通常,Servlet 容器的错误处理是在更低的级别进行的,并且不会有任何活动的观察或跨度。对于这个用例,需要一个特定于容器的实现,例如 Tomcat 的 org.apache.catalina.Valve;这超出了本项目的范围。

默认情况下,以下 KeyValues 被创建:

表 5. 低基数键

名称描述
error (必需)在交换过程中抛出的异常的类名,或 "none" 如果没有发生异常。
exception (已弃用)重复 error 键,可能在未来被移除。
method (必需)HTTP 请求方法的名称,或 "none" 如果不是一个知名方法。
outcome (必需)HTTP 服务器交换的结果。
status (必需)HTTP 响应原始状态码,或 "UNKNOWN" 如果没有创建响应。
uri (必需)可用的匹配处理程序的 URI 模式,对于 3xx 响应回退到 REDIRECTION,对于 404 响应回退到 NOT_FOUND,对于没有路径信息的请求回退到 root,以及对于所有其他请求回退到 UNKNOWN

表 6. 高基数键

名称描述
http.url (必需)HTTP 请求 URI.

响应式应用

应用程序需要使用 MeterRegistry 配置 WebHttpHandlerBuilder 以启用服务器仪表 instrumentation。这可以在 WebHttpHandlerBuilder 上完成,如下所示:

import io.micrometer.observation.ObservationRegistry;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.server.reactive.HttpHandler;
import org.springframework.web.server.adapter.WebHttpHandlerBuilder;

@Configuration(proxyBeanMethods = false)
public class HttpHandlerConfiguration {

private final ApplicationContext applicationContext;

public HttpHandlerConfiguration(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}

@Bean
public HttpHandler httpHandler(ObservationRegistry registry) {
return WebHttpHandlerBuilder.applicationContext(this.applicationContext)
.observationRegistry(registry)
.build();
}
}
java

它默认使用 org.springframework.http.server.reactive.observation.DefaultServerRequestObservationConvention,由 ServerRequestObservationContext 支持。

这将仅在 Exception 未被应用程序控制器处理时,将观察记录为错误。通常,通过 Spring WebFlux 的 @ExceptionHandlerProblemDetail 支持 处理的所有异常将不会与观察一起记录。您可以在请求处理的任何时刻,自己设置 ObservationContext 上的错误字段:

import org.springframework.http.ResponseEntity;
import org.springframework.http.server.reactive.observation.ServerRequestObservationContext;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.server.ServerWebExchange;

@Controller
public class UserController {

@ExceptionHandler(MissingUserException.class)
ResponseEntity<Void> handleMissingUser(ServerWebExchange exchange, MissingUserException exception) {
// We want to record this exception with the observation
ServerRequestObservationContext.findCurrent(exchange.getAttributes())
.ifPresent(context -> context.setError(exception));
return ResponseEntity.notFound().build();
}

static class MissingUserException extends RuntimeException {
}

}
java

默认情况下,以下 KeyValues 被创建:

表 7. 低基数键

名称描述
error (必填)在交换过程中抛出的异常的类名,如果没有异常发生则为 "none"
exception (已弃用)重复 error 键,可能在未来被移除。
method (必填)HTTP 请求方法的名称,如果不是一个知名方法则为 "none"
outcome (必填)HTTP 服务器交换的结果。
status (必填)HTTP 响应的原始状态码,如果没有创建响应则为 "UNKNOWN"
uri (必填)如果可用,匹配处理程序的 URI 模式,对于 3xx 响应回退为 REDIRECTION ,对于 404 响应回退为 NOT_FOUND ,对于没有路径信息的请求回退为 root ,对于所有其他请求回退为 UNKNOWN

表 8. 高基数键

名称描述
http.url (必需)HTTP 请求 URI.

HTTP 客户端监控

HTTP 客户端交换观察是以名称 "http.client.requests" 创建的,适用于阻塞和反应式客户端。该观察测量整个 HTTP 请求/响应交换,从连接建立到主体反序列化。与服务器端的对应物不同,仪器直接在客户端实现,因此唯一需要的步骤是在客户端配置一个 ObservationRegistry

RestTemplate

应用程序必须在 RestTemplate 实例上配置一个 ObservationRegistry 以启用仪器化;否则,观察将是“无操作”。 Spring Boot 将自动配置带有已设置观察注册表的 RestTemplateBuilder bean。

仪器使用默认的 org.springframework.http.client.observation.ClientRequestObservationConvention,由 ClientRequestObservationContext 支持。

表 9. 低基数键

名称描述
method (必需)HTTP 请求方法的名称,或者如果不是一个知名方法则为 "none"
uri (必需)用于 HTTP 请求的 URI 模板,或者如果没有提供则为 "none"。URI 的协议、主机和端口部分不被考虑。
client.name (必需)从请求 URI 主机派生的客户端名称。
status (必需)HTTP 响应原始状态码,或者在发生 IOException 时为 "IO_ERROR",如果没有收到响应则为 "CLIENT_ERROR"
outcome (必需)HTTP 客户端交换的结果。
error (必需)在交换过程中抛出的异常的类名,或者如果没有发生异常则为 "none"
exception (已弃用)重复 error 键,可能在未来被移除。

表 10. 高基数键

名称描述
http.url (必需)HTTP 请求 URI.

RestClient

应用程序必须在 RestClient.Builder 上配置一个 ObservationRegistry 以启用仪器;否则,观察将是 "无操作"。

仪器使用默认的 org.springframework.http.client.observation.ClientRequestObservationConvention,由 ClientRequestObservationContext 支持。

表 11. 低基数键

名称描述
method (必需)HTTP 请求方法的名称,或 "none" 如果请求无法创建。
uri (必需)用于 HTTP 请求的 URI 模板,或 "none" 如果没有提供。URI 的协议、主机和端口部分不被考虑。
client.name (必需)从请求 URI 主机派生的客户端名称。
status (必需)HTTP 响应原始状态码,或在 IOException 的情况下为 "IO_ERROR",如果没有收到响应则为 "CLIENT_ERROR"
outcome (必需)HTTP 客户端交换的结果。
error (必需)在交换过程中抛出的异常的类名,或如果没有发生异常则为 "none"
exception (已弃用)重复了 error 键,可能在未来被移除。

表 12. 高基数键

名称描述
http.url (必需)HTTP 请求 URI.

WebClient

应用程序必须在 WebClient.Builder 上配置一个 ObservationRegistry 以启用仪表化;否则,观察将是“无操作”。 Spring Boot 将自动配置已设置观察注册表的 WebClient.Builder bean。

仪表化默认使用 org.springframework.web.reactive.function.client.ClientRequestObservationConvention,由 ClientRequestObservationContext 支持。

表 13. 低基数键

名称描述
method (必需)HTTP 请求方法的名称,或者如果不是一个知名方法则为 "none"
uri (必需)用于 HTTP 请求的 URI 模板,如果没有提供则为 "none"。URI 的协议、主机和端口部分不被考虑。
client.name (必需)从请求 URI 主机派生的客户端名称。
status (必需)HTTP 响应的原始状态码,或者在 IOException 的情况下为 "IO_ERROR",如果没有收到响应则为 "CLIENT_ERROR"
outcome (必需)HTTP 客户端交换的结果。
error (必需)在交换过程中抛出的异常的类名,如果没有发生异常则为 "none"
exception (已弃用)重复 error 键,可能在未来被移除。

表 14. 高基数键

名称描述
http.url (必需)HTTP 请求 URI.

应用事件和 @EventListener

Spring Framework 不为 @EventListener 调用 提供 Observations,因为它们没有适合此类仪表化的语义。默认情况下,事件发布和处理是在同一线程上同步进行的。这意味着在执行该任务期间,ThreadLocals 和日志上下文将与事件发布者相同。

如果应用程序全局配置了一个自定义 ApplicationEventMulticaster,并使用一种在不同线程上调度事件处理的策略,那么情况就不再如此。所有的 @EventListener 方法将在不同的线程上处理,位于主事件发布线程之外。在这些情况下,Micrometer 上下文传播库 可以帮助传播这些值,并更好地关联事件的处理。应用程序可以配置所选择的 TaskExecutor,使用 ContextPropagatingTaskDecorator 来装饰任务并传播上下文。为了使其工作,io.micrometer:context-propagation 库必须在类路径上:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.event.SimpleApplicationEventMulticaster;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.core.task.support.ContextPropagatingTaskDecorator;

@Configuration
public class ApplicationEventsConfiguration {

@Bean(name = "applicationEventMulticaster")
public SimpleApplicationEventMulticaster simpleApplicationEventMulticaster() {
SimpleApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster();
SimpleAsyncTaskExecutor taskExecutor = new SimpleAsyncTaskExecutor();
// decorate task execution with a decorator that supports context propagation
taskExecutor.setTaskDecorator(new ContextPropagatingTaskDecorator());
eventMulticaster.setTaskExecutor(taskExecutor);
return eventMulticaster;
}

}
java

同样,如果为每个 @EventListener 注解的方法在本地做出异步选择,可以通过添加 @Async 来选择一个 TaskExecutor,该执行器通过引用其限定符来传播上下文。考虑以下配置了专用任务装饰器的 TaskExecutor bean 定义:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.SimpleAsyncTaskExecutor;
import org.springframework.core.task.TaskExecutor;
import org.springframework.core.task.support.ContextPropagatingTaskDecorator;

@Configuration
public class EventAsyncExecutionConfiguration {

@Bean(name = "propagatingContextExecutor")
public TaskExecutor propagatingContextExecutor() {
SimpleAsyncTaskExecutor taskExecutor = new SimpleAsyncTaskExecutor();
// decorate task execution with a decorator that supports context propagation
taskExecutor.setTaskDecorator(new ContextPropagatingTaskDecorator());
return taskExecutor;
}

}
java

使用 @Async 注解事件监听器以及相关的限定符将实现类似的上下文传播结果:

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

@Component
public class EmailNotificationListener {

private final Log logger = LogFactory.getLog(EmailNotificationListener.class);

@EventListener(EmailReceivedEvent.class)
@Async("propagatingContextExecutor")
public void emailReceived(EmailReceivedEvent event) {
// asynchronously process the received event
// this logging statement will contain the expected MDC entries from the propagated context
logger.info("email has been received");
}

}
java