zoukankan      html  css  js  c++  java
  • springboot2.0 如何异步操作,@Async失效,无法进入异步

    springboot异步操作可以使用@EnableAsync@Async两个注解,本质就是多线程和动态代理。

    一、配置一个线程池

    @Configuration
    @EnableAsync//开启异步
    public class ThreadPoolConfig {
        @Bean("logThread")
        public TaskExecutor taskExecutor() {
            ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
            // 设置核心线程数
            executor.setCorePoolSize(4);
            // 设置最大线程数
            executor.setMaxPoolSize(8);
            // 设置队列容量
            executor.setQueueCapacity(100);
            // 设置线程活跃时间(秒)
            executor.setKeepAliveSeconds(60);
            // 设置默认线程名称
            executor.setThreadNamePrefix("home.bus.logThread-");
            // 设置拒绝策略
            executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
            // 等待所有任务结束后再关闭线程池
            executor.setWaitForTasksToCompleteOnShutdown(true);
            return executor;
        }
    }

    二、异步操作

    比如有一个日志服务需要异步入库

    @Service
    public class LogServiceImpl implements LogService {
    
        @Resource
        SysLogRepository sysLogRepository;
    
        private Logger logger = LoggerFactory.getLogger(LogServiceImpl.class);
    
        @Override
        @Async("logThread")//对应线程池里的bean
        public void writeLog(SysLog sysLog)
        {
            long start = System.currentTimeMillis();
            try {
                Thread.sleep(3000);//为了测试加入,绝对不是为了以后给客户优化性能加入
            }catch (Exception e)
            {
                e.printStackTrace();
            }
            sysLogRepository.save(sysLog);
            long end = System.currentTimeMillis();
            logger.info("异步日志入库完成,耗时:"+(end-start)+"毫秒,入库内容:"+sysLog);
        }
       
    }

    这里有一个小坑,writeLog函数不能由本类内其他函数调用,必须是外部使用者调用,如果内部函数调用会出现代理绕过的问题,从而无法执行异步,不会出错,会变成同步操作。看起来就是@Async失效的状态。

    例如:

    @Service
    public class LogServiceImpl implements LogService {
    
        @Resource
        SysLogRepository sysLogRepository;
    
        private Logger logger = LoggerFactory.getLogger(LogServiceImpl.class);
    
        @Override
        @Async("logThread")//对应线程池里的bean
        public void writeLog(SysLog sysLog)
        {
            long start = System.currentTimeMillis();
            try {
                Thread.sleep(3000);
            }catch (Exception e)
            {
                e.printStackTrace();
            }
            sysLogRepository.save(sysLog);
            long end = System.currentTimeMillis();
            logger.info("异步日志入库完成,耗时:"+(end-start)+"毫秒,入库内容:"+sysLog);
        }
    
     public void doSysLog(String action,String event)
        {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            SysLog sysLog = new SysLog();
            sysLog.setAction(action);
            sysLog.setEvent(event);
            sysLog.setHost(NetworkUtils.getIpAddress(request));
            sysLog.setUserName((String)request.getSession().getAttribute("userName"));
            sysLog.setInsertTime(LocalDateTime.now());
    
            wirteLog(sysLog);//这里不会进入异步
        }
       
    }

    使用doSyslog调用异步函数wirteLog,最终会是一个同步方法。为什么不直接在doSysLog函数加上异步注解?因为RequestContextHolder在异步里取不到信息。

    HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();

    三、调用异步

    比如登录controller,登录成功后调用异步日志入库

            LoginResult loginResult = loginService.login(userName, password);
            if (loginResult.isLogin()) {
                map.put("userName", userName);
                SysLog sysLog = LogFactory.createSysLog("登录","登录成功");
                logService.writeLog(sysLog);//这里异步,完全阻塞,如果之前函数内嵌套调用,这里就阻塞了,把sleep设置大一些可以看得明显
                return "/index";
            } else {
                map.put("msg", loginResult.getResult());
                map.put("userName", userName);
                return "/user/login";
            }

    这里SysLog 对象直接在调用层生成,也就是把doSysLog拆分成两个部分处理,logService直接调用异步方法,正常情况不会阻塞,直接就到下一步。

    结果:

    [home.bus.logThread-1] INFO  c.h.bus.service.impl.LogServiceImpl - 异步日志入库完成,耗时:3089毫秒,入库内容:SysLog{logId=367, userName='admin', host='0:0:0:0:0:0:0:1', action='登录', event='登录成功', insertTime=2018-11-16T00:18:32.522} 
  • 相关阅读:
    16条很有用的Chrome浏览器命令
    通用测试用例
    Vue中@click、@click.stop和@click.prevet用法
    Vue事件的函数传参
    Vue事件的基本用法
    vue中v-on和v-bind的区别
    Vue中数据的双向绑定方法
    v-once用法
    v-clock、v-text、v-html和v-pre详解
    IDEA给已有项目添加maven依赖
  • 原文地址:https://www.cnblogs.com/asker009/p/9967053.html
Copyright © 2011-2022 走看看