最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

在 Spring Boot 中使用異步線程時的 HttpServletRequest 復(fù)用問題記錄

 更新時間:2025年03月01日 15:27:43   作者:老友@  
文章討論了在SpringBoot中使用異步線程時,由于HttpServletRequest復(fù)用導(dǎo)致的Cookie解析失敗問題,為了解決這個問題,文章推薦了使用HttpServletRequestWrapper創(chuàng)建請求副本、手動傳遞請求上下文和延遲請求清理等方法,感興趣的朋友一起看看吧

一、問題描述:異步線程操作導(dǎo)致請求復(fù)用時 Cookie 解析失敗

1. 場景背景

在一個 Web 應(yīng)用中,通常每個請求都會有一個 HttpServletRequest 對象來保存該請求的上下文信息。例如,HttpServletRequest 存儲了請求中的 Cookie 信息。為了提高性能和減少內(nèi)存使用,Web 容器(例如 Tomcat)會對 HttpServletRequest 對象進(jìn)行復(fù)用。也就是說,當(dāng)一個請求完成后,Tomcat 會將 HttpServletRequest 對象放回池中,供下一次請求使用。

為了避免每次請求都重復(fù)解析某些信息(例如 Cookie),開發(fā)人員可能會在主線程中解析并標(biāo)記請求對象的狀態(tài),例如通過設(shè)置一個 cookieParsed 標(biāo)志位,表明 Cookie 已經(jīng)解析過。這一過程本來是為了避免重復(fù)的解析操作,但如果在異步線程中修改了請求的標(biāo)志位,可能會影響到請求復(fù)用時的行為,導(dǎo)致下一個請求復(fù)用時出現(xiàn)問題。

2. 問題根源

  • 異步線程操作請求對象: 當(dāng)主線程解析完 HttpServletRequest 中的 Cookie 信息后,標(biāo)記 cookieParsed 為“已解析”,然后啟動一個異步線程執(zhí)行一些長時間的任務(wù),然后主線程執(zhí)行完畢,進(jìn)行Request回收操作(例如:清空上下文信息,cookieParsed置為未解析狀態(tài))。由于 HttpServletRequest 是一個共享對象(在主線程和異步線程之間共享),異步線程可能會修改該請求對象的狀態(tài),例如將 cookieParsed 設(shè)置為“已解析”。
  • 請求復(fù)用機(jī)制: 當(dāng)前請求完成后,HttpServletRequest 會被回收并返回到請求池中,準(zhǔn)備供下一個請求復(fù)用。在復(fù)用時,Tomcat 會檢查當(dāng)前請求對象的狀態(tài)。如果上一個請求對象的 cookieParsed 被標(biāo)記為“已解析”,則下一個請求在復(fù)用這個請求對象時會跳過 Cookie 的解析步驟,從而導(dǎo)致下一個請求無法正確獲取 Cookie 信息。
  • 標(biāo)志位未重置: 由于在主線程結(jié)束后,cookieParsed 標(biāo)志位被設(shè)置為“已解析”,但異步線程沒有在任務(wù)完成后重置該標(biāo)志位,導(dǎo)致請求對象在復(fù)用時被錯誤地標(biāo)記為已經(jīng)解析過 Cookie。這會直接影響到下一個請求的處理,導(dǎo)致 Cookie 解析失敗,直到該Request再次被回收,再次進(jìn)行Request回收操作,才會正常。

二、問題詳細(xì)分析

1. 場景重現(xiàn)

  • 主線程獲取 HttpServletRequestCookie:主線程在處理 HTTP 請求時,首先從 HttpServletRequest 中解析出 Cookie 信息,并標(biāo)記其解析狀態(tài)。通常,Tomcat 會在請求完成后將請求對象回收。
  • 異步線程啟動:主線程結(jié)束后,將繼續(xù)執(zhí)行異步任務(wù)(例如,長時間的導(dǎo)出任務(wù)),在此過程中,異步線程會繼續(xù)訪問同一個 HttpServletRequest 對象。
  • 請求復(fù)用:由于 Tomcat 對請求對象進(jìn)行復(fù)用,當(dāng)一個請求處理完后,它會將請求對象歸還到池中,以便下一個請求復(fù)用。如果異步線程修改了請求的某些狀態(tài)標(biāo)志(例如標(biāo)記 Cookie 已經(jīng)解析),下一個請求可能會復(fù)用已經(jīng)被修改過的 HttpServletRequest 對象。
  • 數(shù)據(jù)污染問題:由于復(fù)用的請求對象已經(jīng)被標(biāo)記為“Cookie 已解析”,這個狀態(tài)可能會被復(fù)用,導(dǎo)致下一次請求跳過 Cookie 的解析邏輯,導(dǎo)致獲取到的 Cookienull,進(jìn)而影響請求的數(shù)據(jù)處理。

