SpringBoot 技术专题—来看看异步处理返回方案
发布于: 2021 年 05 月 13 日

1.异步请求和同步请求
1.1 同步请求
1.2 异步请求
可以先释放容器分配给请求的线程与相关资源,减轻系统负担,释放了容器所分配线程的请求,其响应将被延后,可以在耗时处理完成(例如长时间的运算)时再对客户端进行响应。
特点
一句话:增加了服务器对客户端请求的吞吐量(实际生产上我们用的比较少,如果并发请求量很大的情况下,我们会通过 nginx 把请求负载到集群服务的各个节点上来分摊请求压力,当然还可以通过消息队列来做请求的缓冲)。
2.异步请求的实现
Servlet 方式实现异步请求
@RequestMapping(value = "/email/servletReq", method = GET) public void servletReq (HttpServletRequest request, HttpServletResponse response) { //启动异步上下文 AsyncContext asyncContext = request.startAsync(); //设置监听器:可设置其开始、完成、异常、超时等事件的回调处理 asyncContext.addListener(new AsyncListener() { @Override public void onTimeout(AsyncEvent event) throws IOException { System.out.println("超时了..."); //做一些超时后的相关操作... } @Override public void onStartAsync(AsyncEvent event) throws IOException { System.out.println("线程开始"); } @Override public void onError(AsyncEvent event) throws IOException { System.out.println("发生错误:"+event.getThrowable()); } @Override public void onComplete(AsyncEvent event) throws IOException { System.out.println("执行完成"); //这里可以做一些清理资源的操作... } }); //设置超时时间 asyncContext.setTimeout(20000); asyncContext.start(new Runnable() { @Override public void run() { try { Thread.sleep(10000); System.out.println("内部线程:" + Thread.currentThread().getName()); asyncContext.getResponse().setCharacterEncoding("utf-8"); asyncContext.getResponse().setContentType("text/html;charset=UTF-8"); asyncContext.getResponse().getWriter().println("这是异步的请求返回"); } catch (Exception e) { System.out.println("异常:"+e); } //异步请求完成通知 //此时整个请求才完成 asyncContext.complete(); } }); //此时之类 request的线程连接已经释放了 System.out.println("主线程:" + Thread.currentThread().getName()); }复制代码
Callable
使用很简单,直接返回的参数包裹一层 callable 即可,可以继承 WebMvcConfigurerAdapter 类来设置默认线程池和超时处理
@RequestMapping(value = "/email/callableReq", method = GET) @ResponseBody public Callable<String> callableReq () { System.out.println("外部线程:" + Thread.currentThread().getName()); return new Callable<String>() { @Override public String call() throws Exception { Thread.sleep(10000); System.out.println("内部线程:" + Thread.currentThread().getName()); return "callable!"; } }; } @Configuration public class RequestAsyncPoolConfig extends WebMvcConfigurerAdapter { @Resource private ThreadPoolTaskExecutor myThreadPoolTaskExecutor; @Override public void configureAsyncSupport(final AsyncSupportConfigurer configurer) { //处理 callable超时 configurer.setDefaultTimeout(60*1000); configurer.setTaskExecutor(myThreadPoolTaskExecutor); configurer.registerCallableInterceptors(timeoutCallableProcessingInterceptor()); } @Bean public TimeoutCallableProcessingInterceptor timeoutCallableProcessingInterceptor() { return new TimeoutCallableProcessingInterceptor(); } }复制代码
WebAsyncTask
WebAsyncTask 设置一个超时回调,即可实现超时处理,本身相当于对 Callable 做了一次封装和集成。
@RequestMapping(value = "/email/webAsyncReq", method = GET) @ResponseBody public WebAsyncTask<String> webAsyncReq () { System.out.println("外部线程:" + Thread.currentThread().getName()); Callable<String> result = () -> { System.out.println("内部线程开始:" + Thread.currentThread().getName()); try { TimeUnit.SECONDS.sleep(4); } catch (Exception e) { // TODO: handle exception } logger.info("副线程返回"); System.out.println("内部线程返回:" + Thread.currentThread().getName()); return "success"; }; WebAsyncTask<String> wat = new WebAsyncTask<String>(3000L, result); wat.onTimeout(new Callable<String>() { @Override public String call() throws Exception { // TODO Auto-generated method stub return "超时"; } }); return wat; }复制代码
DeferredResult
可以处理一些相对复杂一些的业务逻辑,最主要还是可以在另一个线程里面进行业务处理及返回,即可在两个完全不相干的线程间的通信。
@RequestMapping(value = "/email/deferredResultReq", method = GET) @ResponseBody public DeferredResult<String> deferredResultReq () { System.out.println("外部线程:" + Thread.currentThread().getName()); //设置超时时间 DeferredResult<String> result = new DeferredResult<String>(60*1000L); //处理超时事件 采用委托机制 result.onTimeout(new Runnable() { @Override public void run() { System.out.println("DeferredResult超时"); result.setResult("超时了!"); } }); result.onCompletion(new Runnable() { @Override public void run() { //完成后 System.out.println("调用完成"); } }); myThreadPoolTaskExecutor.execute(new Runnable() { @Override public void run() { //处理业务逻辑 System.out.println("内部线程:" + Thread.currentThread().getName()); //返回结果 result.setResult("DeferredResult!!"); } }); return result; }复制代码
划线
评论
复制
发布于: 2021 年 05 月 13 日阅读数: 39
李浩宇/Alex
关注
我们始于迷惘,终于更高水平的迷惘。 2020.03.25 加入
🏆 【酷爱计算机技术、醉心开发编程、喜爱健身运动、热衷悬疑推理的”极客狂人“】 🏅 【Java技术领域,MySQL技术领域,APM全链路追踪技术及微服务、分布式方向的技术体系等】 🤝未来我们希望可以共同进步🤝











评论