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

Tomcat Request Cookie 丟失問(wèn)題解決

 更新時(shí)間:2024年09月11日 09:00:26   作者:程序猿進(jìn)階  
生產(chǎn)環(huán)境多線程處理時(shí)偶發(fā)性出現(xiàn)前端Cookie為空告警,導(dǎo)致請(qǐng)求失敗,問(wèn)題與Tomcat的Request復(fù)用和ThreadLocal使用缺陷有關(guān),本文就來(lái)介紹一下如何解決,感興趣的可以了解一下

一、問(wèn)題描述

生產(chǎn)環(huán)境偶爾(涉及到多線程處理)出現(xiàn)"前端傳遞`Cookie為空"的告警,導(dǎo)致前端請(qǐng)求丟失,出現(xiàn)請(qǐng)求失敗問(wèn)題。告警內(nèi)容如下

前端傳遞Cookie為空
告警內(nèi)容:服務(wù)端獲取request Cookie為空,請(qǐng)盡快處理?。?!
AppId:xxxxxx
ip:xx.xx.xxx.xx
告警事件:2024-03-15

背景:為什么要加Cookie告警:項(xiàng)目出海,需要保證多語(yǔ)言,語(yǔ)言信息從Cookie中獲取,所以添加了Cookie告警,告警后發(fā)到工作群中,但是相關(guān)開(kāi)發(fā)人員告知自己能夠正常訪問(wèn),沒(méi)有問(wèn)題,因?yàn)檎弥芪?,自己覺(jué)得偶發(fā)性肯定和并發(fā)相關(guān),所以周末研究了下代碼,發(fā)現(xiàn)和Tomcat Rquest復(fù)用機(jī)制和ThreadLocal的使用存在缺陷,導(dǎo)致這個(gè)偶發(fā)性問(wèn)題

在分析原因前,先需要搞懂一個(gè)概念:requesttomcat里面是循環(huán)使用的

二、Tomcat 中 Reqeust 復(fù)用機(jī)制

Request對(duì)象的復(fù)用機(jī)制是為了提高性能和減少垃圾收集壓力而設(shè)計(jì)的。Tomcat使用了一種對(duì)象池的機(jī)制來(lái)管理Request對(duì)象和Response對(duì)象。通過(guò)復(fù)用這些對(duì)象,Tomcat可以避免頻繁地創(chuàng)建和銷(xiāo)毀對(duì)象,從而提高系統(tǒng)的效率。

復(fù)用機(jī)制的工作原理【1】對(duì)象池:Tomcat維護(hù)一個(gè)對(duì)象池,用于存儲(chǔ)Request對(duì)象和Response對(duì)象。當(dāng)一個(gè)新的HTTP請(qǐng)求到達(dá)時(shí),Tomcat從對(duì)象池中獲取一個(gè)空閑的Request對(duì)象和Response對(duì)象。如果對(duì)象池中沒(méi)有空閑的對(duì)象,Tomcat會(huì)創(chuàng)建新的對(duì)象。簡(jiǎn)單看個(gè)案例:

public class RequestPool {
    private Stack<Request> pool = new Stack<>();
    // 獲取對(duì)象:getRequest 方法從對(duì)象池中獲取一個(gè) Request 對(duì)象。如果對(duì)象池為空,則創(chuàng)建一個(gè)新的 Request 對(duì)象。
    public Request getRequest() {
        if (pool.isEmpty()) {
            return new Request();
        } else {
            return pool.pop();
        }
    }
    // 釋放對(duì)象:releaseRequest 方法將 Request 對(duì)象重置(調(diào)用 recycle 方法)并放回對(duì)象池中。
    public void releaseRequest(Request request) {
        request.recycle();
        pool.push(request);
    }
}

【2】對(duì)象重置:當(dāng)一個(gè)請(qǐng)求處理完畢后,Request對(duì)象會(huì)被重置(通過(guò)調(diào)用recycle方法),以清除上一次請(qǐng)求的狀態(tài),使其可以安全地用于下一個(gè)請(qǐng)求。以下是org.apache.catalina.connector.Request類(lèi)中recycle方法的簡(jiǎn)化源碼和解釋?zhuān)?/p>

public class Request {
    // Various fields representing the state of the request
    private String protocol;
    private String method;
    private String requestURI;
    private String queryString;
    private String remoteAddr;
    private String remoteHost;
    private String serverName;
    private int serverPort;
    private boolean secure;
    private InputStream inputStream;
    private Reader reader;
    private ServletInputStream servletInputStream;
    private BufferedReader bufferedReader;
    private Map<String, Object> attributes;
    private Map<String, String[]> parameters;
    private Cookie[] cookies;
    private HttpSession session;

