1、传统Servlet处理
Web容器会为每个请求分配一个线程,默认情况下,响应完成前,该线程占用的资源都不会被释放。若有些请求需要长时间(例如长处理时间运算、等待某个资源),就会长时间占用线程所需资源,若这类请求很多,许多线程资源都被长时间占用,会对系统的性能造成负担。
2、新特性:异步处理
Servlet 3.0新增了异步处理,可以先释放容器分配给请求的线程与相关资源,减轻系统负担,原先释放了容器所分配线程的请求,其响应将被延后,可以在处理完成(例如长时间运算完成、所需资源已获得)时再对客户端进行响应。
Servlet 3.0 之前,一个普通 Servlet 的主要工作流程大致如下:
第一步,Servlet 接收到请求之后,可能需要对请求携带的数据进行一些预处理;
第二步,调用业务接口的某些方法,以完成业务处理;
第三步,根据处理的结果提交响应,Servlet 线程结束。
其中第二步的业务处理通常是最耗时的,这主要体现在数据库操作,以及其它的跨网络调用等,在此过程中,Servlet 线程一直处于阻塞状态,直到业务方法执行完毕。在处理业务的过程中,Servlet 资源一直被占用而得不到释放,对于并发较大的应用,这有可能造成性能的瓶颈。对此,在以前通常是采用私有解决方案来提前结束 Servlet 线程,并及时释放资源。
Servlet 3.0 针对这个问题做了开创性的工作,现在通过使用 Servlet 3.0 的异步处理支持,之前的 Servlet 处理流程可以调整为如下的过程:
第一步,Servlet 接收到请求之后,可能首先需要对请求携带的数据进行一些预处理;
第二步,Servlet 线程将请求转交给一个异步线程来执行业务处理,线程本身返回至容器,
第三步,Servlet 还没有生成响应数据,异步线程处理完业务以后,可以直接生成响应数据(异步线程拥有 ServletRequest 和 ServletResponse 对象的引用),或者将请求继续转发给其它 Servlet。
Servlet 线程不再是一直处于阻塞状态以等待业务逻辑的处理,而是启动异步线程之后可以立即返回。
3、异步实现方案
请求入参ServletRequest对象新增了一个startAsync()方法。该方法用于开启异步,同时返回AsyncContext 异步上下文对象。
public AsyncContext startAsync() throws IllegalStateException;
可以通过AsyncContext的getRequest()、getResponse()方法取得请求、响应对象,此次对客户端的响应将暂缓至调用AsyncContext的complete()或dispatch()方法为止,前者表示响应完成,后者表示将调派指定的URL进行响应。
4、异步实现案例:
pom.xml:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><parent><artifactId>SpringBootStudy</artifactId><groupId>com.hsc.www</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>SB_33_servlet_app</artifactId><packaging>war</packaging><properties><maven.compiler.source>11</maven.compiler.source><maven.compiler.target>11</maven.compiler.target></properties><dependencies><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.0.1</version><scope>provided</scope></dependency><dependency><groupId>net.dreamlu</groupId><artifactId>mica-core</artifactId><version>2.1.1-GA</version></dependency></dependencies></project>
web.xml
<!DOCTYPE web-app PUBLIC"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN""http://java.sun.com/dtd/web-app_2_3.dtd" ><web-app><display-name>Archetype Created Web Application</display-name>
</web-app>
AsyncServlet.java:
package com.hsc.www.webFlux.servlet;import net.dreamlu.mica.core.utils.$;import javax.servlet.*;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;@WebServlet(name = "AsyncServlet", urlPatterns = {"/testAsyn"}, asyncSupported = true)
public class AsyncServlet extends GenericServlet {ExecutorService executorService = Executors.newFixedThreadPool(10);@Overridepublic void service(ServletRequest servletRequest, ServletResponse servletResponse) throws IOException {AsyncContext asyncContext = servletRequest.startAsync();executorService.submit(new Task(asyncContext));PrintWriter out = asyncContext.getResponse().getWriter();out.println("<h1>"+ $.formatDateTime(new Date())+" service threadName:" + Thread.currentThread().getName() + "</h1>");out.flush();}public static class Task implements Runnable {private final AsyncContext asyncContext;public Task(AsyncContext asyncContext) {this.asyncContext = asyncContext;}@Overridepublic void run() {try {HttpServletResponse response = (HttpServletResponse) asyncContext.getResponse();sleep(10);PrintWriter out = response.getWriter();out.println("<h1>"+ $.formatDateTime(new Date())+" task threadName:" + Thread.currentThread().getName() + "</h1>");out.flush();} catch (Exception e) {e.printStackTrace();} finally {asyncContext.complete();}}}private static void sleep(int s) {try {Thread.sleep(s * 1000L);} catch (Exception e) {e.printStackTrace();}}
}
Tomcat servlet3.0需要 tomcat 7或更高版本才支持
运行结果: