servlet · 2025-10-01 0

Servlet 使用 AsyncContext 的 startAsync() 开启异步

一、同步和的异步 Servlet

Servlet 3.0 的异步处理支持特性,使Servlet 线程不再需要一直阻塞。

1.同步的 Servlet

public class SyncServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        response.getWriter().write("data:Data chunk\n");
        response.getWriter().flush();
    }
}

2.异步的 Servlet

使用 request.startAsync() 开启异步
使用 asyncContext.complete() 结束

public class AsyncServlet extends HttpServlet {

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        AsyncContext asyncContext = request.startAsync();
        asyncContext.setTimeout(0);

        new Thread(() -> {
            try {
                TimeUnit.SECONDS.sleep(5);
                ServletResponse response1 = asyncContext.getResponse();
                response1.getWriter().write("data:Data chunk\n");
                response1.getWriter().flush();
                asyncContext.complete();
            } catch (Exception e) {
                System.err.println(e.getMessage());
            }
        }).start();
    }
}

二、原理

1) response.getWriter().flush() 会调用 coyoteResponse.action(ActionCode.CLIENT_FLUSH, null) 把数据发送给客户端
2) response.finishResponse() 会调用 coyoteResponse.action(ActionCode.CLOSE, null) 通知客户端结束
3) asyncContext.complete() 会调用 request.getCoyoteRequest().action(ActionCode.ASYNC_COMPLETE, null) 通知客户端结束

// CoyoteAdapter.java
@Override
public void service(org.apache.coyote.Request req, org.apache.coyote.Response res) throws Exception {
    Request request = (Request) req.getNote(ADAPTER_NOTES);
    Response response = (Response) res.getNote(ADAPTER_NOTES);
    ...
    boolean async = false;
    boolean postParseSuccess = false;

    req.setRequestThread();

    try {
        // Parse and set Catalina and configuration specific
        // request parameters
        postParseSuccess = postParseRequest(req, request, res, response);
        if (postParseSuccess) {
            // check valves if we support async
            request.setAsyncSupported(connector.getService().getContainer().getPipeline().isAsyncSupported());
            // Calling the container
            connector.getService().getContainer().getPipeline().getFirst().invoke(request, response);
        }
        if (request.isAsync()) {
            async = true;
            ...
        } else {
            request.finishRequest();
            response.finishResponse();
        }
    } catch (IOException e) {
        // Ignore
    }
    ...
}
// OutputBuffer.java
protected void doFlush(boolean realFlush) throws IOException {
    ...
    coyoteResponse.action(ActionCode.CLIENT_FLUSH, null);
    ...
}

public void close() throws IOException {
    ...
    coyoteResponse.action(ActionCode.CLOSE, null);
}