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:
- Enable Asynchronous Processing:
Use the@WebServletannotation’sasyncSupported = trueor explicitly configure it inweb.xml. -
Start Asynchronous Context:
Callrequest.startAsync()to start asynchronous processing. This detaches the request and response from the servlet’s typical request-response lifecycle. -
Set Timeout (Optional):
CallasyncContext.setTimeout()to define a maximum time for asynchronous processing. If processing exceeds this time, theAsyncListener.onTimeoutmethod will be triggered, which can be useful for handling timeouts. -
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. -
Complete the Request:
Once processing is completed, callasyncContext.complete()to end the asynchronous context, signaling the server to finalize the response. -
Handle Exceptions:
Wrap asynchronous operations in atry-catchblock to handle any errors properly and ensureasyncContext.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
AsyncContextand close any resources used during async processing. - Timeouts: It’s a good practice to handle timeouts using
AsyncListenerto 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> 