Spring Web Async异步处理#Callable #DeferredResult

Spring MVC 对于异步请求处理的两种方式

场景: Tomcat对于主线程性能瓶颈,当Tomcat请求并发数过多时,当线程数满时,就会出现请求等待Tomcat处理,这个时候可以使用子线程处理业务逻辑,主线程只是处理返回请求,这样可以大大提高Tomcat的吞吐量。

1. Callable

  1. 使用Callable返回异步信息
    - 对于前台用户来说,只是一个同步的请求,根本感觉不到后台的异步处理。
    - 后台直接返回Callable,是由Tomcat返回给前台, 而Callable数据会等待返回结果给前台。
    - 代码
	//简单的Mapping映射请求
	@GetMapping("order")
    public Callable order1 () {
        _LOGGER.info("主线程开始");
        Callable callable = () -> {
            _LOGGER.info("副线程启动");
            //模拟业务逻辑-需要一秒来处理
            TimeUnit.SECONDS.sleep(1);
            _LOGGER.info("副线程返回");
            return "success";
        };
        _LOGGER.info("主线程返回");
        return callable;
    }
	//日志输出,可以看到主线程直接返回, 而子线程等待一秒后,这时候前台返回数据
	//2018-04-11 11:27:54.584  INFO 1912 --- [nio-8010-exec-8] org.ko.web.async.AsyncController         : 主线程开始
	//2018-04-11 11:27:54.584  INFO 1912 --- [nio-8010-exec-8] org.ko.web.async.AsyncController         : 主线程返回
	//2018-04-11 11:27:54.592  INFO 1912 --- [      MvcAsync1] org.ko.web.async.AsyncController         : 副线程启动
	//2018-04-11 11:27:55.592  INFO 1912 --- [      MvcAsync1] org.ko.web.async.AsyncController         : 副线程返回
- 使用Callable 对于前台来说是和正常请求一样的,对于后台来说却可以大大增加Tomcat吞吐量。

当然有些场景, Callable并不能解决,比如说:我们访问A接口,A接口调用三方的服务,服务回调B接口,这种情况就没办法使用Callable了,这个时候可以使用DeferredResult

2. DeferredResult
2. 使用DeferredResult异步处理复杂场景,线程间数据传递
- DeferredResult: 对于用户来说,只是一个同步请求,而后台是分开两个接口。
- 同Callable一样,直接返回给前台,后续再像DeferredResult中放入值,前台直接获取数据。
- 代码

	@GetMapping("order")
    public DeferredResult order () throws InterruptedException {
        _LOGGER.info("主线程开始");
        String orderNumber = RandomStringUtils.randomNumeric(8);
        mockQueue.setPlaceOrder(orderNumber);
        DeferredResult result = new DeferredResult<>();
        deferredResultHolder.getMap().put(orderNumber, result);
        return result;
    }
- PlaceOrder中创建线程模拟双接口
public void setPlaceOrder(String placeOrder) throws InterruptedException {
        _LOGGER.info("接到下单请求");
        new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.completeOrder = placeOrder;
            _LOGGER.info("下单请求处理完毕");
        }).start();
    }
- 实现ApplicationListener模拟回调,最后返回给前台
	@Component
	public class QueueListener implements ApplicationListener{
	
	    private static final Logger _LOGGER = LoggerFactory.getLogger(QueueListener.class);
	
	    @Autowired
	    private MockQueue mockQueue;
	
	    @Autowired DeferredResultHolder deferredResultHolder;
	
	    @Override
	    public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
	        new Thread(() -> {
	            while (true) {
	                if (StringUtils.isNotBlank(mockQueue.getCompleteOrder())) {
	                    String orderNumber = mockQueue.getCompleteOrder();
	                    _LOGGER.info("返回订单处理结果: {}", orderNumber);
	                    deferredResultHolder.getMap().get(orderNumber).setResult("place order success;");
	                    mockQueue.setCompleteOrder(null);
	                } else {
	                    try {
	                        TimeUnit.SECONDS.sleep(1);
	                    } catch (InterruptedException e) {
	                        e.printStackTrace();
	                    }
	                }
	            }
	        }).start();
	    }
	}
- Holder实现
	@Component
	public class DeferredResultHolder {
	    /**
	     * 订单处理结果
	     */
	    private Map> map = new HashMap<>();
		    public Map> getMap() {
		        return map;
		    }
		
		    public void setMap(Map> map) {
		        this.map = map;
		    }
	}
- DeferredResult比较适合一些比较复杂的业务场景,提升性能。
- 有个问题,当使用分布式部署,调用链走的不是同一个实例时,DeferredResult的处理有可能会出现问题。

**3. 异步调优 **

  • 对异步处理调优的一些参数配置,Spring默认异步线程是不使用线程池的,可以自己设定一些可以重用的线程

  • 继承WebMvcConfigurerAdapter重写configureAsyncSupport()方法

	//@Configuration
	public class WebConfig extends WebMvcConfigurerAdapter {

	    @Override
	    public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
	        //注册callable拦截器
	        configurer.registerCallableInterceptors();
	        //注册deferredResult拦截器
	        configurer.registerDeferredResultInterceptors()
	        //异步请求超时时间
	        configurer.setDefaultTimeout()
	        //设定异步请求线程池callable等, spring默认线程不可重用
	        configurer.setTaskExecutor()
	    }
	}

4. 代码

  • https://github.com/Artister/tutorials-java/tree/master/security/security-web/src/main/java/org/ko/web/async

你可能感兴趣的:(J2EE与Web框架)