Spring Security入门(十二) 使用Spring MVC开发RESTful API-异步处理rest服务

一.导学
  • 使用Callable异步处理Rest服务
  • 使用DeferredResult异步处理REST服务
  • 异步处理配置
    在这里插入图片描述
  • 异步处理就是主线程使用委托副线程去处理业务,然后主线程去接纳其他的请求。提高性能
二.使用Callable异步处理Rest服务
  • 我们先来看看模拟同步处理
@RestController
public class AsyncController {


    private Logger logger = LoggerFactory.getLogger(getClass());

    @RequestMapping("/order")
    public String order() throws Exception {
         logger.info("主线程开始");
         Thread.sleep(1000);
         logger.info("主线程结束");
         return "success";
    }
}

耗时1s
在这里插入图片描述

  • 我们再来看看Callable异步的处理
@RestController
public class AsyncController {


    private Logger logger = LoggerFactory.getLogger(getClass());

    @RequestMapping("/order")
    public Callable<String> order() throws Exception {
         logger.info("主线程开始");
        Callable<String> result = new Callable<String>() {//Callable 就是单开一个线程
            @Override
            public String call() throws Exception {
                logger.info("副线程开始");
                TimeUnit.SECONDS.sleep(1);
                logger.info("副线程结束");
                return "success";
            }
        };
         logger.info("主线程结束");
         return result;
    }
}

输出

2019-05-11 21:41:57.324  INFO 22676 --- [nio-8070-exec-4] c.p.web.controller.AsyncController       : 主线程开始
2019-05-11 21:41:57.326  INFO 22676 --- [nio-8070-exec-4] c.p.web.controller.AsyncController       : 主线程结束
2019-05-11 21:41:57.358  INFO 22676 --- [      MvcAsync1] c.p.web.controller.AsyncController       : 副线程开始
2019-05-11 21:41:58.359  INFO 22676 --- [      MvcAsync1] c.p.web.controller.AsyncController       : 副线程结束
  • 主线程和副线程是两个不同的线程,没有任何停顿 ,服务器吞吐量有个很大的提升
  • 副线程处理业务逻辑时 主线程还是能够继续处理HTTP请求的
  • 实际上浏览器响应还是1s
三.DeferredResult异步处理rest服务
  • 在实际场景中可能会非常复杂,我们就以下单来举例,接收下单和真正处理下单逻辑的应用并不是一台服务器,如下图
  • 其中线程1和线程2是隔离的 互相不知道对方的存在
  • 显然Callable不能满足需要了

在这里插入图片描述

  • 业务实现逻辑如下:

    • 请求下单
    • 发送消息到消息队列中
    • 另外一个应用(假如是订单系统)处理下单,并返回处理结果
  • 一个订单号对应一个处理结果

@Component
public class DeferredResultHolder {
    private Map<String, DeferredResult<String>> map = new HashMap<>();

    public Map<String, DeferredResult<String>> getMap() {
        return map;
    }

    public void setMap(Map<String, DeferredResult<String>> map) {
        this.map = map;
    }
}
  • 消息队列
@Component
public class MockQueue {

    private Logger logger = LoggerFactory.getLogger(getClass());

    private String placeOrder;// 请求下单

    private String completeOrder;// 下单完成

    public String getPlaceOrder() {
        return placeOrder;
    }

    public void setPlaceOrder(String placeOrder) {
        new Thread(()->{
            logger.info("请求下单处理,"+placeOrder);
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.placeOrder = placeOrder;
            logger.info("下单请求处理完毕,"+placeOrder);
        }).start();
    }

    public String getCompleteOrder() {
        return completeOrder;
    }

    public void setCompleteOrder(String completeOrder) {
        this.completeOrder = completeOrder;
    }
}

  • 监听器
@Component
public class QueueLinstener implements ApplicationListener<ContextRefreshedEvent> {

    private Logger logger = LoggerFactory.getLogger(getClass());
    @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.getPlaceOrder();//获取订单号
                      logger.info("返回订单处理结果:"+orderNumber);
                      deferredResultHolder.getMap().get(orderNumber).setResult("place order success");//设置订单号处理成功
                      mockQueue.setCompleteOrder(null);//设置完成订单为空
                      deferredResultHolder.getMap().remove(orderNumber);//移除key
                  }else{
                      try {
                          TimeUnit.SECONDS.sleep(1);//等待
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                  }
              }
           }).start();
    }
}

  • 控制器类
@RestController
public class AsyncController {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private MockQueue mockQueue;
    @Autowired
    private DeferredResultHolder deferredResultHolder;

    @RequestMapping("/order")
    public DeferredResult<String> order(){
         logger.info("主线程开始");

         String orderNumber = RandomStringUtils.randomNumeric(8);

         mockQueue.setPlaceOrder(orderNumber);//设置订单号

         DeferredResult<String> deferredResult = new DeferredResult<>();


         deferredResultHolder.getMap().put(orderNumber,deferredResult);//一个订单号对应一个处理结果

         logger.info("主线程结束");

         return deferredResult;
    }
}

输出

2019-05-11 22:30:05.272  INFO 21508 --- [nio-8070-exec-4] com.playmaker.web.async.AsyncController  : 主线程开始
2019-05-11 22:30:05.276  INFO 21508 --- [nio-8070-exec-4] com.playmaker.web.async.AsyncController  : 主线程结束
2019-05-11 22:30:05.276  INFO 21508 --- [      Thread-10] com.playmaker.web.async.MockQueue        : 请求下单处理
2019-05-11 22:30:06.277  INFO 21508 --- [      Thread-10] com.playmaker.web.async.MockQueue        : 下单请求处理完毕,55996134
2019-05-11 22:30:06.566  INFO 21508 --- [       Thread-7] com.playmaker.web.async.QueueLinstener   : 返回订单处理结果:55996134
  • 可以看出一共有三个线程

    • 主线程接受http请求
    • 第2个线程处理下单逻辑
    • 第3个把第2个线程处理结果返回给前台
  • DeferredResult 在效果上实现了一个让主线程可以立即返回,但是连接不断开,可以通过DeferredResult设置返回结果来让连接返回并断开的功能

四.异步处理配置
  • 相关配置
  • 针对讲过的两种处理方式 拦截器 同步是直接add添加,而异步和同步不一样,异步请求需要单独用这两种方法注册拦截器的

在这里插入图片描述

configurer.registerCallableInterceptors(); // callable 拦截器
configurer.registerDeferredResultInterceptors(); // deferredResult拦截器
configurer.setTaskExecutor(); // 应该是自定义线程池
configurer.setDefaultTimeout() // 超时设置
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值