    // Other fields and methods...

    /**
     * Recycle this request object.
     */
    public void recycle() {
        // Reset the state of the request object
        // 重置基本屬性:recycle 方法將 Request 對(duì)象的基本屬性(如 protocol、method、requestURI 等)重置為初始狀態(tài)(通常為 null 或默認(rèn)值)。
        // 清空集合和數(shù)組:attributes 和 parameters 集合被清空,以確保沒(méi)有殘留的請(qǐng)求數(shù)據(jù)。cookies 數(shù)組也被重置為 null。
        // 重置流和讀者:inputStream、reader、servletInputStream 和 bufferedReader 被重置為 null,以確保沒(méi)有殘留的輸入流和讀者對(duì)象。
        // 重置會(huì)話:session 被重置為 null,以確保沒(méi)有殘留的會(huì)話信息。
        protocol = null;
        method = null;
        requestURI = null;
        queryString = null;
        remoteAddr = null;
        remoteHost = null;
        serverName = null;
        serverPort = 0;
        secure = false;
        inputStream = null;
        reader = null;
        servletInputStream = null;
        bufferedReader = null;
        attributes.clear();
        parameters.clear();
        cookies = null;
        session = null;

        // Other reset logic...
    }
}

recycle執(zhí)行的時(shí)機(jī): recycle方法在Tomcat源碼中的調(diào)用時(shí)機(jī)主要是在請(qǐng)求處理完畢之后,Request對(duì)象被返回到對(duì)象池之前。具體來(lái)說(shuō),recycle方法通常在以下幾個(gè)場(chǎng)景中被調(diào)用:
【1】請(qǐng)求處理完畢后:在Tomcatorg.apache.coyote.Request類(lèi)中,recycle方法通常在請(qǐng)求處理完畢后被調(diào)用。例如,在AbstractProcessorLight類(lèi)中處理請(qǐng)求和響應(yīng)的邏輯中,recycle方法被調(diào)用來(lái)重置Request對(duì)象。

// org.apache.coyote.AbstractProcessorLight
public class AbstractProcessorLight<S> implements Processor {
    // Various fields and methods...

    @Override
    public SocketState process(SocketWrapperBase<S> socketWrapper, SocketEvent status) throws IOException {
        // Process the request and response
        try {
            // Request processing logic...
        } finally {
            // Recycle the request and response objects
            request.recycle();
            response.recycle();
        }
        return SocketState.CLOSED;
    }
}

【2】連接關(guān)閉時(shí):在Tomcatorg.apache.coyote.http11.Http11Processor類(lèi)中,當(dāng)連接關(guān)閉時(shí),recycle方法也會(huì)被調(diào)用。例如,當(dāng)處理完一個(gè)請(qǐng)求并決定關(guān)閉連接時(shí),會(huì)調(diào)用recycle方法。

// org.apache.coyote.http11.Http11Processor
public class Http11Processor extends AbstractProcessorLight<SocketChannel> {
    // Various fields and methods...

    @Override
    public SocketState service(SocketWrapperBase<SocketChannel> socketWrapper) throws IOException {
        // Service the request and response
        try {
            // Request servicing logic...
        } finally {
            // Recycle the request and response objects
            request.recycle();
            response.recycle();
        }
        return SocketState.CLOSED;
    }
}

【3】異常處理:在處理請(qǐng)求的過(guò)程中,如果發(fā)生異常,Tomcat也會(huì)確保調(diào)用recycle方法來(lái)重置Request對(duì)象。例如:

// org.apache.coyote.http11.Http11Processor
public class Http11Processor extends AbstractProcessorLight<SocketChannel> {
    // Various fields and methods...

    @Override
    public SocketState service(SocketWrapperBase<SocketChannel> socketWrapper) throws IOException {
        try {
            // Request servicing logic...
        } catch (Exception e) {
            // Handle exception and recycle request
            request.recycle();
            response.recycle();
            throw e;
        }
    }
}

后期原因分析中需要使用到RequestFacade,這里解釋下RequestFacadeRequest之間的關(guān)系:RequestFacade是一個(gè)包裝類(lèi)Facade,用于保護(hù)底層的Request對(duì)象,確保應(yīng)用程序無(wú)法直接訪問(wèn)和修改內(nèi)部實(shí)現(xiàn)細(xì)節(jié)。

