Java Servlet线程中AsyncContext异步处理Http请求
作者:Redick01 发布时间:2023-10-02 23:17:39
AsyncContext
AsyncContext
是Servlet 3.0
使Servlet 线程不再需要一直阻塞,直到业务处理完毕才能再输出响应,最后才结束该Servlet线程。在接收到请求之后,Servlet线程可以将耗时的操作委派给另一个线程来完成,自己在不生成响应的情况下返回至容器。针对业务处理较耗时的情况,这将大大减少服务器资源的占用,并且提高并发处理速度
Servlet 3.0新增了异步处理,可以先释放容器分配给请求的线程与相关资源,减轻系统负担,原先释放了容器所分配线程的请求,其响应将被延后,可以在处理完成(例如长时间运算完成、所需资源已获得)时再对客户端进行响应。
在Servlet 3.0中,在ServletRequest
上提供了startAsync()
方法,该方法会根据请求的ServletRequest
和ServletResponse
创建AsyncContext
对象。
@Override
public AsyncContext startAsync() {
return startAsync(getRequest(),response.getResponse());
}
@Override
public AsyncContext startAsync(ServletRequest request,
ServletResponse response) {
if (!isAsyncSupported()) {
IllegalStateException ise =
new IllegalStateException(sm.getString("request.asyncNotSupported"));
log.warn(sm.getString("coyoteRequest.noAsync",
StringUtils.join(getNonAsyncClassNames())), ise);
throw ise;
}
if (asyncContext == null) {
asyncContext = new AsyncContextImpl(this);
}
asyncContext.setStarted(getContext(), request, response,
request==getRequest() && response==getResponse().getResponse());
asyncContext.setTimeout(getConnector().getAsyncTimeout());
return asyncContext;
}
请求调用startAsync
后Servlet
线程将会被释放,请求交由其他线程去处理,如果业务线程没有处理完,客户端将不会收到响应,直到调用AsyncContext
的complete()
和dispatch(ServletContext context, String path)
方法为止,dispatch
方法会根据path
进行重定向。AsyncContextImpl
大致代码如下:
@Override
public void complete() {
if (log.isDebugEnabled()) {
logDebug("complete ");
}
check();
request.getCoyoteRequest().action(ActionCode.ASYNC_COMPLETE, null);
}
@Override
public void dispatch() {
check();
String path;
String cpath;
ServletRequest servletRequest = getRequest();
if (servletRequest instanceof HttpServletRequest) {
HttpServletRequest sr = (HttpServletRequest) servletRequest;
path = sr.getRequestURI();
cpath = sr.getContextPath();
} else {
path = request.getRequestURI();
cpath = request.getContextPath();
}
if (cpath.length() > 1) {
path = path.substring(cpath.length());
}
if (!context.getDispatchersUseEncodedPaths()) {
path = UDecoder.URLDecode(path, StandardCharsets.UTF_8);
}
dispatch(path);
}
AsyncContext使用示例及测试
示例
设置Tomcat线程数为1
server:
port: 9099
servlet:
context-path: /server/v1
# 设置Tomcat线程数为1
tomcat:
min-spare-threads: 1
max-threads: 1
Controller
@RestController
public class AsyncTestController {
private final ScheduledExecutorService timeoutChecker = new ScheduledThreadPoolExecutor(1, threadFactory);
private static boolean result = false;
@PostMapping("/async")
public void async(@RequestBody Request re1, HttpServletRequest request, HttpServletResponse response) {
// 创建AsyncContext
AsyncContext asyncContext = request.startAsync(request, response);
String name = re1.getUsername();
// 设置处理超时时间2s
asyncContext.setTimeout(2000L);
// asyncContext监听
asyncContext.addListener(new AsyncListener() {
@Override
public void onComplete(AsyncEvent asyncEvent) throws IOException {
}
@Override
public void onTimeout(AsyncEvent asyncEvent) throws IOException {
asyncContext.getResponse().getWriter().print(name + ":timeout");
asyncContext.complete();
}
@Override
public void onError(AsyncEvent asyncEvent) throws IOException {
}
@Override
public void onStartAsync(AsyncEvent asyncEvent) throws IOException {
}
});
// 定时处理业务,处理成功后asyncContext.complete();完成异步请求
timeoutChecker.scheduleWithFixedDelay(() -> {
try {
if (result) {
asyncContext.getResponse().getWriter().print(name);
asyncContext.complete();
}
} catch (IOException e) {
e.printStackTrace();
}
}, 0, 100L, TimeUnit.MILLISECONDS);
}
// 模拟业务处理完成
@PostMapping("/notify")
public void notify(Boolean s) {
result = s;
}
}
测试结果
测试指标
并发5,两个循环
测试结果
10条并发请求都能够处理,并且处理的时间都是2s左右(因为设置的超时时间是2s),通过该测试结果可以看出,使用AsyncContext
可以在容器资源有限的情况下处理更多的请求,这在高并发场景下就比较有用了。
AsyncContext应用场景
使用AsyncContext
实现的Http长轮询在许多的中间件的信息同步场景中应用广泛,例如Nacos配置中心和Apache Shenyu网关。
背景
公司一个系统是Netty
实现的TCP协议的服务,其中的一个业务是设备请求后台接口查询支付结果,接口的处理逻辑是收到请求后就将请求放到一个队列中,然后由业务线程异步处理,当收到支付结果完成后将响应给客户端支付结果,该接口的超时时间是2s,如果2s查不到支付结果就返回给客户端查不到结果,客户端收到该错误后重新发起查询,直到客户端的整个业务超时。
公司由于服务架构调整,要将该系统改造成基于SpringBoot的Http协议接口,如果”支付结果查询接口“不做机制的变更,就会导致每一次结果查询都会阻塞等待队列中查询支付结果的查询,因为支付是异步的,所以支付结果查询会比较耗时,如果机制不改那么如果并发增大的话会导致服务器的处理请求线程全部被打满,整个服务对于其他请求,其他业务都变得不可用了,这个结果是不可以接受的。
AsyncContext解决生产问题
基于示例中的demo进行业务改造
开启异步,设置整个异步接口处理的超时时间(2s),设置Listener主要用于处理接口超时,阻塞队列处理查询支付结果,查到结果后调用complete完成该长轮询,如果2s没有查到结果,那就返回查询超时,客户端继续轮询。
来源:https://blog.csdn.net/qq_31279701/article/details/125376579


