大部分程序员不知道的 Servelt3 异步请求,原来这么简单?阿粉带你全面扫盲!(上)

简介: 当一个 HTTP 请求到达 Tomcat,Tomcat 将会从线程池中取出线程,然后按照如下流程处理请求:将请求信息解析为 HttpServletRequest分发到具体 Servlet 处理相应的业务通过 HttpServletResponse 将响应结果返回给等待客户端

前言

当一个 HTTP 请求到达 Tomcat,Tomcat 将会从线程池中取出线程,然后按照如下流程处理请求:

  • 将请求信息解析为 HttpServletRequest
  • 分发到具体 Servlet 处理相应的业务
  • 通过 HttpServletResponse 将响应结果返回给等待客户端

整体流程如下所示:

这是我们日常最常用同步请求模型,所有动作都交给同一个 Tomcat 线程处理,所有动作处理完成,线程才会被释放回线程池。

想象一下如果业务需要较长时间处理,那么这个 Tomcat 线程其实一直在被占用,随着请求越来越多,可用 I/O 线程越来越少,直到被耗尽。这时后续请求只能等待空闲 Tomcat 线程,这将会加长了请求执行时间。

如果客户端不关心返回业务结果,这时我们可以自定义线程池,将请求任务提交给线程池,然后立刻返回。

也可以使用 Spring Async 任务,大家感兴趣可以自行查找一下资料

但是很多场景下,客户端需要处理返回结果,我们没办法使用上面的方案。在 Servlet2 时代,我们没办法优化上面的方案。

不过等到 Servlet3 ,引入异步 Servlet 新特性,可以完美解决上面的需求。

异步 Servlet 执行请求流程:

  • 将请求信息解析为 HttpServletRequest
  • 分发到具体 Servlet 处理,将业务提交给自定义业务线程池,请求立刻返回,Tomcat 线程立刻被释放
  • 当业务线程将任务执行结束,将会将结果转交给 Tomcat 线程
  • 通过 HttpServletResponse 将响应结果返回给等待客户端

引入异步 Servlet3 整体流程如下:

25.jpg

使用异步 Servelt,Tomcat 线程仅仅处理请求解析动作,所有耗时较长的业务操作全部交给业务线程池,所以相比同步请求, Tomcat 线程可以处理 更多请求。

虽然我们将业务处理交给业务线程池异步处理,但是对于客户端来讲,其还在同步等待响应结果

可能有些同学会觉得异步请求将会获得更快响应时间,其实不是的,相反可能由于引入了更多线程,增加线程上下文切换时间。

虽然没有降低响应时间,但是通过请求异步化带来其他明显优点

  • 可以处理更高并发连接数,提高系统整体吞吐量
  • 请求解析与业务处理完全分离,职责单一
  • 自定义业务线程池,我们可以更容易对其监控,降级等处理
  • 可以根据不同业务,自定义不同线程池,相互隔离,不用互相影响

所以具体使用过程,我们还需要进行的相应的压测,观察响应时间以及吞吐量等其他指标,综合选择。

异步 Servelt 使用方式

异步 Servelt 使用方式不是很难,阿粉总结就是下面三板斧:

  1. HttpServletRequest#startAsync 获取 AsyncContext 异步上下文对象
  2. 使用自定义的业务线程池处理业务逻辑
  3. 业务线程处理结束,通过 AsyncContext#complete 返回响应结果

下面的例子将会使用 SpringBoot ,Web 容器选择 Tomcat

示例代码如下:

ExecutorService executorService = Executors.newFixedThreadPool(10);
@RequestMapping("/hello")
public void hello(HttpServletRequest request) {
    AsyncContext asyncContext = request.startAsync();
    // 超时时间
    asyncContext.setTimeout(10000);
    executorService.submit(() -> {
        try {
            // 休眠 5s,模拟业务操作
            TimeUnit.SECONDS.sleep(5);
            // 输出响应结果
            asyncContext.getResponse().getWriter().println("hello world");
            log.info("异步线程处理结束");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            asyncContext.complete();
        }
    });
    log.info("servlet 线程处理结束");
}

浏览器访问该请求将会同步等待 5s 得到输出响应,应用日志输出结果如下:

2020-03-24 07:27:08.997  INFO 79257 --- [nio-8087-exec-4] com.xxxx   : servlet 线程处理结束
2020-03-24 07:27:13.998  INFO 79257 --- [pool-1-thread-3] com.xxxx   : 异步线程处理结束

这里我们需要注意设置合理的超时时间,防止客户端长时间等待。


相关文章
|
8天前
|
XML 存储 前端开发
前端网络请求真的搞懂了吗?解密前端参数传递方式,让开发更从容(三)
前端网络请求真的搞懂了吗?解密前端参数传递方式,让开发更从容
|
8天前
|
XML JSON 前端开发
前端网络请求真的搞懂了吗?解密前端参数传递方式,让开发更从容(二)
前端网络请求真的搞懂了吗?解密前端参数传递方式,让开发更从容
|
8天前
|
缓存 前端开发 安全
究竟何为GET,何为POST?前端程序员的必修课
究竟何为GET,何为POST?前端程序员的必修课
63 0
|
8天前
|
XML 前端开发 JavaScript
前端网络请求真的搞懂了吗?解密前端参数传递方式,让开发更从容(一)
前端网络请求真的搞懂了吗?解密前端参数传递方式,让开发更从容
|
8天前
|
存储 前端开发 Java
Web开发:解释一下MVC架构。
Web开发:解释一下MVC架构。
60 0
|
Java API 定位技术
Java后台专业术语
OOD(Object Oriented Design):面向对象设计 OOA(Object Oriented Analysis):面向对象分析
88 0
|
缓存 前端开发 API
异步的发展,顺手学会怎么处理多请求
异步的发展,顺手学会怎么处理多请求
95 0
|
XML Java 数据库
面试项目说实现了一个后端多线程网络服务器框架应该怎样写
面试项目说实现了一个后端多线程网络服务器框架应该怎样写
面试项目说实现了一个后端多线程网络服务器框架应该怎样写
|
存储 缓存 JavaScript
想好怎么学 Servlet规范了嘛?想好了嘛?没想好先看看这篇文章(爆肝之作),先看着然后慢慢想!!
Servlet(Server Applet)是Java Servlet的简称,称为小服务程序或服务连接器,用Java编写的服务器端程序,具有独立于平台和协议的特性,主要功能在于交互式地浏览和生成数据,生成动态Web内容。 狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的类,一般情况下,人们将Servlet理解为后者。Servlet运行于支持Java的应用服务器中。从原理上讲,Servlet可以响应任何类型的请求,但绝大多数情况下Servlet只用来扩展基于HTTP协议的Web服务器。
想好怎么学 Servlet规范了嘛?想好了嘛?没想好先看看这篇文章(爆肝之作),先看着然后慢慢想!!
|
设计模式 应用服务中间件 API
测试小姐姐问我 gRPC 怎么用,我直接把这篇文章甩给了她
测试小姐姐问我 gRPC 怎么用,我直接把这篇文章甩给了她
243 0