【1】Request類(lèi): Request類(lèi)是Tomcat內(nèi)部用來(lái)表示HTTP請(qǐng)求的類(lèi),包含了請(qǐng)求的所有詳細(xì)信息。該類(lèi)提供了許多方法來(lái)訪問(wèn)和操作請(qǐng)求的各個(gè)部分,例如請(qǐng)求頭、請(qǐng)求參數(shù)、輸入流等。

【2】RequestFacade 類(lèi): RequestFacade類(lèi)是一個(gè)包裝器,用于保護(hù)Request對(duì)象。它實(shí)現(xiàn)了javax.servlet.http.HttpServletRequest接口,并將方法調(diào)用委托給內(nèi)部的Request對(duì)象。通過(guò)使用RequestFacade,Tomcat確保了應(yīng)用程序只能通過(guò)標(biāo)準(zhǔn)的HttpServletRequest接口訪問(wèn)請(qǐng)求數(shù)據(jù),而不能直接訪問(wèn)或修改Request對(duì)象的內(nèi)部實(shí)現(xiàn)。

具體實(shí)現(xiàn):在Tomcat中,RequestFacade類(lèi)通常包含一個(gè)Request對(duì)象的引用,并將所有的接口方法調(diào)用委托給這個(gè)內(nèi)部的Request對(duì)象。例如:

// org.apache.catalina.connector.RequestFacade
public class RequestFacade implements HttpServletRequest {
    private final Request request;

    public RequestFacade(Request request) {
        this.request = request;
    }

    @Override
    public String getParameter(String name) {
        return request.getParameter(name);
    }

    // Other methods from HttpServletRequest interface
    // All methods delegate to the internal Request object
}

使用場(chǎng)景:在Tomcat處理請(qǐng)求的過(guò)程中,當(dāng)需要將HttpServletRequest對(duì)象傳遞給應(yīng)用程序時(shí),Tomcat會(huì)創(chuàng)建一個(gè)RequestFacade實(shí)例,并將內(nèi)部的Request對(duì)象傳遞給它。例如

// org.apache.catalina.connector.CoyoteAdapter
public class CoyoteAdapter implements Adapter {
    @Override
    public void service(org.apache.coyote.Request req, org.apache.coyote.Response res) throws Exception {
        Request request = (Request) req.getNote(ADAPTER_NOTES);
        Response response = (Response) res.getNote(ADAPTER_NOTES);

        // Create a RequestFacade to pass to the application
        HttpServletRequest requestFacade = request.getRequest();

        // Pass the RequestFacade to the application
        context.getPipeline().getFirst().invoke(requestFacade, response);
    }
}

三、原因分析

【1】第一次請(qǐng)求由線程A正常執(zhí)行,執(zhí)行完成后執(zhí)行recycle方法,將RequestFacade中的屬性修改為null,準(zhǔn)備下次復(fù)用,但是當(dāng)前線程的ThreadLocal沒(méi)有被清理。

【2】第二次請(qǐng)求恰好也由線程A執(zhí)行(這也是偶發(fā)的原因),通過(guò)ThreadLocal獲取RequestFacade對(duì)象,并通過(guò)getCookies獲取Cookie,因?yàn)榈谝淮握?qǐng)求結(jié)束后將Cookie置為null并將cookiesParsed修改為了false,但是這次請(qǐng)求再次調(diào)用getCookies的時(shí)候,將cookiesParsed修改為了true。用來(lái)表示RequestFacade ACookies已經(jīng)被解析過(guò)了。同時(shí)需要注意,此時(shí)第一次請(qǐng)求的生命周期已經(jīng)結(jié)束了,所以重置cookiesParsed的操作就不復(fù)存在了,Tomcat重新復(fù)用RequestFacade A的時(shí)候Cookies就會(huì)獲取到一個(gè)null。

@Override
public Cookie[] getCookies() {
    if (!cookiesParsed) {
        parseCookies();
    }
    return cookies;
}

protected void parseCookies() {
    cookiesParsed = true;

    Cookies serverCookies = coyoteRequest.getCookies();
    int count = serverCookies.getCookieCount();
    if (count <= 0) {
        returnl
    }

    cookies = new Cookie[count];
}

【3】第三次請(qǐng)求時(shí),Tomcat復(fù)用了RequestFacade A,當(dāng)正常解析Cookies的時(shí)候發(fā)現(xiàn)cookiesParsedtrue就跳過(guò)了正確解析的環(huán)節(jié),當(dāng)需要使用Cookie的時(shí)候發(fā)現(xiàn)為空,本次請(qǐng)求直接被中止。(靈異事件)

解決方案:

