一:首先,来看看同步处理和异步处理有什么区别:

java rest接口接收 java请求rest接口_主线程

    那么为什么要有异步处理,从上图当中可以看出,同步处理请求进入时,主线程负责处理所有的业务逻辑结束之后,才发出HTTP响应的,我们知道,像Tomcat这种服务器能够处理的请求数量是有限的,当连接服务器的请求达到一定数量之后,Tomcat就会拒绝其他请求。而异步处理则是当HTTP请求进入之后,服务器会调用一个副线程处理具体的业务逻辑,当副线程处理结束之后,主线程再把结果返回回去,那么在副线程处理具体的业务逻辑的过程中,主线程是可以空闲出来处理其他的请求的,这样做无疑提高了服务器的效率。

二:来看看实现REST服务异步处理的几种方式

一:使用Java自带的Callable接口实现


     创建一个控制器模拟下单请求:


import com.imooc.async.DeferredResultHolder;
import com.imooc.async.MyQueue;
import org.apache.commons.lang.RandomStringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.async.DeferredResult;

import java.util.concurrent.Callable;

@RestController
public class AsyncController {

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

    @Autowired
    private MyQueue myQueue;

    @Autowired
    private DeferredResultHolder resultHolder;

    @RequestMapping("/order")
    public Callable<String> order() throws Exception {
        /*logger.info("主线程进入");
        String orderNum = RandomStringUtils.randomNumeric(8);
        myQueue.setPlaceOrder(orderNum);

        DeferredResult<String> deferredResult = new DeferredResult<>();
        resultHolder.getMap().put(orderNum, deferredResult);
        logger.info("主线程返回");
        return deferredResult;*/
        Callable<String> result = ()->
        {
                logger.info("副线程开始");
                Thread.sleep(1000);//模拟下单业务逻辑耗时
                logger.info("副线程返回");
                return "success";
        };
        logger.info("主线程返回");
        return result;
    }
}

浏览器访问请求:

2018-06-28 09:44:57.063  INFO 8068 --- [nio-8080-exec-1] com.imooc.controller.AsyncController     : 主线程进入
2018-06-28 09:44:57.063  INFO 8068 --- [nio-8080-exec-1] com.imooc.controller.AsyncController     : 主线程返回
2018-06-28 09:44:57.069  INFO 8068 --- [      MvcAsync1] com.imooc.controller.AsyncController     : 副线程开始
2018-06-28 09:44:58.070  INFO 8068 --- [      MvcAsync1] com.imooc.controller.AsyncController     : 副线程返回

可以看出主线程基本上是立即返回,副线程大概耗时1S完成具体的业务处理。

但是,此种方式实现的异步处理的副线程是由主线程主动发起的,这种情况其实有一定的局限性,某些业务场景当中主线程并不知道该由谁来处理具体的业务逻辑,下面我们举例说明:

 

java rest接口接收 java请求rest接口_spring_02

如图:一个HTTP请求进入,线程1发消息到消息中间件,而线程2则负责监听处理结果然后做响应。

    二:使用DeferredResult实现异步请求:

    1.使用一个model模拟消息队列

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

@Component
public class MyQueue {

    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 {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            this.completeOrder = placeOrder;
            logger.info("下单请求处理完毕:"+placeOrder);
        }).start();
    }

    public String getCompleteOrder() {
        return completeOrder;
    }

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

2.使用Map保存消息

import org.springframework.stereotype.Component;
import org.springframework.web.context.request.async.DeferredResult;

import java.util.HashMap;
import java.util.Map;

@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;
    }
}

3.创建消息队列的监听器监听结果

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;

@Component
public class QueueListener implements ApplicationListener<ContextRefreshedEvent>{

    @Autowired
    private MyQueue myQueue;

    @Autowired
    private DeferredResultHolder resultHolder;

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

    @Override
    public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
        new Thread(() ->{
            while (true){
                if(StringUtils.isNotBlank(myQueue.getCompleteOrder())){
                    String orderNumber = myQueue.getCompleteOrder();
                    logger.info("返回订单处理结果:"+orderNumber);
                    resultHolder.getMap().get(orderNumber).setResult("place order success");//处理完毕,结果返回

                    myQueue.setCompleteOrder(null);
                }else {
                    try {
                        Thread.sleep(100);
                    }catch (InterruptedException e){
                        e.printStackTrace();
                    }
                }
            }
        }).start();
    }
}

4.控制器实现:

import com.imooc.async.DeferredResultHolder;
import com.imooc.async.MyQueue;
import org.apache.commons.lang.RandomStringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.async.DeferredResult;

import java.util.concurrent.Callable;

@RestController
public class AsyncController {

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

    @Autowired
    private MyQueue myQueue;

    @Autowired
    private DeferredResultHolder resultHolder;

    @RequestMapping("/order")
    public DeferredResult<String> order() throws Exception {
        logger.info("主线程进入");
        String orderNum = RandomStringUtils.randomNumeric(8);
        myQueue.setPlaceOrder(orderNum);

        DeferredResult<String> deferredResult = new DeferredResult<>();
        resultHolder.getMap().put(orderNum, deferredResult);
        logger.info("主线程返回");
        return deferredResult;
        /*Callable<String> result = ()->
        {
                logger.info("副线程开始");
                Thread.sleep(1000);//模拟下单业务逻辑耗时
                logger.info("副线程返回");
                return "success";
        };
        logger.info("主线程返回");
        return result;*/
    }
}

5.浏览器访问:http://localhost:8080/order

java rest接口接收 java请求rest接口_Java_03

看看控制台:

java rest接口接收 java请求rest接口_java rest接口接收_04

三个线程完成整个请求,但是每个线程都不知道其它线程的存在。