写点什么

SpringBoot 技术专题—来看看异步处理返回方案

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

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;    }
复制代码


用户头像

我们始于迷惘,终于更高水平的迷惘。 2020.03.25 加入

🏆 【酷爱计算机技术、醉心开发编程、喜爱健身运动、热衷悬疑推理的”极客狂人“】 🏅 【Java技术领域,MySQL技术领域,APM全链路追踪技术及微服务、分布式方向的技术体系等】 🤝未来我们希望可以共同进步🤝

评论

发布
暂无评论
SpringBoot技术专题—来看看异步处理返回方案