【1】ThreadLocal使用完后一定需要clean
【2】不要在跨線程中使用request對(duì)象。可以使用-Dorg.apache.catalina.connector.RECYCLE_FACADES=true禁止復(fù)用。在項(xiàng)目的extraenv.sh中設(shè)置參數(shù)后,如果有訪問(wèn)已經(jīng)被回收的request對(duì)象,就會(huì)拋出The request object has been recycled and is no longer associated with this facade異常,以此就能定位到問(wèn)題

到此這篇關(guān)于Tomcat Request Cookie 丟失問(wèn)題解決的文章就介紹到這了,更多相關(guān)Tomcat Request Cookie 丟失內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家! 

您可能感興趣的文章:

相關(guān)文章

  • tomcat部署jenkins項(xiàng)目的實(shí)現(xiàn)示例

    tomcat部署jenkins項(xiàng)目的實(shí)現(xiàn)示例

    Jenkins自動(dòng)化部署可以解決集成、測(cè)試、部署等重復(fù)性的工作,本文主要介紹了tomcat部署jenkins項(xiàng)目,具有一定的參考價(jià)值,感興趣的可以了解一下
    2023-11-11
  • 修改Tomcat默認(rèn)訪問(wèn)根目錄的方法

    修改Tomcat默認(rèn)訪問(wèn)根目錄的方法

    這篇文章主要介紹了修改Tomcat默認(rèn)訪問(wèn)根目錄的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2018-05-05
  • tomcat?集群監(jiān)控與彈性伸縮詳解

    tomcat?集群監(jiān)控與彈性伸縮詳解

    這篇文章主要為大家介紹了tomcat?集群監(jiān)控與彈性伸縮詳解,
    2022-09-09
  • Tomcat實(shí)現(xiàn)熱部署

    Tomcat實(shí)現(xiàn)熱部署

    本篇文章主要介紹了Tomcat熱部署的概念、好處與實(shí)現(xiàn)方式,具有很好的參考價(jià)值,下面跟著小編一起來(lái)看下吧
    2017-02-02
  • Java Tomcat 啟動(dòng)閃退問(wèn)題解決集

    Java Tomcat 啟動(dòng)閃退問(wèn)題解決集

    Tomcat 啟動(dòng)時(shí)出現(xiàn)黑屏一閃而過(guò)的現(xiàn)象原因有很多,這篇文章就詳細(xì)介紹了tomcat啟動(dòng)閃退問(wèn)題的一些解決方法,感興趣的同學(xué)可以仔細(xì)閱讀
    2023-03-03
  • tomcat服務(wù)安裝步驟及詳細(xì)配置實(shí)戰(zhàn)教程

    tomcat服務(wù)安裝步驟及詳細(xì)配置實(shí)戰(zhàn)教程

    Tomcat是由Apache開(kāi)發(fā)的一個(gè)開(kāi)源Java WEB應(yīng)用服務(wù)器,下面這篇文章主要給大家介紹了關(guān)于tomcat服務(wù)安裝步驟及詳細(xì)配置實(shí)戰(zhàn)教程,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-12-12
  • Tomcat 類(lèi)加載器的實(shí)現(xiàn)方法及實(shí)例代碼

    Tomcat 類(lèi)加載器的實(shí)現(xiàn)方法及實(shí)例代碼

    這篇文章主要介紹了Tomcat 類(lèi)加載器的實(shí)現(xiàn)方法及實(shí)例代碼,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值 ,需要的朋友可以參考下
    2019-05-05
  • 解決Tomcat?Caused?by:?java.lang.ClassNotFoundException:?java.util.logging.Logger的問(wèn)題

    解決Tomcat?Caused?by:?java.lang.ClassNotFoundException:?ja

    這篇文章主要給大家介紹了如何解決Tomcat?Caused?by:?java.lang.ClassNotFoundException:?java.util.logging.Logger的問(wèn)題,文中有詳細(xì)的原因分析及解決方法,需要的朋友可以參考下
    2023-10-10
  • Tomcat+Mysql高并發(fā)配置優(yōu)化講解

    Tomcat+Mysql高并發(fā)配置優(yōu)化講解

    今天小編就為大家分享一篇關(guān)于Tomcat+Mysql高并發(fā)配置優(yōu)化講解,小編覺(jué)得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧
    2019-03-03
  • 淺談Tomcat亂碼與端口占用的解決方案

    淺談Tomcat亂碼與端口占用的解決方案

    這篇文章主要介紹了淺談Tomcat亂碼與端口占用的解決方案,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-11-11

最新評(píng)論