srvlet 新增了异步处理,可以先释放容器分配给请求的线程与相关资源,减轻系统负担,原先释放了容器所分配线程的请求,其响应将被延后,可以在处理完成(例如长时间运算完成、所需资源已获得)时再对客户端进行响应。
Servlet2.0之前,一个普通 Servlet 的主要工作流程大致如下: 第一步,Servlet 接收到请求之后,可能需要对请求携带的数据进行一些预处理; 第二步,调用业务接口的某些方法,以完成业务处理; 第三步,根据处理的结果提交响应,Servlet 线程结束。 其中第二步的业务处理通常是最耗时的,这主要体现在数据库操作,以及其它的跨网络调用等,在此过程中,Servlet 线程一直处于阻塞状态,直到业务方法执行完毕。在处理业务的过程中,Servlet 资源一直被占用而得不到释放,对于并发较大的应用,这有可能造成性能的瓶颈。对此,在以前通常是采用私有解决方案来提前结束 Servlet 线程,并及时释放资源。
Servlet 针对这个问题做了开创性的工作,现在通过使用 Servlet 3.0 的异步处理支持,之前的 Servlet 处理流程可以调整为如下的过程: 第一步,Servlet 接收到请求之后,可能首先需要对请求携带的数据进行一些预处理; 第二步,Servlet 线程将请求转交给一个异步线程来执行业务处理,线程本身返回至容器, 第三步,Servlet 还没有生成响应数据,异步线程处理完业务以后,可以直接生成响应数据(异步线程拥有 ServletRequest 和 ServletResponse 对象的引用),或者将请求继续转发给其它 Servlet。 Servlet 线程不再是一直处于阻塞状态以等待业务逻辑的处理,而是启动异步线程之后可以立即返回。
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;}
使用场景
该方法主要是将客户端的长轮询请求添加到某个东西中去:服务端将客户端的长轮询请求封装成一个叫 ClientLongPolling 的任务,交给 scheduler 去执行,但是request,和response不会立即返回客户端直到asyncContext.complete才返回,generateResponse是nacos客户端长轮询请求达到超时时间30秒或者检测到配置变更才会调用。
总结
容器里边实现 request 的时候尽量复用,而不是收回,但是组件的异步处理功用被启用(request 上调用了 startAsync 办法)。request 的生命周期在遇到异步的时候有点特殊,仅仅会释放容器分配给请求的线程与相关资源但是request和response不会被释放可以在子线程获取,待子线程耗时操作执行完才会一起释放返回客户端