欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

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

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

1. 場(chǎng)景背景

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

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

2. 問(wèn)題根源

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

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

1. 場(chǎng)景重現(xiàn)

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

問(wèn)題:

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

2. 問(wèn)題分析

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

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

異步線程與請(qǐng)求對(duì)象狀態(tài)沖突

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

請(qǐng)求上下文傳遞失敗

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

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

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

三、解決方案

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

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

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

public String handleRequest(HttpServletRequest request, HttpServletResponse response) {
    // 創(chuàng)建請(qǐ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):通過(guò) HttpServletRequestWrapper 創(chuàng)建的副本確保了異步線程不會(huì)直接修改原始請(qǐng)求對(duì)象,從而避免了請(qǐng)求復(fù)用時(shí)出現(xiàn)數(shù)據(jù)污染。

手動(dòng)傳遞請(qǐng)求上下文

通過(guò) RequestContextHolder 手動(dòng)傳遞請(qǐng)求上下文到異步線程,確保異步線程可以訪問(wèn)主線程的請(qǐng)求數(shù)據(jù)。

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

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

延遲請(qǐng)求對(duì)象的清理

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

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

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

四、總結(jié)

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

核心問(wèn)題

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

解決方案

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

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

相關(guān)文章

  • Java環(huán)境配置與編譯運(yùn)行詳解

    Java環(huán)境配置與編譯運(yùn)行詳解

    這篇文章主要為大家詳細(xì)介紹了Java環(huán)境配置與編譯運(yùn)行的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2017-03-03
  • 深入探究Java中的類加載機(jī)制

    深入探究Java中的類加載機(jī)制

    這篇文章主要給大家介紹了關(guān)于Java中類加載機(jī)制的相關(guān)資料,JVM將類加載過(guò)程分為三個(gè)步驟:裝載(Load)、鏈接(Link)和初始化(Initialize),本文通過(guò)示例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2021-09-09
  • idea resources目錄下的application.properties不能自動(dòng)提示問(wèn)題

    idea resources目錄下的application.properties不能自動(dòng)提示問(wèn)題

    這篇文章主要介紹了idea resources目錄下的application.properties不能自動(dòng)提示問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-11-11
  • Spring Cloud Data Flow初體驗(yàn)以Local模式運(yùn)行

    Spring Cloud Data Flow初體驗(yàn)以Local模式運(yùn)行

    這篇文章主要介紹了Spring Cloud Data Flow初體驗(yàn)以Local模式運(yùn)行,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-08-08
  • 基于Java實(shí)現(xiàn)Actor模型

    基于Java實(shí)現(xiàn)Actor模型

    Actor模型是一種常見的并發(fā)模型,與最常見的并發(fā)模型—共享內(nèi)存(同步鎖)不同,它將程序分為許多獨(dú)立的計(jì)算單元—Actor,文中有詳細(xì)的代碼示例,感興趣的同學(xué)可以參考閱讀
    2023-05-05
  • MyBatis類型轉(zhuǎn)換模塊的實(shí)現(xiàn)

    MyBatis類型轉(zhuǎn)換模塊的實(shí)現(xiàn)

    MyBatis是一個(gè)持久層框架ORM框架,實(shí)現(xiàn)數(shù)據(jù)庫(kù)中數(shù)據(jù)和Java對(duì)象中的屬性的雙向映射,那么不可避免的就會(huì)碰到類型轉(zhuǎn)換的問(wèn)題,本文主要介紹了MyBatis類型轉(zhuǎn)換模塊的實(shí)現(xiàn),感興趣的可以了解一下
    2023-09-09
  • Java實(shí)現(xiàn)四則混合運(yùn)算代碼示例

    Java實(shí)現(xiàn)四則混合運(yùn)算代碼示例

    這篇文章主要介紹了Java實(shí)現(xiàn)四則混合運(yùn)算代碼示例,文中展示了詳細(xì)代碼,具有一定參考價(jià)值,需要的朋友可以了解下。
    2017-10-10
  • java HashMap的keyset實(shí)例

    java HashMap的keyset實(shí)例

    簡(jiǎn)單地說(shuō),在keyset方法返回的set上做修改會(huì)改變?cè)瓉?lái)hashmap,這也許不是你想要的,于是形成一個(gè)隱藏的bug
    2013-04-04
  • mybatis-plus中配置日志信息方式

    mybatis-plus中配置日志信息方式

    這篇文章主要介紹了mybatis-plus中配置日志信息方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2025-03-03
  • Java?Optional的使用技巧與最佳實(shí)踐

    Java?Optional的使用技巧與最佳實(shí)踐

    在?Java?中,Optional?是用于優(yōu)雅處理?null?的容器類,其核心目標(biāo)是?顯式提醒開發(fā)者處理空值場(chǎng)景,避免?NullPointerException,本文給大家介紹Java?Optional的使用技巧,感興趣的朋友一起看看吧
    2025-04-04

最新評(píng)論