1 Servlet 3.0 异步处理
1.1 同步请求流程
在Servlet 3.0之前,Servlet采用Thread-Per-Request的方式处理请求。即每一次Http请求都由某一个线程从头到尾负责处理。
如果一个请求需要进行IO操作,比如访问数据库、调用第三方服务接口等,那么其所对应的线程将同步地等待IO操作完成, 而IO操作是非常慢的,所以此时的线程并不能及时地释放回线程池以供后续使用,在并发量越来越大的情况下,这将带来严重的性能问题。即便是像Spring、Struts这样的高层框架也脱离不了这样的桎梏,因为他们都是建立在Servlet之上的。为了解决这样的问题,Servlet 3.0引入了异步处理,然后在Servlet 3.1中又引入了非阻塞IO来进一步增强异步处理的性能。
1.2 异步请求流程
/**
* 1.开启异步处理 asyncSupported=true
*/
// http://localhost:8080/async
@WebServlet(value = "/async", asyncSupported = true)
public class AsyncServlet extends HttpServlet {
private final HelloService helloService = new HelloService();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("thread name is " + Thread.currentThread().getName());
System.out.println("doGet start timestamp=" + System.currentTimeMillis());
//2.开启异步模式
AsyncContext asyncContext = req.startAsync();
//3.业务逻辑进行异步处理(开始)
asyncContext.start(() -> {
System.out.println("runnable start timestamp=" + System.currentTimeMillis());
try {
String res = helloService.sayHello();
// 异步处理调用结束
asyncContext.complete();
//获取到异步的上下文
//AsyncContext async = req.getAsyncContext();
//4.获取响应
ServletResponse response = asyncContext.getResponse();
response.getWriter().write(res);
System.out.println("runnable end timestamp=" + System.currentTimeMillis());
} catch (IOException ignored) {}
});
System.out.println("doGet end timestamp=" + System.currentTimeMillis());
}
}
public class HelloService {
public String sayHello() {
System.out.println("exe sayHello thread name is " + Thread.currentThread().getName());
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException ignored) {}
return Thread.currentThread().getName();
}
}
<!-- https://mvnrepository.com/artifact/javax.servlet/javax.servlet-api -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
2 SpringMVC异步处理