猜你喜欢
- 为什么我们需要IntentService ?Android中的IntentService是继承自Service类的,在我们讨论IntentS
- 实践过程效果代码public partial class Form1 : Form{ public Form1()
- 使用Android AudioRecord 录制PCM文件,android SDK保证在所有设备上都支持的采样频率只有44100HZ,所以如
- 本文分析了Android编程中出现The connection to adb is down问题的解决方法。分享给大家供大家参考,具体如下:
- 可重入锁,从字面来理解,就是可以重复进入的锁。可重入锁,也叫做递归锁,指的是同一线程外层函数获得锁之后,内层递归函数仍然有获取该锁的代码,但
- 目录前言Java Heap进程内存分配内存不足管理GC 垃圾回收内核交换守护进程低内存终止守护进程最后前言很高兴遇见你~内存优化一直是 An
- Spring boot 2.1.X整合Elasticsearch最新版的一处问题新版本的Spring boot 2的spring-boot-
- 本文实例为大家分享了Unity使用射线实现贴花系统,供大家参考,具体内容如下老规矩,直接上代码:using System.Collectio
- 背景最近让我做一个大数据的系统,分析了一下,麻烦的地方就是多数据源切换抽取数据。考虑到可以跨服务器跨数据库抽数,再整理数据,就配置了这个动态
- Android签名机制什么是Android签名了解 HTTPS 通信的同学都知道,在消息通信时,必须至少解决两个问题:一是确保消息来源的真实
- 一、RESTful 简介REST 是一种软件架构风格。REST:Representational State Transfer,表现层资源状
- Android MediaPlayer实现音乐播放器1、布局文件<?xml version="1.0" encod
- mybatis if test判断BigDecimal遇到的坑<update id="test" paramete
- 如果使用IDEA创建Springboot项目,默认会在resource目录下创建application.properties文件,在spri
- 不用单点登录,模拟远程项目的登录页面表单,在访问这个页面的时候自动提交表单到此项目的登录action,就可以实现登录到其他系统。ssh框架项
- 本文实例讲述了C# winform实现右下角弹出窗口结果的方法。分享给大家供大家参考,具体如下:using System.Runtime.I
- 学生模块功能比较少,就是进行考试和查看自己成绩两个大的功能。学生进行考试的功能比较复杂(首先做了校验,不在考试时间范围内,不能进行考试)考试
- 前言经常使用Swagger的小伙伴应该有所体会,Swagger对于JSON的支持真的很不友好!最近发现了两款颜值很不错的JSON可视化工具,
- CSRF介绍CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也被称为:one click atta
- 方向传感器是算法生成的传感器之一,主要借助于磁场传感器的数据。Android系统自带了方向传感器,不过系统5.0之后方法就被废除了(我们还是