使用Callable和DeferredResult,提高服务性能。

官方文档中说DeferredResult和Callable都是为了异步生成返回值提供基本的支持。简单来说就是一个请求进来,如果你使用了DeferredResult或者Callable,在没有得到返回数据之前,DispatcherServlet和所有Filter就会退出Servlet容器线程,但响应保持打开状态,一旦返回数据有了,这个DispatcherServlet就会被再次调用并且处理,以异步产生的方式,向请求端返回值。
这么做的好处就是请求不会长时间占用服务连接池,提高服务器的吞吐量。

Callable
Callable的实现比较简单,call()方法的返回值就是服务端返回给请求端的数据。

package com.imooc.demo.web.async;

import com.imooc.demo.web.async.two.DeferredResultHolder;
import com.imooc.demo.web.async.two.MockQueue;
import org.apache.commons.lang.RandomStringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.async.DeferredResult;

import java.util.concurrent.Callable;

/**
 * 异步处理rest服务
 */
@Controller
@RequestMapping("/async")
public class AsyncController {

    Logger logger = LoggerFactory.getLogger(AsyncController.class);

    /**
     * 使用Runnable异步处理rest服务
     * 弊端:
     *  1. 比如消息中间件. 请求 与 响应的线程并不是同一个线程.
     *  2. 可以使用deferredResult对象来解决
     * @return
     */
    @RequestMapping("")
    public Callable<String> asyncHandlerMethod() {
        logger.info("主线程执行");
        Callable<String> result = new Callable<String>() {
            @Override
            public String call() throws Exception {
                logger.info("副线程执行");
                Thread.sleep(1000);
                logger.info("副线程返回");
                return null;
            }
        };
        logger.info("主线程返回");
        return result;
        /**
         * 如下 , 两条线程
         */
        /**
         2018-07-24 16:11:18.883  INFO 2924 --- [nio-8080-exec-2] c.imooc.demo.web.async.AsyncController   : 主线程执行
         2018-07-24 16:11:18.884  INFO 2924 --- [nio-8080-exec-2] c.imooc.demo.web.async.AsyncController   : 主线程返回
         2018-07-24 16:11:18.890  INFO 2924 --- [      MvcAsync1] c.imooc.demo.web.async.AsyncController   : 副线程执行
         2018-07-24 16:11:19.891  INFO 2924 --- [      MvcAsync1] c.imooc.demo.web.async.AsyncController   : 副线程返回
         */
    }
}

DeferredResult

一旦启用了异步请求处理功能 ,控制器就可以将返回值包装在DeferredResult,控制器可以从不同的线程异步产生返回值。优点就是可以实现两个完全不相干的线程间的通信。

# 业务逻辑

1. 此处的业务 , 是模拟如下业务流程
    1. 用户发起http请求
    2. 应用1
    3. 发消息给消息队列
    4. 应用2监听并处理消息
    5. 应用2返回处理结果并写入消息队列
    6. 应用1监听消息队列的处理结果
    7. 应用1返回http响应
    8. 完毕
2. 业务通过 DeferredResult对象来实现异步调用.
3. 代码执行顺序
    1. http://localhost:8080/async/order  -> AsyncController  (下单,并将其加入deferredResult)
    2. QueueListener监听器监听spring初始化完毕事件. 监听MockQueue对象的下单标识.
    3. MockQueue -> 标识消息中间件

如图:
这里写图片描述

  1. 模拟消息中间件的类
package com.imooc.demo.web.async.two;
import com.imooc.demo.web.async.AsyncController;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
@Component
public class MockQueue {
    Logger logger = LoggerFactory.getLogger(MockQueue.class);
    private String placeOrder; //下单
    private String completeOrder; //完成
    public String getPlaceOrder() {
        return placeOrder;
    }
    public void setPlaceOrder(String placeOrder) throws InterruptedException {
        // 此处按照逻辑 , 也应该是单独一个线程来处理实现的
        new Thread(() -> {
            logger.info("接到下单请求, {}",placeOrder);
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //this.placeOrder = placeOrder;
            this.completeOrder = placeOrder;
            logger.info("下单请求完毕, {}",placeOrder);
        }).start();
    }
    public String getCompleteOrder() {
        return completeOrder;
    }
    public void setCompleteOrder(String completeOrder) {
        this.completeOrder = completeOrder;
    }
}
  1. 应用1操作类
package com.imooc.demo.web.async;
import com.imooc.demo.web.async.two.DeferredResultHolder;
import com.imooc.demo.web.async.two.MockQueue;
import org.apache.commons.lang.RandomStringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.async.DeferredResult;
import java.util.concurrent.Callable;
/**
 * 异步处理rest服务
 */
@Controller
@RequestMapping("/async")
public class AsyncController {
    Logger logger = LoggerFactory.getLogger(AsyncController.class);
    /**
     * 第二种方式: -> 下单
     */
    @Autowired
    private MockQueue mockQueue;
    @Autowired
    private DeferredResultHolder deferredResultHolder;

    @RequestMapping("/order")
    @ResponseBody
    public DeferredResult<String> order() throws InterruptedException {
        logger.info("主线程执行");
        // 生成订单号
        String orderNumber = RandomStringUtils.randomNumeric(8);
        // 返回result
        mockQueue.setPlaceOrder(orderNumber);
        DeferredResult<String> result = new DeferredResult<>();
        deferredResultHolder.getMap().put(orderNumber,result);
        logger.info("主线程返回");
        return result;
        // 然后编写监听器.
    }
}
  1. spring监听器实现类模拟应用1监听
package com.imooc.demo.web.async.two;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;

/**
 * spring监听器 -> 泛型类型(ContextRefreshedEvent) -> spring初始化完毕事件
 */
@Component
public class QueueListener implements ApplicationListener<ContextRefreshedEvent>{
    Logger logger = LoggerFactory.getLogger(QueueListener.class);
    @Autowired
    private MockQueue mockQueue;
    @Autowired
    private 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);
                    logger.info("下单请求处理完毕," + orderNumber);
                }else{
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }
}

猜你喜欢

转载自blog.csdn.net/qq_34531925/article/details/81189452