异步线程传递MDC信息与异常捕获

 为了解决开启异步线程后,日志非常难找的问题,我们继承了Spring的AsyncConfigurer,并重写了getAsyncExecutor方法,这样在Spring中使用@Async注解开启异步线程,会自动传递MDC信息给子线程,具体代码放在文章最后.

另外关于异步线程的异常捕获,先列举一下一般开启异步的方式:

A.使用Spring的@Async注解开启异步

B.通过executor.execute开启异步

C.通过executor.submit开启异步

D.通过CompletableFuture开启异步

下面针对异步子线程的异常捕获提供几种解决方案:

1.重写AsyncConfigurer的getAsyncUncaughtExceptionHandler方法,这种方式只能捕获方式A开启的异步

2.使用Future.get(),可以捕获方式C开启的异步

3.使用Completable.join()或者Completable.get(),可以捕获方式D开启的异步

4.重写getAsyncExecutor方法时,在runnable.run()代码块上使用try/catch,可以捕获方式A,B,C开启的异步

5.使用try/catch包裹整个runnable函数式接口,这样可以捕获A,B,C,D开启的异步

    executor.execute(() -> {
            try {
                //需要开启异步的业务逻辑方法或者代码块
                xxx();
            } catch (Throwable e) {
                log.error("异常", e);
            }
        });

下面给出完整的代码

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.ThreadPoolExecutor;

@Slf4j
@EnableAsync
@Configuration
@RequiredArgsConstructor
public class ThreadPoolTaskConfig implements AsyncConfigurer {

    @Bean("XxxExecutor")
    @Override
    public ThreadPoolTaskExecutor getAsyncExecutor() {
        ThreadPoolTaskExecutor threadPoolTaskExecutor = new ThreadPoolTaskExecutor() {
            /**
             * 所有线程都会委托给这个execute方法,在这个方法中我们把父线程的MDC内容赋值给子线程
             * https://logback.qos.ch/manual/mdc.html#managedThreads
             *
             * @param runnable runnable
             */
            @Override
            public void execute(Runnable runnable) {
                // 获取父线程MDC中的内容,必须在run方法之前,否则等异步线程执行的时候有可能MDC里面的值已经被清空了,这个时候就会返回null
                Map<String, String> context = MDC.getCopyOfContextMap();
                super.execute(() -> {
                    // 将父线程的MDC内容传给子线程
                    if (context != null) {
                        MDC.setContextMap(context);
                    }
                    try {
                        // 执行异步操作
                        runnable.run();
                    } catch (Throwable e) {
                        log.info("异步线程执行异常:{}", e.getMessage(), e);
                        //替换成业务异常
                        throw new RuntimeException("异步线程执行异常");
                    } finally {
                        // 清空MDC内容
                        MDC.clear();
                    }
                });
            }

            @Override
            public <T> Future<T> submit(Callable<T> task) {
                // 获取父线程MDC中的内容,必须在run方法之前,否则等异步线程执行的时候有可能MDC里面的值已经被清空了,这个时候就会返回null
                Map<String, String> context = MDC.getCopyOfContextMap();
                return super.submit(() -> {
                    // 将父线程的MDC内容传给子线程
                    if (context != null) {
                        MDC.setContextMap(context);
                    }
                    try {
                        // 执行异步操作
                        return task.call();
                    } catch (Throwable e) {
                        log.info("异步线程执行异常:{}", e.getMessage(), e);
                        //替换成业务异常
                        throw new RuntimeException("异步线程执行异常");
                    } finally {
                        // 清空MDC内容
                        MDC.clear();
                    }
                });
            }
        };
        ;
        // 设置核心线程数
        threadPoolTaskExecutor.setCorePoolSize(30);
        // 设置最大线程数
        threadPoolTaskExecutor.setMaxPoolSize(50);
        // 设置队列容量
        threadPoolTaskExecutor.setQueueCapacity(1000);
        // 设置线程活跃时间(秒)
        threadPoolTaskExecutor.setKeepAliveSeconds(60);
        // 设置拒绝策略
        threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        // 设置线程池终止等待时间
        threadPoolTaskExecutor.setAwaitTerminationSeconds(10);

        threadPoolTaskExecutor.initialize();
        return threadPoolTaskExecutor;
    }

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return (Throwable throwable, Method method, Object... objects) -> {
            log.error("AsyncUncaughtExceptionHandler: ", throwable);
            log.info("method: {}", method.getName());
            log.info("objects: {}", objects);
        };
    }

}
  • 11
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值