我正在使用Spring启动来探索异步servlet。据我所知,异步servlet用于在容器启动以处理请求的线程之外的线程中执行长时间运行的任务,以便容器可以使用自己的线程来处理其他连接。基于这种理解,我尝试了以下代码:
@WebServlet(asyncSupported = true, urlPatterns = "/demo")
@Component
public class demo extends HttpServlet {
/**
* @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
final AsyncContext context = request.startAsync();
ServletOutputStream stream = response.getOutputStream();
WriteListener listener = new WriteListener() {
@Override
public void onWritePossible() throws IOException {
System.out.println(Thread.currentThread().getId());
ServletOutputStream output = context.getResponse().getOutputStream();
if (output.isReady()) {
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
output.print("Heloooooooooo");
}
context.complete();
}
@Override
public void onError(Throwable throwable) {
throwable.printStackTrace(System.err);
context.complete();
}
};
stream.setWriteListener(listener);
System.out.println(Thread.currentThread().getId());
}
/**
* @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse
* response)
*/
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException,
IOException {
// TODO Auto-generated method stub
}
}
但问题是onWritePossible
方法是由运行doGet
的同一个线程调用的。难道不一样吗?
答案 0 :(得分:1)
在doGet
方法中,您创建了一个AsyncContext
,您有责任将其进一步传递到另一个线程,该线程将在稍后通过设置结果完成处理 - servlet容器不会为您创建新线程。
将AsyncContext
传递给另一个线程后,doGet
可以立即返回释放servlet容器的执行程序线程以提供新请求。
基于您的代码的示例:
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
final AsyncContext context = request.startAsync();
System.out.println("Servlet container thread before async started: "
+ Thread.currentThread().getName());
new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println("New thread started: "
+ Thread.currentThread().getName());
ServletOutputStream output = context.getResponse().getOutputStream();
TimeUnit.SECONDS.sleep(3);
System.out.println("New thread writing response: "
+ Thread.currentThread().getName());
output.print("Hi!");
context.complete();
} catch (Throwable e) {
e.printStackTrace();
}
}
}).start();
System.out.println("Servlet container thread returning: "
+ Thread.currentThread().getName());
}
输出:
Servlet container thread before async started: http-nio-8080-exec-1
Servlet container thread returning: http-nio-8080-exec-1
New thread started: Thread-3
New thread writing response: Thread-3
如果使用Spring Boot,则可以实现Controller
,返回Callable
,Spring Framework将确保Callable
在生命周期由其管理的其他线程中执行弹簧。
如果你想在没有Spring的情况下这样做(这也很好),通常是将AsyncContext
放入队列并让ExecutorService
消耗排队的请求。
答案 1 :(得分:-1)
你的理解有点不对劲。异步方法通常使用的好处是不消耗额外的线程,而不阻止处理请求的线程。要从中获得任何真正的好处,您需要在执行IO时使用非阻塞API。
This question,虽然在.NET技术的背景下被问到,但应该有所启发。