异步servlet不是异步操作

我有一个servlet接受请求并写一个长响应。 响应处于循环中,该循环使用Thread.sleep(1000)来模拟长时间运行的操作。 我试图在这里设置一个异步请求,如代码所示。 但它没有用。 当我向servlet调用多个请求时,它们都是连续执行,而不是同时执行。 我究竟做错了什么?

我认为servlet应该是线程化的 – 每个服务器请求都会导致容器执行一个新线程(或者从池中重用一个)。

package test; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.AsyncContext; import javax.servlet.annotation.WebServlet; @WebServlet(urlPatterns={"/test"}, asyncSupported=true) public class TestServ extends HttpServlet { @Override public void doGet(HttpServletRequest rq, HttpServletResponse rs){ rs.setContentType("text/plain"); rs.setHeader("Access-Control-Allow-Origin", "*"); AsyncContext asy = rq.startAsync(rq, rs); asy.start(new Client(asy)); } @Override public String getServletInfo() { return "Short description"; } } class Client implements Runnable { private int counter = 0; private AsyncContext asy; Client(AsyncContext asy) { this.asy = asy; } @Override public void run() { //run long task here try { PrintWriter out = asy.getResponse().getWriter(); while (counter < 5) { out.println(counter++ + 1); Thread.sleep(1000); } } catch (Exception ex) { } finally{ asy.complete(); } } } 

使用方法ExecutorService.execute()在后台线程中生成一些任务。

要遵循的步骤:

  • 在servlet init()方法中读取web.xml中的一些init参数,例如timeout和threadpoolsize
    • timeout参数用于设置Async线程的超时
    • threadpoolsize用于创建异步线程池
  • 通过在doGet()或doPost()方法中调用HTTP request.startAsync()来获取AsyncContext
  • 设置AsyncContext的超时
  • 附加侦听器以响应此AsyncContext的生命周期事件,例如onComplete(),onTimeout(),onError(),onStartAsync()
  • 调用ExecutorService.execute()在后台线程中生成一些任务

试试这个示例代码。 它可能对你有所帮助。

AsyncServletTaskProcessor:

 import java.io.IOException; import javax.servlet.AsyncContext; import javax.servlet.ServletException; public interface AsyncServletTaskProcessor { void process(AsyncContext ctx) throws IOException, ServletException; } 

TestServ:

 import java.io.IOException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import javax.servlet.AsyncContext; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet(urlPatterns = { "/test" }, asyncSupported = true) public class TestServ extends HttpServlet implements AsyncServletTaskProcessor{ /** The exec. */ private ExecutorService exec; public int CALLBACK_TIMEOUT; public void init() throws ServletException { // read callback timeout form web.xml as init parameter CALLBACK_TIMEOUT = Integer.parseInt(getInitParameter("timeout")); // read thread pool size form web.xml as init parameter int size = Integer.parseInt(getInitParameter("threadpoolsize")); exec = Executors.newFixedThreadPool(size); } @Override public void doGet(HttpServletRequest rq, HttpServletResponse rs) { rs.setContentType("text/plain"); rs.setHeader("Access-Control-Allow-Origin", "*"); //AsyncContext asy = rq.startAsync(rq, rs); //asy.start(new Client(asy)); final AsyncContext asy = rq.startAsync(); // set the timeout asy.setTimeout(CALLBACK_TIMEOUT); // attach listener to respond to lifecycle events of this AsyncContext asy.addListener(new AsyncListenerImpl(asy)); // spawn some task in a background thread exec.execute(new AsyncServletTaskRunner(asy, this)); } @Override public String getServletInfo() { return "Short description"; } @Override public void process(AsyncContext ctx) throws IOException, ServletException { //do whatever you want to do as process of each thread } } 

AsyncServletTaskRunner:

 import javax.servlet.AsyncContext; public class AsyncServletTaskRunner implements Runnable { /** The ctx. */ private AsyncContext ctx; /** The processor. */ private AsyncServletTaskProcessor processor; public AsyncServletTaskRunner() { super(); } public AsyncServletTaskRunner(AsyncContext ctx, AsyncServletTaskProcessor processor) { this.ctx = ctx; this.processor = processor; } @Override public void run() { try { processor.process(ctx); } catch (Exception e) { try { // redirect to error page or do whatever is needed } catch (Exception e1) { e1.printStackTrace(); } } finally { ctx.complete(); } } public AsyncContext getCtx() { return ctx; } public void setCtx(AsyncContext ctx) { this.ctx = ctx; } } 

AsyncListenerImpl:

 import java.io.IOException; import javax.servlet.AsyncContext; import javax.servlet.AsyncEvent; import javax.servlet.AsyncListener; public class AsyncListenerImpl implements AsyncListener { /** The ctx. */ private AsyncContext ctx; public AsyncListenerImpl() { super(); } public AsyncListenerImpl(AsyncContext ctx) { this.ctx = ctx; } @Override public void onComplete(AsyncEvent event) throws IOException { /** complete() has already been called on the async context, nothing to do */ } @Override public void onTimeout(AsyncEvent event) throws IOException { /** timeout has occured in async task... handle it */ try { // redirect to error page or do whatever is needed } catch (Exception e1) { e1.printStackTrace(); } finally { ctx.complete(); } } @Override public void onError(AsyncEvent event) throws IOException { /** THIS NEVER GETS CALLED - error has occured in async task... handle it */ try { // redirect to error page or do whatever is needed } catch (Exception e1) { e1.printStackTrace(); } finally { ctx.complete(); } } @Override public void onStartAsync(AsyncEvent event) throws IOException { /** async context has started, nothing to do */ } }