首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >用@异步和TaskDecorator记录MDC

用@异步和TaskDecorator记录MDC
EN

Stack Overflow用户
提问于 2017-08-25 22:31:47
回答 3查看 13K关注 0票数 7

使用Spring,我有以下设置:

  1. 用于记录请求的AbstractRequestLoggingFilter派生筛选器。
  2. 一个TaskDecorator,用于封送从web请求线程到@异步线程的MDC上下文映射。

我试图使用MDC (或ThreadLocal对象)为处理请求的所有组件收集上下文信息。

我可以正确地从@异步线程中检索MDC上下文信息。但是,如果@异步线程要向MDC添加上下文信息,我现在如何将MDC上下文信息封送到处理响应的线程?

TaskDecorator

代码语言:javascript
复制
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();
        }
    };
}

}

异步方法

代码语言:javascript
复制
@Async
public CompletableFuture<String> doSomething_Async() {
    MDC.put("doSomething", "started");
    return doit();
}

测井过滤器

代码语言:javascript
复制
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();
}

}

EN

回答 3

Stack Overflow用户

发布于 2020-10-14 01:05:33

我希望你已经解决了这个问题,但如果你不解决,就有一个解决办法。

您所要做的事情可以概括为以下两个简单步骤:

  1. 保持你的班级MdcTaskDecorator
  2. 为主类扩展AsyncConfigurerSupport,并覆盖getAsyncExecutor(),用定制的装饰器设置装饰器,如下所示:
代码语言:javascript
复制
    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);
        }
    }
票数 4
EN

Stack Overflow用户

发布于 2021-06-18 11:09:08

创建一个bean,将MDC属性从父线程传递给后续线程。

代码语言:javascript
复制
@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();
      }
    };
  }
}

现在一切都好了。快乐编码

票数 2
EN

Stack Overflow用户

发布于 2019-03-01 08:03:20

我有一些解决方案,大致分为可调用(用于@异步)、AsyncExecutionInterceptor(用于@异步)、CallableProcessingInterceptor(用于控制器)。

1.将上下文信息放入@异步线程的可调用解决方案:

密钥是使用ContextAwarePoolExecutor替换@异步的默认执行器:

代码语言:javascript
复制
@Configuration

公共类DemoExecutorConfig {

代码语言:javascript
复制
@Bean("demoExecutor")
public Executor contextAwarePoolExecutor() {
    return new ContextAwarePoolExecutor();
}

}

以及ContextAwarePoolExecutor覆盖提交和submitListenable方法,其中包含ContextAwareCallable:

代码语言:javascript
复制
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:

代码语言:javascript
复制
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或其他上下文信息:

代码语言:javascript
复制
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中:

代码语言:javascript
复制
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 {
        }
    }
票数 -3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/45890181

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档