SoFunction
Updated on 2025-04-10

HttpServletRequest Reuse Problem Record when using asynchronous threads in Spring Boot

1. Problem description: Cookie parsing failed when request reuse is caused by asynchronous thread operation

1. Scene background

In a web application, there is usually one for each requestHttpServletRequestObject to save the context information of the request. For example,HttpServletRequestStored the requestedCookieinformation. To improve performance and reduce memory usage, web containers (such as Tomcat) will workHttpServletRequestObjects are reused. That is, when a request is completed, Tomcat willHttpServletRequestThe object is put back into the pool for use on the next request.

To avoid repeated parsing of certain information every time the request (e.g.Cookie), the developer may parse and mark the state of the requested object in the main thread, for example by setting acookieParsedThe flag indicatesCookieHas been parsed. This process was originally intended to avoid duplicate parsing operations, but if the requested flag bit is modified in the asynchronous thread, it may affect the behavior during request reuse, resulting in problems during the next request reuse.

2. The root of the problem

  • Asynchronous thread operation request object: When the main thread is parsedHttpServletRequestIn-houseCookieAfter the information,markcookieParsedfor "parsed", then start an asynchronous thread to execute some long-term tasks, and then the main thread completes execution and performs a Request recycling operation (For example: clear context information,cookieParsedSet to unresolved state). becauseHttpServletRequestis a shared object (shared between the main thread and the asynchronous thread), and the asynchronous thread may modify the state of the requested object, for examplecookieParsedSet to "Resolved".
  • Request multiplexing mechanism: After the current request is completed,HttpServletRequestIt will be recycled and returned to the request pool for reuse of the next request. During multiplexing, Tomcat checks the status of the current requested object. If the previous request objectcookieParsedMarked as "resolved", the next request will be reused when the request object isSkip the parsing steps of cookies, resulting in the next request not being able to obtain cookie information correctly.
  • Flag bit is not reset: Since after the main thread ends,cookieParsedThe flag is set to "Resolved", but the asynchronous thread does not reset the flag after the task is completed, resulting in the request object being incorrectly marked as having been resolved cookies when reused. This will directly affect the processing of the next request.Causes to resolve cookiesIt will not be normal until the Request is recycled again and the Request recycling operation is performed again.

2. Detailed analysis of the problem

1. Scene reappears

  • Main thread acquisitionHttpServletRequestofCookie: When the main thread processes HTTP requests, it first starts fromHttpServletRequestIn the parsingCookieand mark its parsing status. Typically, Tomcat will recycle the request object after the request is completed.
  • Asynchronous thread startup: After the main thread ends, asynchronous tasks (for example, long-term export tasks) will continue to be executed. During this process, the asynchronous thread will continue to access the sameHttpServletRequestObject.
  • Request reuse: Since Tomcat reuses the request object, when a request is processed, it returns the request object to the pool for the next request to be reused. If an asynchronous thread modifies some status flags of the request (e.g. markup).Cookiehas been parsed), the next request may reuse the modified onesHttpServletRequestObject.
  • Data pollution issues: Since the multiplexed request object has been marked as "Cookies have been resolved”, this state may be reused,Causes the next request to skipCookieAnalytical logic, resulting in the acquisition ofCookiefornull, thereby affecting the processing of the requested data.

Code example:

public String handleRequest(HttpServletRequest request, HttpServletResponse response) {
    // The main thread starts executing and parsing cookie information    String cookieValue = null;
    Cookie[] cookies = ();
    if (cookies != null) {
        for (Cookie cookie : cookies) {
            if ("UID".equals(())) {
                cookieValue = ();
                break;
            }
        }
    }
    // Start the asynchronous thread after the main thread is completed    AsyncContext asyncContext = (request, response);
    new Thread(() -> {
        try {
            // Simulate delay tasks            (5000);
            // Asynchronous thread tries to read the cookie again, setting the `cookieParsed` in the recycled request to "parsed"            String cookieValueFromAsync = ()[0].getValue();  
            ("Cookies in asynchronous threads: " + cookieValueFromAsync);
            ();
        } catch (InterruptedException e) {
            ();
        }
    }).start();
    return "success";
}

question:

  • When an asynchronous thread executes,requestHas been recycled,()ReturnedCookieIt might be oneEmpty arrayOrWrong Cookies. At this time, even if there is a valid one in the requestCookie, asynchronous threads still cannot obtain the correct value.
  • Recycled at the same timerequestHas been marked as "Cookies have been resolved”,The request that causes the next request to be reused is skippedCookieAnalytical logic, causing the next request to obtainCookieis empty.

2. Problem analysis

Tomcat request multiplexing mechanism

  • Tomcat will not be destroyed immediately after the request is processedHttpServletRequestObject, instead put it in the object pool for reuse of the next request. When the request is completed, if the asynchronous thread has accessed itHttpServletRequest, the request object of the main thread will continue to be used.
  • If the main thread has processed the request, it has alreadyHttpServletRequestTagged "Cookies have been resolved”, this state may be reused, causing the next request to skipCookieAnalysis of .

