異步處理ServletRequest引起的血案

咱們的APP生產上出了一次比較嚴重的事故,許多用戶投訴登陸後能看到別人的信息,收到投訴後咱們就開始查找問題,通常這樣的問題都是線程安全引發的,因此查找緣由的思路也是按線程安全的思路去查。java

業務場景是這樣的,用戶登陸後,點擊一個頁面查看信息,這個信息顯示了別人的信息。tomcat

登陸交易大體流程以下:安全

//一系列驗證
session.setAttribute("id",id); //證件號放入session
//其餘操做

查看信息交易的流程以下:服務器

session = request.getSession();
if(session != null && session.getAttribute("LoginStatus") == True) {
    String id = session.getAttribute("id");
    Information info = queryInfo(id);
    return info;
} else {
    return "not login";
}

經過加日誌等方法,咱們確認了是查看信息的時候,從session裏拿出來的證件號是其餘人的,可是究竟是在何時變化的,沒找到,由於咱們一直順着線程安全的思路,找全局變量這樣的地方。session

另外還發現有個地方可疑,就是有一個異步的線程,會驗證用戶證件號,而且從新在session裏放一次。curl

public void updateId(HttpServletRequest request) {
    HttpSession session = request.getSession();
    String id = validate();
    session.setAttribute("id",id);
}

這個函數是在登陸主交易調起的線程池處理的,看上去其實沒有多大毛病,並且也是老的代碼,好久了。並且我發現了一個規律,被別人看到信息的用戶,登陸交易都觸發了使用新設備登陸,由於咱們加了一個邏輯,對換設備登陸作了驗證,這樣的話須要驗證短信,登陸分兩步了,第一步比較快的返回了,可是異步的更新信息流程還在。異步

異步處理ServletRequest引起的血案

因而我懷疑是否是由於Servlet的交易已經返回了,異步的線程雖然拿到了HttpServletRequest,可是這個request已經無效了或者被複用了。ide

我寫了下面的代碼進行驗證,不停的用curl調用。Http請求很快就返回了,可是我會把HttpServletRequest傳給一個線程池,等5s後纔會去處理這個Request,結果果真是有問題的函數

異步處理ServletRequest引起的血案

結果果真有問題,大部分狀況new_session都是null,但也出現了不是null的狀況,這時候發現session不是本身的。
異步處理ServletRequest引起的血案
HttpServletRequest是有生命週期的,當一個http請求過來後,應用服務器解析報文,把各類參數放到一個HttpServletRequest對象中,而後傳遞給Servlet的service函數,service函數根據裏面的方法調用對應的doGet/doPost等方法,而一旦service函數調用結束,HttpServletRequest的生命週期就結束了,再這以後你繼續使用這個對象,產生的結果是不肯定的。
異步處理ServletRequest引起的血案
網上遇到這類問題的人很少,我專門找了servlet specification,其中有一章講HttpServletRequest生命週期的。
異步處理ServletRequest引起的血案
從中能夠獲得以下信息:性能

(1)三種狀況下request有效:service函數內,doFilter函數內,startAsync起的異步線程

(2)在三種狀況以外,使用request會產生不肯定的結果(indeterminate results)

(3)大部分容器在實現servlet的時候,爲了提升性能,會複用request對象,但這不是規範裏必須的

其中提到的startAsync是servlet 3.0開始有的,它是爲了讓一個工做線程能夠在作IO或相似阻塞線程的操做的時候能幹其它的事情,可是它要求異步線程都結束了,纔會將請求返回給客戶端,本質上仍是同步的,只是並行了。因此要想異步的處理Request,必須使用servlet本身的異步機制,可是這樣並不能知足咱們的需求,由於咱們就是爲了避免讓主線程等待。

用法示例:
異步處理ServletRequest引起的血案

若是使用了這個,那麼客戶端須要等待5s才能拿到結果。

我又看了tomcat的源碼,發現它確實對Request作了複用:
異步處理ServletRequest引起的血案

雖然問題的緣由很簡單,可是產生的後果十分嚴重。須要異步處理數據的時候必定要特別當心,此處若是傳Session就沒問題了,可是仍是要儘可能避免。

相關文章
相關標籤/搜索