How do I use AsyncContext for asynchronous processing in servlets?

Using AsyncContext in servlets allows you to perform asynchronous request processing. This can improve scalability in situations where a request might involve long-running operations, such as external API calls or database queries, by freeing up server threads while those tasks are performed.

Here’s how you can use AsyncContext for asynchronous processing in a servlet:


1. Mark the Servlet to Support Asynchronous Processing

You need to mark the servlet explicitly as supporting asynchronous processing using the @WebServlet annotation or by defining it in web.xml.

package org.kodejava.servlet; import jakarta.servlet.AsyncContext; import jakarta.servlet.ServletException; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.io.IOException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; @WebServlet(urlPatterns = "/asyncServlet", asyncSupported = true) // Enable async processing public class AsyncServlet extends HttpServlet { // Create a thread pool for processing private final ExecutorService executor = Executors.newFixedThreadPool(10); @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // Start async processing AsyncContext asyncContext = request.startAsync(); // Add a timeout if needed asyncContext.setTimeout(10000); // 10 seconds // Submit async task to executor executor.submit(() -> { try { // Simulate a long task Thread.sleep(2000); // Write response response.getWriter().write("Asynchronous processing completed!"); } catch (Exception e) { asyncContext.complete(); e.printStackTrace(); } finally { // Mark the async context complete asyncContext.complete(); } }); } @Override public void destroy() { executor.shutdown(); // Shut down the executor when the servlet is destroyed } } 

Key Steps in the Code:

  1. Enable Asynchronous Processing:
    Use the @WebServlet annotation’s asyncSupported = true or explicitly configure it in web.xml.

  2. Start Asynchronous Context:
    Call request.startAsync() to start asynchronous processing. This detaches the request and response from the servlet’s typical request-response lifecycle.

  3. Set Timeout (Optional):
    Call asyncContext.setTimeout() to define a maximum time for asynchronous processing. If processing exceeds this time, the AsyncListener.onTimeout method will be triggered, which can be useful for handling timeouts.

  4. Perform Asynchronous Task:
    Use a dedicated thread, thread pool (ExecutorService), or an external resource to perform long-running tasks. This prevents blocking the server’s thread.

  5. Complete the Request:
    Once processing is completed, call asyncContext.complete() to end the asynchronous context, signaling the server to finalize the response.

  6. Handle Exceptions:
    Wrap asynchronous operations in a try-catch block to handle any errors properly and ensure asyncContext.complete() is always called.


2. Advanced Use: Integrate with AsyncListener

With the AsyncListener, you can listen to lifecycle events of asynchronous operations, such as completion, timeouts, errors, etc.

package org.kodejava.servlet; import jakarta.servlet.AsyncContext; import jakarta.servlet.AsyncEvent; import jakarta.servlet.AsyncListener; import jakarta.servlet.annotation.WebServlet; import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; @WebServlet(urlPatterns = "/asyncWithListener", asyncSupported = true) public class AsyncServletWithListener extends HttpServlet { @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) { AsyncContext asyncContext = request.startAsync(); asyncContext.addListener(new AsyncListener() { @Override public void onComplete(AsyncEvent event) { System.out.println("Async operation completed"); } @Override public void onTimeout(AsyncEvent event) { System.out.println("Async operation timed out"); } @Override public void onError(AsyncEvent event) { System.out.println("Async operation error occurred"); } @Override public void onStartAsync(AsyncEvent event) { System.out.println("Async operation started"); } }); asyncContext.start(() -> { try { // Simulate task Thread.sleep(2000); response.getWriter().write("Task processed with listener!"); } catch (Exception e) { e.printStackTrace(); } finally { asyncContext.complete(); } }); } } 

3. Important Notes

  • Thread Safety: Request/Response objects are shared among multiple threads in asynchronous processing. Avoid modifying shared data in a non-thread-safe way.
  • Clean-up Resources: Always ensure you complete the AsyncContext and close any resources used during async processing.
  • Timeouts: It’s a good practice to handle timeouts using AsyncListener to notify the client of any delays.

Using AsyncContext allows your servlet to better handle high concurrency and long-running tasks, improving the application’s scalability and performance.

Maven dependencies

<dependency> <groupId>jakarta.servlet</groupId> <artifactId>jakarta.servlet-api</artifactId> <version>6.1.0</version> <scope>provided</scope> </dependency> 

Maven Central

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.