可观察性支持
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
为例。此观察使用 ServerRequestObservationConvention
和 ServerRequestObservationContext
;可以在 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());
}
}
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());
}
}
您还可以使用自定义 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;
}
}
您可以在 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);
}
}
它默认使用 org.springframework.scheduling.support.DefaultScheduledTaskObservationConvention
,由 ScheduledTaskObservationContext
支持。您可以直接在 ObservationRegistry
上配置自定义实现。在调度方法执行期间,当前观察会在 ThreadLocal
上下文或 Reactor 上下文中恢复(如果调度方法返回 Mono
或 Flux
类型)。
默认情况下,以下 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.JmsInstrumentation
对 jakarta.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 (必需) | 目标是否为 TemporaryQueue 或 TemporaryTopic (值:"true" 或 "false" )。 |
messaging.operation (必需) | 正在执行的 JMS 操作的名称(值:"publish" 或 "process" )。 |
表 4. 高基数键
名称 | 描述 |
---|---|
messaging.message.conversation_id | JMS 消息的关联 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");
}
}
它默认使用 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;
}
}
一个 默认容器工厂是必需的,以启用注解支持,但请注意,@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 的 @ExceptionHandler
和 ProblemDetail 支持 处理的异常都不会与观察一起记录。您可以在请求处理的任何时刻,自己设置 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 {
}
}
由于仪器是在 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();
}
}
它默认使用 org.springframework.http.server.reactive.observation.DefaultServerRequestObservationConvention
,由 ServerRequestObservationContext
支持。
这将仅在 Exception
未被应用程序控制器处理时,将观察记录为错误。通常,通过 Spring WebFlux 的 @ExceptionHandler
和 ProblemDetail 支持 处理的所有异常将不会与观察一起记录。您可以在请求处理的任何时刻,自己设置 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 {
}
}
默认情况下,以下 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;
}
}
同样,如果为每个 @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;
}
}
使用 @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");
}
}