代碼示例:

public String handleRequest(HttpServletRequest request, HttpServletResponse response) {
    // 主線程開始執(zhí)行,解析 Cookie 信息
    String cookieValue = null;
    Cookie[] cookies = request.getCookies();
    if (cookies != null) {
        for (Cookie cookie : cookies) {
            if ("UID".equals(cookie.getName())) {
                cookieValue = cookie.getValue();
                break;
            }
        }
    }
    // 主線程完成后啟動異步線程
    AsyncContext asyncContext = request.startAsync(request, response);
    new Thread(() -> {
        try {
            // 模擬延遲任務(wù)
            Thread.sleep(5000);
            // 異步線程嘗試再次讀取 Cookie,將回收后的request中的 `cookieParsed` 設(shè)置為“已解析”
            String cookieValueFromAsync = request.getCookies()[0].getValue();  
            System.out.println("異步線程中的 cookie: " + cookieValueFromAsync);
            asyncContext.complete();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }).start();
    return "success";
}

問題:

  • 當(dāng)異步線程執(zhí)行時,request已經(jīng)被回收,request.getCookies() 返回的 Cookie 可能會是一個 空數(shù)組 或者是 錯誤的 Cookie。這時,即使請求中存在有效的 Cookie,異步線程依然無法獲取到正確的值。
  • 同時被回收的request已經(jīng)被異步線程標(biāo)記為“Cookie 已解析”,導(dǎo)致下一次復(fù)用該request的請求跳過了 Cookie 的解析邏輯,造成下一次請求的獲取Cookie為空。

2. 問題分析

Tomcat 請求復(fù)用機(jī)制

  • Tomcat 在請求處理結(jié)束后并不會立即銷毀 HttpServletRequest 對象,而是將其放入對象池中以供下一個請求復(fù)用。當(dāng)請求完成后,如果異步線程訪問了 HttpServletRequest,會繼續(xù)使用主線程的請求對象。
  • 如果主線程處理完請求后,已經(jīng)對 HttpServletRequest 標(biāo)記了“Cookie 已解析”,這個狀態(tài)可能會被復(fù)用,導(dǎo)致下一次請求跳過 Cookie 的解析。

異步線程與請求對象狀態(tài)沖突

  • 異步線程和主線程雖然共享同一個 HttpServletRequest 對象,但異步線程修改了請求的狀態(tài)(例如 cookieParsed 標(biāo)志),就會影響其他線程訪問請求數(shù)據(jù)的能力。
  • 這種情況下,下一個請求使用了已經(jīng)標(biāo)記為“Cookie 解析完畢”的請求對象,導(dǎo)致解析失敗。

請求上下文傳遞失敗

  • 在異步線程中,由于線程隔離,主線程中的 HttpServletRequest 無法自動傳遞到異步線程中。即使使用 AsyncContext 來延遲清理請求,HttpServletRequest 中的數(shù)據(jù)也可能無法正確傳遞給異步線程。

請求標(biāo)志和清理機(jī)制

  • Tomcat 使用請求標(biāo)志(如 cookieParsed 或者 requestCompleted)來追蹤請求的狀態(tài),并在請求處理完成后清理請求資源。異步線程和主線程共享同一個請求對象時,可能會意外地修改這些標(biāo)志,影響復(fù)用請求的正確性。
  • 一旦請求進(jìn)入異步模式,Tomcat 會將其狀態(tài)標(biāo)記為“處理完成”,并通過 asyncContext.complete() 延遲清理請求對象。這種延遲清理機(jī)制會讓異步線程繼續(xù)持有原始的請求對象,造成請求標(biāo)志的沖突和數(shù)據(jù)污染。

三、解決方案

為了避免 HttpServletRequest 的狀態(tài)被修改,并正確地將請求上下文傳遞給異步線程,以下是推薦的幾種解決方案。

使用 HttpServletRequestWrapper 創(chuàng)建請求副本

在異步線程中創(chuàng)建請求副本,避免直接操作原始請求對象,從而解決請求復(fù)用問題。

public String handleRequest(HttpServletRequest request, HttpServletResponse response) {
    // 創(chuàng)建請求副本
    HttpServletRequest requestCopy = new HttpServletRequestWrapper(request) {
        @Override
        public Cookie[] getCookies() {
            Cookie[] cookies = super.getCookies();
            // 解析 cookie 或者創(chuàng)建副本
            return cookies;
        }
    };
    AsyncContext asyncContext = request.startAsync(request, response);
    new Thread(() -> {
        try {
            // 在異步線程中使用副本
            String cookieValueFromAsync = requestCopy.getCookies()[0].getValue(); 
            System.out.println("異步線程中的 cookie: " + cookieValueFromAsync);
            asyncContext.complete();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }).start();
    return "success";
}

優(yōu)點(diǎn):通過 HttpServletRequestWrapper 創(chuàng)建的副本確保了異步線程不會直接修改原始請求對象,從而避免了請求復(fù)用時出現(xiàn)數(shù)據(jù)污染。

手動傳遞請求上下文

通過 RequestContextHolder 手動傳遞請求上下文到異步線程,確保異步線程可以訪問主線程的請求數(shù)據(jù)。

public String handleRequest(HttpServletRequest request, HttpServletResponse response) {
    AsyncContext asyncContext = request.startAsync(request, response);
    // 手動傳遞請求上下文到異步線程
    new Thread(() -> {
        try {
            // 設(shè)置當(dāng)前請求上下文
            ServletRequestAttributes attributes = new ServletRequestAttributes(request, response);
            RequestContextHolder.setRequestAttributes(attributes, true);
            // 在異步線程中獲取請求參數(shù)
            String cookieValueFromAsync = request.getCookies()[0].getValue(); 
            System.out.println("異步線程中的 cookie: " + cookieValueFromAsync);
            asyncContext.complete();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            // 清理請求上下文
            RequestContextHolder.resetRequestAttributes();
        }
    }).start();
    return "success";
}

優(yōu)點(diǎn):手動傳遞請求上下文使得異步線程能夠訪問主線程的請求信息,避免了異步線程和主線程的上下文隔離問題。

延遲請求對象的清理

通過 AsyncContext.complete() 延遲請求的清理,避免請求對象在異步線程執(zhí)行期間被回收,從而保持請求數(shù)據(jù)的有效性。

public String handleRequest(HttpServletRequest request, HttpServletResponse response) {
    AsyncContext asyncContext = request.startAsync(request, response);
    new Thread(() -> {
        try {
            // 執(zhí)行異步任務(wù)
            Thread.sleep(5000); // 模擬長時間任務(wù)
            asyncContext.complete(); // 延遲請求清理
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }).start();
    return "success";
}

優(yōu)點(diǎn):通過延遲清理請求對象,確保異步線程可以訪問到有效的請求數(shù)據(jù),避免了請求數(shù)據(jù)在異步任務(wù)執(zhí)行期間被誤清理。

四、總結(jié)

在處理異步線程時,特別是涉及到 HttpServletRequest 等請求對象時,可能會遇到請求復(fù)用和上下文傳遞問題。通過合理地使用請求副本、手動傳遞請求上下文和延遲請求清理等方法,可以有效避免數(shù)據(jù)污染和請求對象復(fù)用問題,從而確保異步任務(wù)中的請求數(shù)據(jù)正確性。

核心問題

  • 請求復(fù)用:Tomcat 會復(fù)用請求對象,導(dǎo)致異步線程訪問到已經(jīng)修改過的請求。
  • 異步線程訪問不到請求數(shù)據(jù):由于請求對象在異步線程執(zhí)行時可能已經(jīng)被清理或標(biāo)記為“完成”,導(dǎo)致訪問不到請求數(shù)據(jù)。

解決方案

  • 使用 HttpServletRequestWrapper 創(chuàng)建請求副本。
  • 手動傳遞請求上下文到異步線程。
  • 延遲請求對象的清理,確保異步線程在執(zhí)行期間能夠訪問到請求數(shù)據(jù)。

到此這篇關(guān)于在 Spring Boot 中使用異步線程時的 HttpServletRequest 復(fù)用問題的文章就介紹到這了,更多相關(guān)Spring Boot 異步線程HttpServletRequest 內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 淺析Java如何在Excel中添加或刪除分頁符

    淺析Java如何在Excel中添加或刪除分頁符

    一份排版精良、分頁合理的報表,不僅能提升專業(yè)度,更能確保信息的有效傳達(dá),本文將向大家介紹如何使用 Spire.XLS for Java 在Java中實(shí)現(xiàn)Excel分頁符的添加與刪除,有需要的可以了解下
    2025-11-11
  • Java多線程之并發(fā)編程的核心AQS詳解

    Java多線程之并發(fā)編程的核心AQS詳解

    AQS ( AbstractQueuedSynchronizer)是一個用來構(gòu)建鎖和同步器的框架,使用AQS能簡單且高效地構(gòu)造出應(yīng)用廣泛的大量的同步器,下面小編和大家來一起學(xué)習(xí)一下吧
    2021-09-09
  • springboot starter自定義實(shí)現(xiàn)公共模塊方式

    springboot starter自定義實(shí)現(xiàn)公共模塊方式

    這篇文章主要介紹了springboot starter自定義實(shí)現(xiàn)公共模塊方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • Java枚舉通過Code獲取相應(yīng)的Value值實(shí)現(xiàn)方式

    Java枚舉通過Code獲取相應(yīng)的Value值實(shí)現(xiàn)方式

    本文介紹了枚舉定義、如何通過code獲取value的方法,并提供了一個完整的代碼示例,通過實(shí)際測試,證明了該方法的有效性,希望本文能夠為讀者提供參考,并鼓勵大家支持腳本之家
    2026-03-03
  • Java 同步關(guān)鍵字 synchronized用法 、場景及避坑指南

    Java 同步關(guān)鍵字 synchronized用法 、場景及避坑指南

    synchronized 是 Java 最基礎(chǔ)、最常用的線程同步方式,優(yōu)點(diǎn)是 簡單易用、無需手動釋放鎖、JVM 自動優(yōu)化,適合解決大多數(shù)線程安全問題,本文給大家介紹Java 同步關(guān)鍵字 synchronized用法 + 場景 + 避坑指南,感興趣的朋友跟隨小編一起看看吧
    2025-12-12
  • 使用springboot aop記錄接口請求的參數(shù)及響應(yīng)

    使用springboot aop記錄接口請求的參數(shù)及響應(yīng)

    該文章介紹了如何使用SpringAOP的切面注解,如@Before和@AfterReturning,來記錄Controller層的方法輸入?yún)?shù)和響應(yīng)結(jié)果,還討論了@Around注解的靈活性,允許在方法執(zhí)行前后進(jìn)行更多控制,需要的朋友可以參考下
    2024-05-05
  • 詳解Java如何判斷一個對象是否為空

    詳解Java如何判斷一個對象是否為空

    我們在剛開始學(xué)習(xí)Java的時候,遇到過最多的異常肯定是臭名昭著的空指針異常(NullPointerException),可以說它陪伴了我們整個初學(xué)階段,那么如何優(yōu)雅的判斷一個對象是否為空并且減少空指針異常呢,
    2024-01-01
  • Spring?Boot?集成PageHelper的使用方法

    Spring?Boot?集成PageHelper的使用方法

    這篇文章主要介紹了Spring?Boot?集成PageHelper的使用方法,文章內(nèi)容圍繞主題展開詳細(xì)介紹,需要的小伙伴可以參考一下,希望對你的學(xué)習(xí)有所幫助
    2022-04-04
  • Spring MVC參數(shù)校驗詳解(關(guān)于`@RequestBody`返回`400`)

    Spring MVC參數(shù)校驗詳解(關(guān)于`@RequestBody`返回`400`)

    這篇文章主要介紹了Spring MVC參數(shù)校驗的相關(guān)資料,主要是針對`@RequestBody`返回`400`的問題,文中通過示例代碼介紹的非常詳細(xì),對大家具有一定的參考學(xué)習(xí)價值,需要的朋友們下面跟著小編來一起學(xué)習(xí)學(xué)習(xí)吧。
    2017-08-08
  • JAVA Spring Boot 自動配置實(shí)現(xiàn)原理詳解

    JAVA Spring Boot 自動配置實(shí)現(xiàn)原理詳解

    這篇文章主要介紹了詳解SpringBoot自動配置原理,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2021-09-09

最新評論

疏附县| 武川县| 浮梁县| 黄冈市| 邹平县| 偃师市| 沛县| 彩票| 综艺| 邹平县| 任丘市| 青神县| 兴城市| 吴旗县| 建瓯市| 江口县| 铁力市| 河源市| 长岭县| 察雅县| 武强县| 汶上县| 陇西县| 巴青县| 崇礼县| 淳安县| 宁都县| 崇阳县| 海口市| 黔东| 莱芜市| 巴楚县| 舒兰市| 永吉县| 东山县| 略阳县| 伊金霍洛旗| 始兴县| 甘孜县| 化州市| 温州市|