异步并发实战
13.1 同步阻塞调用
13.2 异步Future
private void FutureTest() { //请求 Future<?> userInfoTask = POOL.submit(() -> {/**请求客户信息**/}); Future<?> orderInfoTask = POOL.submit(() -> {/**请求订单数据**/}); try { Object userInfo = userInfoTask.get(300, TimeUnit.MILLISECONDS); Object orderInfo = orderInfoTask.get(300, TimeUnit.MILLISECONDS); } catch (Exception e) { if (userInfoTask != null) { userInfoTask.cancel(true); } if (orderInfoTask != null) { orderInfoTask.cancel(true); } throw new RuntimeException(e); } }
13.3 异步Callback
13.4 异步编排CompletableFuture
CompletableFuture可以对多个异步处理进行编排,实现更复杂的异步处理。内部使用ForkJoinPool实现异步处理。ForkJoinPool内部使用双端队列实现工作密取。
13.5 异步Web服务实现
AsyncContext示例:
当Tomcat封装请求和响应传递给Servlet,Servlet通常要阻塞等待业务处理完毕才能返回。使用startAsync开启异步,自己则继续处理下一个请求,后续的处理由AsyncContext完成,并返回客户端。对于客户端来说,依然是同步等待,但是对于Tomcat来说,是异步处理请求的。
@Service public class AsyncContextTest { @Autowired private HttpServletRequest request; private static final ExecutorService POOL = Executors.newCachedThreadPool(); public void test() { //释放servlet线程让其继续接待其他请求 AsyncContext asyncContext = request.startAsync(); //先不设置超时自己控制 asyncContext.setTimeout(0); POOL.submit(() -> { try { //模拟处理过程 Thread.sleep(1000); //返回客户端 PrintWriter writer = asyncContext.getResponse().getWriter(); writer.println("handle success"); //任务执行完成,通知回调 asyncContext.complete(); } catch (Exception ignore) { // } }); } }
结合CompletableFuture
public void test2(Callable<CompletableFuture> task) throws Exception { final String uri = request.getRequestURI(); final Map<String, String[]> params = request.getParameterMap(); //释放Servlet AsyncContext asyncContext = request.startAsync(); asyncContext.getRequest().setAttribute("uri", uri); asyncContext.getRequest().setAttribute("params", params); asyncContext.setTimeout(5000); //执行任务 CompletableFuture call = task.call(); //执行完成后 call.thenAccept(result -> { //返回调用方 HttpServletResponse httpServletResponse = (HttpServletResponse) asyncContext.getResponse(); try { byte[] bytes; if (result instanceof String) { bytes = ((String) result).getBytes("GBK"); } else { bytes = JSON.toJSONBytes(result); } httpServletResponse.setContentType("text/html;charset=gbk"); httpServletResponse.setContentLength(bytes.length); httpServletResponse.getOutputStream().write(bytes); } catch (Exception e) { httpServletResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); System.out.println(e.getMessage()); } finally { asyncContext.complete(); } }).exceptionally(exception -> { //异常处理 asyncContext.complete(); return null; }); }
13.6 请求缓存
一次请求中重复调用同一个接口。譬如一次用户查询,调用多次商品详情接口。一般的解决方法是对商品详情查询接口包装一层缓存。另这一种可行的方法是使用Hystrix提供的HystrixRequestContext。
注意一下HystrixCommand
13.7 请求合并
CompletableFuture需要事先定义好多个任务,而Hystrix支持将多个单请求合并成一个批量请求。
注意一下HystrixCollapser
人生就像蒲公英,看似自由,其实身不由己。