Java Servlet3.0异步处理问题
作者:laozhang 发布时间:2023-08-12 00:52:05
通过本篇文章主要给大家讲解了在JAVA开发中Servlet3.0异步处理遇到的问题以及处理办法,以下是具体内容:
Servlet 3.0 开始提供了AsyncContext用来支持异步处理请求,那么异步处理请求到底能够带来哪些好处?
Web容器一般来说处理请求的方式是:为每个request分配一个thread。我们都知道thread的创建不是没有代价的,Web容器的thread pool都是有上限的。
那么一个很容易预见的问题就是,在高负载情况下,thread pool都被占着了,那么后续的request就只能等待,如果运气不好客户端会报等待超时的错误。
在AsyncContext出现之前,解决这个问题的唯一办法就是扩充Web容器的thread pool。
但是这样依然有一个问题,考虑以下场景:
有一个web容器,线程池大小200。有一个web app,它有两个servlet,Servlet-A处理单个请求的时间是10s,Servlet-B处理单个请求的时间是1s。
现在遇到了高负载,有超过200个request到Servlet-A,如果这个时候请求Servlet-B就会等待,因为所有HTTP thread都已经被Servlet-A占用了。
这个时候工程师发现了问题,扩展了线程池大小到400,但是负载依然持续走高,现在有400个request到Servlet-A,Servlet-B依然无法响应。
看到问题了没有,因为HTTP thread和Worker thread耦合在了一起,所以导致了当大量request到一个耗时操作时,就会将HTTP thread占满,导致整个Web容器就会无法响应。
但是如果使用AsyncContext,我们就可以将耗时的操作交给另一个thread去做,这样HTTP thread就被释放出来了,可以去处理其他请求了。
注意,只有使用AsyncContext才能够达到上面所讲的效果,如果直接new Thread()或者类似的方式的,HTTP thread并不会归还到容器。
下面是一个官方的例子:
@WebServlet(urlPatterns={"/asyncservlet"}, asyncSupported=true)
public class AsyncServlet extends HttpServlet {
/* ... Same variables and init method as in SyncServlet ... */
@Override
public void doGet(HttpServletRequest request,
HttpServletResponse response) {
response.setContentType("text/html;charset=UTF-8");
final AsyncContext acontext = request.startAsync();
acontext.start(new Runnable() {
public void run() {
String param = acontext.getRequest().getParameter("param");
String result = resource.process(param);
HttpServletResponse response = acontext.getResponse();
/* ... print to the response ... */
acontext.complete();
}
});
}
}
陷阱
在这个官方例子里,每个HTTP thread都会开启另一个Worker thread来处理请求,然后把HTTP thread就归还给Web容器。但是看AsyncContext.start()方法的javadoc:
Causes the container to dispatch a thread, possibly from a managed thread pool, to run the specified Runnable.
实际上这里并没有规定Worker thread到底从哪里来,也许是HTTP thread pool之外的另一个thread pool?还是说就是HTTP thread pool?
The Limited Usefulness of AsyncContext.start()文章里写道:不同的Web容器对此有不同的实现,不过Tomcat实际上是利用HTTP thread pool来处理AsyncContext.start()的。
这也就是说,我们原本是想释放HTTP thread的,但实际上并没有,因为有HTTP thread依然被用作Worker thread,只不过这个thread和接收请求的HTTP thread不是同一个而已。
这个结论我们也可以通过AsyncServlet1和SyncServlet的Jmeter benchmark看出来,两者的throughput结果差不多。启动方法:启动Main,然后利用Jmeter启动benchmark.jmx(Tomcat默认配置下HTTP thread pool=200)。
使用ExecutorService
前面看到了Tomcat并没有单独维护Worker thread pool,那么我们就得自己想办法搞一个,见AsyncServlet2,它使用了一个带Thread pool的ExecutorService来处理AsyncContext。
其他方式
所以对于AsyncContext的使用并没有固定的方式,你可以根据实际需要去采用不同的方式来处理,为此你需要一点Java concurrent programming的知识。
对于性能的误解
AsyncContext的目的并不是为了提高性能,也并不直接提供性能提升,它提供了把HTTP thread和Worker thread解藕的机制,从而提高Web容器的响应能力。
不过AsyncContext在某些时候的确能够提高性能,但这个取决于你的代码是怎么写的。
比如:Web容器的HTTP thread pool数量200,某个Servlet使用一个300的Worker thread pool来处理AsyncContext。
相比Sync方式Worker thread pool=HTTP thread pool=200,在这种情况下我们有了300的Worker thread pool,所以肯定能够带来一些性能上的提升(毕竟干活的人多了)。
相反,如果当Worker thread的数量<=HTTP thread数量的时候,那么就不会得到性能提升,因为此时处理请求的瓶颈在Worker thread。
你可以修改AsyncServlet2的线程池大小,把它和SyncServlet比较benchmark结果来验证这一结论。
一定不要认为Worker thread pool必须比HTTP thread pool大,理由如下:
两者职责不同,一个是Web容器用来接收外来请求,一个是处理业务逻辑
thread的创建是有代价的,如果HTTP thread pool已经很大了再搞一个更大的Worker thread pool反而会造成过多的Context switch和内存开销
AsyncContext的目的是将HTTP thread释放出来,避免 * 作长期占用进而导致Web容器无法响应
所以在更多时候,Worker thread pool不会很大,而且会根据不同业务构建不同的Worker thread pool。
比如:Web容器thread pool大小200,一个慢速Servlet的Worker thread pool大小10,这样一来,无论有多少请求到慢速操作,它都不会将HTTP thread占满导致其他请求无法处理。
来源:https://segmentfault.com/a/1190000012318350
猜你喜欢
- 一、SpringCache介绍Spring Cache 是一个优秀的缓存组件。自Spring 3.1起,提供了类似于@Transaction
- 在开发Android应用程序中,经常会自定义View来实现各种各样炫酷的效果,在实现这吊炸天效果的同时,我们往往会定义很多attr属性,这样
- 1.最常用的方法是创建一个计数器,判断是否遇到‘\0',不是'\0'指针就往后加一。int my_strlen(co
- 本文实例讲述了Android基于SoftReference缓存图片的方法。分享给大家供大家参考,具体如下:Java中的SoftReferen
- 前言服务消费者调用服务提供者的时候使用RestTemplate技术存在不便之处:拼接urlrestTmplate.getForObJect这
- Android ListView的Item点击效果的定制
- 这篇会深化View拖拽实例,利用Flutter Animation、插值器以及AnimatedBuilder教大家实现带动画的抽屉效果。先来
- 一、前言在Spring中,事务有两种实现方式:编程式事务管理: 编程式事务管理使用TransactionTemplate可实现更细
- 1.引言在开发中,拖放是一种比较常见的手势操作,使用它能够让应用的交互更加地便捷和友好,本文将简要介绍如何为Android中的View添加拖
- 6.0的手机对于写入手机需要申请权限的我做了如下处理下面我贴出代码package com.example.admin.sdapplicati
- 前言gps定位服务的学习是这段时间gps课程的学习内容,之前老师一直在将概念,今天终于是实践课(其实就是给了一个案例,让自己照着敲).不过在
- 好久没有做web了,JSON目前比较流行,闲得没事,所以动手试试将对象序列化为JSON字符(尽管DotNet Framework
- 前言:我们每天都在编写Java代码,编译,执行。很多人已经知道Java源代码文件(.java后缀)会被Java编译器编译为字节码文件(.cl
- 本文实例讲述了Java实现接口的枚举类。分享给大家供大家参考,具体如下:一 点睛枚举类也可以实现一个或多个接口。与普通类实现一个或多个接口完
- 示例 1 :使用搜索表单创建全屏模式我们要构建的小应用程序有一个应用程序栏,右侧有一个搜索按钮。按下此按钮时,将出现一个全屏模式对话框。它不
- package TOOLS;import java.io.BufferedReader;import java.io.File;import
- 在Android中使用SQLite数据库的入门指南,打算分下面几部分与大家一起分享, 1、什么是SQLite 2、Android中使用SQL
- 一、问题分析入门案例的内容已经做完了,在入门案例中我们创建过一个SpringMvcConfig的配置类,再回想前面咱们学习Spring的时候
- tcp客户端示例#include <errno.h> #include <sys/socket.h> #includ
- 首先先简单的说一下其3大特性的定义:封装:隐藏对象的属性和实现细节,仅对外公开接口,控制在程序中属性的读和修改的访问级别。将抽象得到的数据和