Search code examples
javaasynchronousservletsexecutorservicethreadpoolexecutor

Java asynchronous servlets


We are using asynchronous servlets with the following mechanism (Asynchronous servlet not acting asynchronously)

@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
    }
}

class AsyncServletTaskRunner implements Runnable {
...
}

The readers and writers are written using ReadListener and WriteListener with ServletOutputStream.

The thread pool size is 5.

Question: We have 5 different asynchronous servlets. Each of them has its own Executor. Each has different mapping, /test1, /test2 etc. But if the number of users increaeses and the time for processes takes more than 10 seconds, we start getting IllegalStateExceptions and the pages becomes unressponsive.

Should we use a common Executor for all servlets? What about the pool size? Should we use only one asynch servlet for the application and differentiate in implementation?

Another question is we see examples of asynch servlets without using any Executor, instead of Executor the usage is as following, which approach is better?

using Executor:

exec.execute(new AsyncServletTaskRunner(..));

or

asy.start(new AsyncServletTaskRunner(..); 

Solution

  • Question: We have 5 different asynchronous servlets. Each of them has its own Executor. Each has different mapping, /test1, /test2 etc. But if the number of users increaeses and the time for processes takes more than 10 seconds, we start getting IllegalStateExceptions and the pages becomes unressponsive.

    That doesn't sound surprising. You're using FixedThreadPoolExecutors to run relatively long-running tasks, so you can relatively easily overcome the concurrent processing ability you set up.

    But the problem starts earlier. Java EE applications are not at liberty to create their own threads at will, however they want. If you insist on using an executor service, then you must use a container-managed one. But I don't immediately see why you should need to do so. Java EE containers already manage multiple threads, and AsyncContext has a built-in mechanism for hooking into that.

    Should we use a common Executor for all servlets? What about the pool size? Should we use only one asynch servlet for the application and differentiate in implementation?

    You probably should not set up your own executor service at all, but if you do (see above) then I don't see what is to be gained by having a separate one for each servlet.

    And I see no particular advantage to trying to load all async behavior on a single servlet.

    Another question is we see examples of asynch servlets without using any Executor [...]

    Yes. AsyncContext.start() assigns the specified task to a container-managed thread.

    which approach is better?

    I guess that depends on the application, but I recommend AsyncContext.start() until and unless you discover that it is somehow insufficient (and how, and why). If you ever reach that point then you will have all the information you need to decide the needed properties of your own ManagedExecutorService.