Asynchronous thread conflicts with request object status

  • Although the asynchronous thread and the main thread share the sameHttpServletRequestObject, but the asynchronous thread modifies the requested state (e.g.cookieParsedflags), which will affect other threads' ability to access requested data.
  • In this case, the next request uses the already marked "Cookie analysis completed” request object, resulting in parsing failure.

Request context delivery failed

  • In asynchronous threads, due to thread isolation, theHttpServletRequestCannot be automatically passed to asynchronous thread. Even if usedAsyncContextto delay cleaning requests,HttpServletRequestThe data in it may also not be properly passed to the asynchronous thread.

Request flags and cleaning mechanisms

  • Tomcat uses the request flag (e.g.cookieParsedorrequestCompleted) to track the status of the request and clean up the requested resource after the request processing is completed. When the asynchronous thread and the main thread share the same request object, these flags may be modified unexpectedly, affecting the correctness of the multiplexed request.
  • Once the request enters asynchronous mode, Tomcat marks its status as "processing is completed" and passes()Delay cleaning of the request object. This delayed cleaning mechanism will allow the asynchronous thread to continue to hold the original request object, causing conflicts in request flags and data pollution.

3. Solution

To avoidHttpServletRequestThe state of the ,is modified and correctly passes the request context to the asynchronous thread, are the recommended solutions.

useHttpServletRequestWrapperCreate a request copy

Create a copy of the request in an asynchronous thread to avoid directly manipulating the original request object, thereby solving the request reuse problem.

public String handleRequest(HttpServletRequest request, HttpServletResponse response) {
    // Create a request copy    HttpServletRequest requestCopy = new HttpServletRequestWrapper(request) {
        @Override
        public Cookie[] getCookies() {
            Cookie[] cookies = ();
            // parse cookies or create copy            return cookies;
        }
    };
    AsyncContext asyncContext = (request, response);
    new Thread(() -> {
        try {
            // Use replicas in asynchronous threads            String cookieValueFromAsync = ()[0].getValue(); 
            ("Cookies in asynchronous threads: " + cookieValueFromAsync);
            ();
        } catch (InterruptedException e) {
            ();
        }
    }).start();
    return "success";
}

advantage:passHttpServletRequestWrapperThe created replica ensures that the asynchronous thread does not directly modify the original request object, thus avoiding data contamination during request reuse.

Manually pass request context

passRequestContextHolderManually pass the request context to the asynchronous thread, ensuring that the asynchronous thread can access the request data of the main thread.

public String handleRequest(HttpServletRequest request, HttpServletResponse response) {
    AsyncContext asyncContext = (request, response);
    // Manually pass the request context to the asynchronous thread    new Thread(() -> {
        try {
            // Set the current request context            ServletRequestAttributes attributes = new ServletRequestAttributes(request, response);
            (attributes, true);
            // Get request parameters in asynchronous thread            String cookieValueFromAsync = ()[0].getValue(); 
            ("Cookies in asynchronous threads: " + cookieValueFromAsync);
            ();
        } catch (InterruptedException e) {
            ();
        } finally {
            // Clean up the request context            ();
        }
    }).start();
    return "success";
}

advantage: Manually passing the request context allows asynchronous threads to access the request information of the main thread, avoiding the context isolation problem between the asynchronous thread and the main thread.

Delaying the cleaning of request objects

pass()Delay the cleanup of requests, avoiding the request object being recycled during asynchronous thread execution, thereby maintaining the validity of the requested data.

public String handleRequest(HttpServletRequest request, HttpServletResponse response) {
    AsyncContext asyncContext = (request, response);
    new Thread(() -> {
        try {
            // Execute asynchronous tasks            (5000); // Simulate long-term tasks            (); // Delay request cleaning        } catch (InterruptedException e) {
            ();
        }
    }).start();
    return "success";
}

advantage: By delaying cleaning of the request object, ensure that asynchronous threads can access valid request data, avoiding the request data being accidentally cleaned during asynchronous task execution.

4. Summary

When dealing with asynchronous threads, especially when dealing withHttpServletRequestWhen you wait for a request object, you may encounter request reuse and context delivery issues. By rationally using request replicas, manually passing request contexts, delaying request cleaning, etc., we can effectively avoid data contamination and request object reuse issues, thereby ensuring the correctness of request data in asynchronous tasks.

Core issues

  • Request reuse: Tomcat will reuse the request object, causing the asynchronous thread to access the modified request.
  • Asynchronous thread cannot access the requested data: The requested data cannot be accessed because the request object may have been cleaned or marked as "complete" when executing asynchronous thread.

Solution

  • useHttpServletRequestWrapperCreate a copy of the request.
  • Manually pass the request context to the asynchronous thread.
  • Delays the cleanup of request objects, ensuring that asynchronous threads can access request data during execution.

This is the article about the HttpServletRequest reuse problem when using asynchronous threads in Spring Boot. This is the end of this article. For more related contents of Spring Boot asynchronous threads, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!