使用Spring,我有以下设置:
我试图使用MDC (或ThreadLocal对象)为处理请求的所有组件收集上下文信息。
我可以正确地从@异步线程中检索MDC上下文信息。但是,如果@异步线程要向MDC添加上下文信息,我现在如何将MDC上下文信息封送到处理响应的线程?
TaskDecorator
public class MdcTaskDecorator implements TaskDecorator {
@Override
public Runnable decorate(Runnable runnable) {
// Web thread context
// Get the logging MDC context
Map<String, String> contextMap = MDC.getCopyOfContextMap();
return () -> {
try {
// @Async thread context
// Restore the web thread MDC context
if(contextMap != null) {
MDC.setContextMap(contextMap);
}
else {
MDC.clear();
}
// Run the new thread
runnable.run();
}
finally {
MDC.clear();
}
};
}}
异步方法
@Async
public CompletableFuture<String> doSomething_Async() {
MDC.put("doSomething", "started");
return doit();
}测井过滤器
public class ServletLoggingFilter extends AbstractRequestLoggingFilter {
@Override
protected void beforeRequest(HttpServletRequest request, String message) {
MDC.put("webthread", Thread.currentThread().getName()); // Will be webthread-1
}
@Override
protected void afterRequest(HttpServletRequest request, String message) {
MDC.put("responsethread", Thread.currentThread().getName()); // Will be webthread-2
String s = MDC.get("doSomething"); // Will be null
// logthis();
}}
发布于 2020-10-14 01:05:33
我希望你已经解决了这个问题,但如果你不解决,就有一个解决办法。
您所要做的事情可以概括为以下两个简单步骤:
MdcTaskDecorator。AsyncConfigurerSupport,并覆盖getAsyncExecutor(),用定制的装饰器设置装饰器,如下所示: public class AsyncTaskDecoratorApplication extends AsyncConfigurerSupport {
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setTaskDecorator(new MdcTaskDecorator());
executor.initialize();
return executor;
}
public static void main(String[] args) {
SpringApplication.run(AsyncTaskdecoratorApplication.class, args);
}
}发布于 2021-06-18 11:09:08
创建一个bean,将MDC属性从父线程传递给后续线程。
@Configuration
@Slf4j
public class AsyncMDCConfiguration {
@Bean
public Executor asyncExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setTaskDecorator(new MDCTaskDecorator());//MDCTaskDecorator i s a custom created class thet implements TaskDecorator that is reponsible for passing on the MDC properties
executor.initialize();
return executor;
}
}
@Slf4j
public class MDCTaskDecorator implements TaskDecorator {
@Override
public Runnable decorate(Runnable runnable) {
Map<String, String> contextMap = MDC.getCopyOfContextMap();
return () -> {
try {
MDC.setContextMap(contextMap);
runnable.run();
} finally {
MDC.clear();
}
};
}
}现在一切都好了。快乐编码
发布于 2019-03-01 08:03:20
我有一些解决方案,大致分为可调用(用于@异步)、AsyncExecutionInterceptor(用于@异步)、CallableProcessingInterceptor(用于控制器)。
1.将上下文信息放入@异步线程的可调用解决方案:
密钥是使用ContextAwarePoolExecutor替换@异步的默认执行器:
@Configuration公共类DemoExecutorConfig {
@Bean("demoExecutor")
public Executor contextAwarePoolExecutor() {
return new ContextAwarePoolExecutor();
}}
以及ContextAwarePoolExecutor覆盖提交和submitListenable方法,其中包含ContextAwareCallable:
public class ContextAwarePoolExecutor extends ThreadPoolTaskExecutor {
private static final long serialVersionUID = 667815067287186086L;
@Override
public <T> Future<T> submit(Callable<T> task) {
return super.submit(new ContextAwareCallable<T>(task, newThreadContextContainer()));
}
@Override
public <T> ListenableFuture<T> submitListenable(Callable<T> task) {
return super.submitListenable(new ContextAwareCallable<T>(task, newThreadContextContainer()));
}
/**
* set infos what we need
*/
private ThreadContextContainer newThreadContextContainer() {
ThreadContextContainer container = new ThreadContextContainer();
container.setRequestAttributes(RequestContextHolder.currentRequestAttributes());
container.setContextMapOfMDC(MDC.getCopyOfContextMap());
return container;
}}
为了方便起见,ThreadContextContainer只是存储信息的pojo:
public class ThreadContextContainer implements Serializable {
private static final long serialVersionUID = -6809291915300091330L;
private RequestAttributes requestAttributes;
private Map<String, String> contextMapOfMDC;
public RequestAttributes getRequestAttributes() {
return requestAttributes;
}
public Map<String, String> getContextMapOfMDC() {
return contextMapOfMDC;
}
public void setRequestAttributes(RequestAttributes requestAttributes) {
this.requestAttributes = requestAttributes;
}
public void setContextMapOfMDC(Map<String, String> contextMapOfMDC) {
this.contextMapOfMDC = contextMapOfMDC;
}}
ContextAwareCallable(原始任务的可调用代理)覆盖调用方法,以便在原始任务执行其调用方法之前存储MDC或其他上下文信息:
public class ContextAwareCallable<T> implements Callable<T> {
/**
* the original task
*/
private Callable<T> task;
/**
* for storing infos what we need
*/
private ThreadContextContainer threadContextContainer;
public ContextAwareCallable(Callable<T> task, ThreadContextContainer threadContextContainer) {
this.task = task;
this.threadContextContainer = threadContextContainer;
}
@Override
public T call() throws Exception {
// set infos
if (threadContextContainer != null) {
RequestAttributes requestAttributes = threadContextContainer.getRequestAttributes();
if (requestAttributes != null) {
RequestContextHolder.setRequestAttributes(requestAttributes);
}
Map<String, String> contextMapOfMDC = threadContextContainer.getContextMapOfMDC();
if (contextMapOfMDC != null) {
MDC.setContextMap(contextMapOfMDC);
}
}
try {
// execute the original task
return task.call();
} finally {
// clear infos after task completed
RequestContextHolder.resetRequestAttributes();
try {
MDC.clear();
} finally {
}
}
}}
最后,在配置好的bean "demoExecutor“中使用@异步,如下所示:@Async("demoExecutor") void yourTaskMethod();
2.关于你处理答复的问题:
遗憾地告诉你,我真的没有一个经过验证的解决方案。也许org.springframework.aop.interceptor.AsyncExecutionInterceptor#invoke可以解决这个问题。
我不认为它有一个解决方案来处理您的ServletLoggingFilter响应。因为异步方法将立即返回。afterRequest方法立即执行,并在异步方法执行操作之前返回。除非同步地等待异步方法完成执行,否则不会得到所需的东西。
但是,如果您只想记录某些内容,可以在执行原始任务之后将这些代码添加到我的示例ContextAwareCallable中:
try {
// execute the original task
return task.call();
} finally {
String something = MDC.get("doSomething"); // will not be null
// logthis(something);
// clear infos after task completed
RequestContextHolder.resetRequestAttributes();
try {
MDC.clear();
} finally {
}
}https://stackoverflow.com/questions/45890181
复制相似问题