如何异步的处理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

 

 

 

posted @ 2019-04-29 22:29  xj-record  阅读(2197)  评论(0编辑  收藏  举报