Servlet-3 Async Context, how to do asynchronous writes?

后端 未结 6 1897
忘掉有多难
忘掉有多难 2020-12-07 08:44

Problem Description

Servlet-3.0 API allows to detach a request/response context and answer to it later.

However if I try to write a big amount of data, som

6条回答
  •  时光取名叫无心
    2020-12-07 09:05

    I've found the Servlet 3.0 Asynchronous API tricky to implement correctly and helpful documentation to be sparse. After a lot of trial and error and trying many different approaches, I was able to find a robust solution that I've been very happy with. When I look at my code and compare it to yours, I notice one major difference that may help you with your particular problem. I use a ServletResponse to write the data and not a ServletOutputStream.

    Here my go-to Asynchronous Servlet class adapted slightly for your some_big_data case:

    import java.io.IOException;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    
    import javax.servlet.AsyncContext;
    import javax.servlet.AsyncEvent;
    import javax.servlet.AsyncListener;
    import javax.servlet.ServletConfig;
    import javax.servlet.ServletException;
    import javax.servlet.ServletResponse;
    import javax.servlet.annotation.WebInitParam;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;
    
    import org.apache.log4j.Logger;
    
    @javax.servlet.annotation.WebServlet(urlPatterns = { "/async" }, asyncSupported = true, initParams = { @WebInitParam(name = "threadpoolsize", value = "100") })
    public class AsyncServlet extends HttpServlet {
    
      private static final Logger logger = Logger.getLogger(AsyncServlet.class);
    
      public static final int CALLBACK_TIMEOUT = 10000; // ms
    
      /** executor service */
      private ExecutorService exec;
    
      @Override
      public void init(ServletConfig config) throws ServletException {
    
        super.init(config);
        int size = Integer.parseInt(getInitParameter("threadpoolsize"));
        exec = Executors.newFixedThreadPool(size);
      }
    
      @Override
      public void service(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
    
        final AsyncContext ctx = req.startAsync();
        final HttpSession session = req.getSession();
    
        // set the timeout
        ctx.setTimeout(CALLBACK_TIMEOUT);
    
        // attach listener to respond to lifecycle events of this AsyncContext
        ctx.addListener(new AsyncListener() {
    
          @Override
          public void onComplete(AsyncEvent event) throws IOException {
    
            logger.info("onComplete called");
          }
    
          @Override
          public void onTimeout(AsyncEvent event) throws IOException {
    
            logger.info("onTimeout called");
          }
    
          @Override
          public void onError(AsyncEvent event) throws IOException {
    
            logger.info("onError called: " + event.toString());
          }
    
          @Override
          public void onStartAsync(AsyncEvent event) throws IOException {
    
            logger.info("onStartAsync called");
          }
        });
    
        enqueLongRunningTask(ctx, session);
      }
    
      /**
       * if something goes wrong in the task, it simply causes timeout condition that causes the async context listener to be invoked (after the fact)
       * 

    * if the {@link AsyncContext#getResponse()} is null, that means this context has already timed out (and context listener has been invoked). */ private void enqueLongRunningTask(final AsyncContext ctx, final HttpSession session) { exec.execute(new Runnable() { @Override public void run() { String some_big_data = getSomeBigData(); try { ServletResponse response = ctx.getResponse(); if (response != null) { response.getWriter().write(some_big_data); ctx.complete(); } else { throw new IllegalStateException(); // this is caught below } } catch (IllegalStateException ex) { logger.error("Request object from context is null! (nothing to worry about.)"); // just means the context was already timeout, timeout listener already called. } catch (Exception e) { logger.error("ERROR IN AsyncServlet", e); } } }); } /** destroy the executor */ @Override public void destroy() { exec.shutdown(); } }

提交回复
热议问题