• 记一次springboot @Async处理导致后续request请求参数获取为空的坑


    背景

    近期有个导入excel功能的需求,要求是异步处理导入。于是配置了线程池,使用@Async 异步执行导入方法。做完后发现一个现象,在导入后的接下来的一次请求,接口调用异常。经调试发现后端request未获取到参数(parameterMap大小为0),而实际前端有传值,再次调用同接口后正常。

    原因

    异步线程使用了request。而request是有生命周期的、复用的,异步线程未处理request的情况下,会被回收复用,导致下一次使用解析不出参数。

    解决方法

    直接上代码,线程池配置。
    关键在于:

    HttpServletRequest request = attributes.getRequest();
    AsyncContext  asyncContext = request.startAsync();
    // 执行任务......
    asyncContext .complete();
    
    • 1
    • 2
    • 3
    • 4

    AsyncContext complete前request是不会被回收的。

    import lombok.Data;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.core.task.TaskDecorator;
    import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
    import org.springframework.web.context.request.RequestContextHolder;
    import org.springframework.web.context.request.ServletRequestAttributes;
    
    import javax.servlet.AsyncContext;
    import javax.servlet.http.HttpServletRequest;
    import java.util.concurrent.Executor;
    import java.util.concurrent.ThreadPoolExecutor;
    
    @Slf4j
    @Data
    @Configuration
    public class ThreadPoolConfig {
        /**
         * 核心线程
         */
        @Value("${jeecg.task.execution.pool.core-size}")
        private int corePoolSize;
        /**
         * 最大线程
         */
        @Value("${jeecg.task.execution.pool.max-size}")
        private int maxPoolSize;
        /**
         * 队列容量
         */
        @Value("${jeecg.task.execution.pool.queue-capacity}")
        private int queueCapacity;
        /**
         * 保持时间
         */
        @Value("${jeecg.task.execution.pool.keep-alive}")
        private int keepAliveSeconds;
        /**
         * 名称前缀
         */
        @Value("${jeecg.task.execution.pool.thread-name-prefix}")
        private String preFix;
    
        @Bean("asyncServiceExecutor")
        public Executor asyncServiceExecutor() {
            log.info("init asyncServiceExecutor");
            ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
            executor.setCorePoolSize(corePoolSize);
            executor.setMaxPoolSize(maxPoolSize);
            executor.setQueueCapacity(queueCapacity);
            executor.setKeepAliveSeconds(keepAliveSeconds);
            executor.setThreadNamePrefix(preFix);
            executor.setRejectedExecutionHandler( new ThreadPoolExecutor.AbortPolicy());
            executor.setTaskDecorator(new CustomTaskDecorator());
            executor.initialize();
            log.info("asyncServiceExecutor params----corePoolSize:{},maxPoolSize:{},queueCapacity:{},keepAliveSeconds:{},preFix:{}" , corePoolSize,maxPoolSize,queueCapacity,keepAliveSeconds,preFix);
            return executor;
        }
    
    
        /**
         * 线程池修饰类
         */
        class CustomTaskDecorator implements TaskDecorator {
            @Override
            public Runnable decorate(Runnable runnable) {
                ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
                AsyncContext asyncContext = null;
                if (attributes!=null) {
                    HttpServletRequest request = attributes.getRequest();
                    asyncContext = request.startAsync();
                }
                AsyncContext finalAsyncContext = asyncContext;
                return () -> {
                    try {
                        RequestContextHolder.setRequestAttributes(attributes,true);
                        runnable.run();
                    } finally {
                        RequestContextHolder.resetRequestAttributes();
                        if (finalAsyncContext !=null) {
                            finalAsyncContext.complete();
                        }
                    }
                };
            }
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
  • 相关阅读:
    研发过程中的文档管理与工具
    【十四】记一次MySQL宕机恢复过程,MySQL INNODB 损坏恢复
    LeetCode题练习与总结:统计词频--192
    Linux进程间通信
    jmeter压测
    C++演变历史
    hutool导入导出多sheet页的Excel
    EOS的共识机制与区块生成
    CPU、MPU、MCU、SOC的理解
    基于JAVA学校意见征集系统计算机毕业设计源码+系统+mysql数据库+lw文档+部署
  • 原文地址:https://blog.csdn.net/qq_34453866/article/details/127654427