zoukankan      html  css  js  c++  java
  • 如何异步的处理restful服务(基础)

    1、使用Runnable

    2、使用DeferredResult

    3、异步处理的一些配置

    正常请求方式

    package com.nxz.controller;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.util.concurrent.Callable;
    
    /**
     * 异步处理的controller
     */
    @RestController
    @Slf4j
    public class AsyncController {
    
        //标准的同步处理逻辑
        @RequestMapping("/order")
        public String order() throws InterruptedException {
            log.info("主线城开始");
            Thread.sleep(1000);//具体的业务逻辑
            log.info("主线程返回");
            return "success";
        }
    
    }

    1、通过callable异步方式

    package com.nxz.controller;
    
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RestController;
    
    import java.util.concurrent.Callable;
    
    /**
     * 异步处理的controller
     */
    @RestController
    @Slf4j
    public class AsyncController {
    
        //异步处理方式
        @RequestMapping("/order1")
        public Callable<String> order1() throws InterruptedException {
            log.info("主线城开始");
            Callable<String> callable = new Callable<String>() {
    
                @Override
                public String call() throws Exception {
                    log.info("副线程线程开始 callable.call()");
                    Thread.sleep(1000);//具体的业务逻辑
                    log.info("副线程线程结束 callable.call()");
                    return "success";
                }
            };
    
            log.info("主线程返回");
            return callable;
        }
    
    
    }

    访问order1后日志输出:日志表明主线程返回就代表请求已经结束,但是具体的数据信息是在副线程结束时

    才返回的(也就是在主线程结束后tomcat等中间件是可以接受其他http请求,增大了吞吐量)

    2019-04-29 20:28:32.433  INFO 16788 --- [nio-8080-exec-4] com.nxz.controller.AsyncController       : 主线城开始
    2019-04-29 20:28:32.434  INFO 16788 --- [nio-8080-exec-4] com.nxz.controller.AsyncController       : 主线程返回
    2019-04-29 20:28:32.434  INFO 16788 --- [      MvcAsync2] com.nxz.controller.AsyncController       : 副线程线程开始 callable.call()
    2019-04-29 20:28:33.435  INFO 16788 --- [      MvcAsync2] com.nxz.controller.AsyncController       : 副线程线程结束 callable.call()

    浏览器显示结果时间  

    2、DeferredResult形式

    runnable形式的缺点:副线程的发起必须是在主线程下,但是企业级开发时,是比较复杂的。

    DeferredResult是在应用1接受http请求后,由线程1将请求放到消息队列中,然后又另一台服务器具体启用副线程执行逻辑,处理完成之后由线程二监听消息队列,接受返回数据,返回给前端

    controller:

        @Autowired
        private MockQueue mockQueue;
    
        @Autowired
        private DeferredResultHolder deferredResultHolder;
    
        //在主线程中是看不到副线程的任何东西的
        @RequestMapping("/order2")
        public DeferredResult<String> order2() throws InterruptedException {
            log.info("主线程开始");
            String orderNum = RandomStringUtils.randomNumeric(8);//模拟订单号
            mockQueue.setPlaceOrder(orderNum);//模拟消息队列(将订单号放到消息队里中)
    
            DeferredResult<String> result = new DeferredResult<>();
            deferredResultHolder.getMap().put(orderNum, result);
            log.info("主线程结束");
            return result;
        }

    模拟队列:

    package com.nxz.async;
    
    import lombok.Getter;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.stereotype.Component;
    
    /**
     * 模拟队列的对象
     */
    
    @Getter
    @Component
    @Slf4j
    public class MockQueue {
    
        //代表接受的信息
        private String placeOrder;
    
        //代表返回的消息
        private String complateOrder;
    
    
        //set 方法模拟往消息队列中放消息
        public void setPlaceOrder(String placeOrder) throws InterruptedException {
            new Thread(() -> {
                log.info("接到请求消息" + placeOrder);
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                this.complateOrder = placeOrder;
                log.info("接到请求消息处理完成" + placeOrder);
            }).start();
        }
    
        public void setComplateOrder(String complateOrder) {
            this.complateOrder = complateOrder;
        }
    }

    异步监听处理结果:

    package com.nxz.async;
    
    import lombok.extern.slf4j.Slf4j;
    import org.apache.commons.lang.StringUtils;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.ApplicationListener;
    import org.springframework.context.event.ContextRefreshedEvent;
    import org.springframework.stereotype.Component;
    
    //监听器
    @Component
    @Slf4j
    public class QueueListener implements ApplicationListener<ContextRefreshedEvent> {
    
        @Autowired
        private MockQueue mockQueue;
    
        @Autowired
        private DeferredResultHolder deferredResultHolder;
    
    
        /**
         * ContextRefreshedEvent这个事件是spring初始化完毕的一个事件
         * 监听这个事件就是为了 在系统启动完毕后要做什么事
         *
         * @param contextRefreshedEvent
         */
        @Override
        public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
            log.info("接受返回结果的listener");
            new Thread(() -> {
                //将以下while循环放到一个单开的thred线程  防止主线程死循环
                //监听mockqueue中的complateOrder
                while (true) {
                    if (StringUtils.isNotBlank(mockQueue.getComplateOrder())) {
                        String orderNum = mockQueue.getComplateOrder();
                        //返回订单处理结果
                        log.info("返回订单处理结果" + orderNum);
                        deferredResultHolder.getMap().get(orderNum).setResult("place order success");
                        mockQueue.setComplateOrder(null);//表名任务已经处理完了
                    } else {
                        //complateorder中没有值是睡眠100毫秒
                        try {
                            log.info("没有任务休眠100毫秒");
                            Thread.sleep(3000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }).start();
    
        }
    }

     发送一个请求后,日志输出:

    2019-04-29 21:27:18.959  INFO 7176 --- [nio-8080-exec-3] com.nxz.controller.AsyncController       : 主线程开始
    2019-04-29 21:27:18.960  INFO 7176 --- [nio-8080-exec-3] com.nxz.controller.AsyncController       : 主线程结束
    2019-04-29 21:27:18.960  INFO 7176 --- [      Thread-43] com.nxz.async.MockQueue                  : 接到请求消息76311604
    2019-04-29 21:27:19.961  INFO 7176 --- [      Thread-43] com.nxz.async.MockQueue                  : 接到请求消息处理完成76311604
    2019-04-29 21:27:21.242  INFO 7176 --- [      Thread-30] com.nxz.async.QueueListener              : 返回订单处理结果76311604

  • 相关阅读:
    忆2011年的秋天:一个人的项目
    横看成岭侧成峰,远近高低各不同——从面试官的角度谈面试
    使用Scratch进行少儿编程
    初识少儿编程
    升级openssl
    CentOS设置虚拟网卡做NAT方式和Bridge方式桥接
    iptables conntrack有什么用
    nohup和&的区别
    Linux就这个范儿 第12章 一个网络一个世界
    一个由INode节点爆满引起的业务故障
  • 原文地址:https://www.cnblogs.com/nxzblogs/p/10793057.html
Copyright © 2011-2022 走看看