版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/a2011102394/article/details/80672786
1、同步处理
-
编写测试用例
@RunWith(SpringRunner.class) @SpringBootTest public class AsyncControllerTest { @Autowired private WebApplicationContext wac; private MockMvc mockMvc; @Before public void setUp() throws Exception { mockMvc = MockMvcBuilders.webAppContextSetup(wac).build(); } @Test public void order() throws Exception { mockMvc.perform(MockMvcRequestBuilders.get("/order").contentType(MediaType.APPLICATION_JSON_UTF8)) .andExpect(MockMvcResultMatchers.status().isOk()); } }
-
同步处理代码
@RestController @RequestMapping("/order") @Slf4j public class AsyncController { @GetMapping public String order() throws InterruptedException { log.info("主线程开始"); Thread.sleep(1000); log.info("主线程结束"); return "success"; } }
-
运行结果如下:
2.使用Runnable实现异步处理
-
代码如下:
@GetMapping public Callable<String> order() throws InterruptedException { log.info("主线程开始"); Callable<String> result = new Callable<String>() { @Override public String call() throws Exception { log.info("副线程开始"); Thread.sleep(1000); log.info("副线程返回"); return "success"; } }; log.info("主线程结束"); return result; }
-
测试结果如下:
主线程几乎在同时就返回了,并不等待副线程处理结束。
3. 用DeferredResult处理异步请求
之所以要使用DeferredResult来处理异步请求,是因为Runnable的方式并不能完全处理所有的需求。
使用Runnable的方式进行异步处理的时候,副线程必须是由主线程调起的。
在实际的业务场景中,不能满足需求。
扫描二维码关注公众号,回复:
3552216 查看本文章
-
需求案例(以下单操作为例)
在上图所示的业务流程中,接收下单请求的应用和真正处理下单逻辑的应用并不在一个应用,甚至不在同一台服务器中。
线程1接收到http请求之后,会把请求放在消息队列中,应用2所在的服务器,监听到有下单的http请求之后,由应用2来处理下单的逻辑。
当下单的逻辑处理完成之后,会将结果放置在消息队列中,同时在应用1中有另外一个线程,线程2监听到消息队列中有下单完成的消息之后,根据下单的结果做出相应的http响应。
在整个的场景之中,线程1和线程2完全是隔离的状态,使用Runnable的方式就不能满足需求了。
-
编码设计
-
使用一个对象来模拟消息队列,当收到下单请求之后,延迟1秒之后,在消息队列中放置“订单完成”的消息。
-
线程1的处理:
-
线程2的处理
线程2作为一个监听器,监听到消息队列中有订单完成的消息时,把结果返回。
-
DeferredResult
在线程1和线程2之间传递DefrredResult对象,这样才可以实现在线程2中使用线程1中生成的DeferredResult将处理的结果进行返回。
-
-
编码(示例代码为了简化过程,没有使用线程池)
-
模拟消息队列对象
@Component public class MockQueue { /** * 下单消息 */ private String placeOrder; /** * 订单完成的消息 */ private String completeOrder; public String getPlaceOrder() { return placeOrder; } public void setPlaceOrder(String placeOrder) { new Thread(() -> { log.info("接到下单请求, " + placeOrder); try { Thread.sleep(1000); } catch (Exception e) { e.printStackTrace(); } this.completeOrder = placeOrder; log.info("下单请求处理完毕," + placeOrder); }).start(); } public String getCompleteOrder() { return completeOrder; } public void setCompleteOrder(String completeOrder) { this.completeOrder = completeOrder; } }
-
DeferredResultHolder对象
@Component @Getter @Setter public class DeferredResultHolder { private Map<String, DeferredResult<String>> map = new HashMap<String, DeferredResult<String>>(); }
-
监听消息队列
@Slf4j @Component public class QueueListener implements ApplicationListener<ContextRefreshedEvent> { @Autowired private MockQueue mockQueue; @Autowired private DeferredResultHolder deferredResultHolder; /** * 监听Spring容器 * @param contextRefreshedEvent */ @Override public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) { new Thread(()->{ while (true) { if (StringUtils.isNotBlank(mockQueue.getCompleteOrder())) { String orderNumber = mockQueue.getCompleteOrder(); log.info("返回订单处理结果:"+orderNumber); deferredResultHolder.getMap().get(orderNumber).setResult("place order success"); mockQueue.setCompleteOrder(null); }else{ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); } }
-
测试结果如下:
-
补充
在继承了WebMvcConfigurerAdapter类的配置类中可以注册异步拦截器,如下所示