踩坑
近日在對一個ASP.NET WEBFORM專案進行調優過程中,偶然發現頁面POSTBACK事件是序列處理的,甚至不同頁面的請求都是序列處理的(一個頁面載入完畢後,才開始載入第二個頁面)。但是網站明明是併發處理請求的啊,當時十分不解,把程式碼翻來覆去排查了大半天,尋找業務框架中什麼地方是不是鎖了資源。各種斷點、除錯、日誌全部用上,還是找不到原因。
仔細思考了一下,ASP.NET肯定不會是序列處理的,難道是某一個Session中,ASP.NET對其序列處理請求?
排雷
在專案中新增一個頁面,只能加最簡單的事件程式碼:
log.Info("start");
System.Threading.Thread.Sleep(5000);
log.Info("end");
瀏覽器中開打兩個標籤頁同時訪問該頁面,結果請求時並行處理的。
16:24:44[9]start
16:24:45[10]start
16:24:49[9]end
16:24:50[10]end
接著先訪問下登陸頁面,然後再重複上次動作,請求變成序列處理了!
16:26:11[9]start
16:26:16[9]end
16:26:16[10]start
16:26:21[10]end
換成兩個瀏覽器,分別登陸後訪問該頁面,請求被並行處理。
自此確認肯定是SESSION的問題!
GOOGLE搜尋了一番,找到原因及解決方案:
The session state module implements a locking mechanism and queues the access to state values. A page that has session-state write access will hold a writer lock on the session until the request finishes. A page gains write access to the session state by setting the EnableSessionState attribute on the @Page directive to True. A page that has session-state read access -- for example, when the EnableSessionState attribute is set to ReadOnly -- will hold a reader lock on the session until the request finishes.
原來SessionStateModule模組實現了一個寫入鎖,並對其進行排隊處理,所以同一SESSION下的所有請求都會被序列執行。
解決方案也很簡單,在頁面上增加EnableSessionState="ReadOnly"指令即可。 當然,增加了這個指令後,這個頁面也就不能修改SESSION值了。
衍生
GOOGLE的過程中,發現.NET MVC同樣也有這個問題,其解決方案是在controller上增加特性[SessionState(SessionStateBehavior.ReadOnly)]
隨手實驗一番,不加以上特性時,在有SESSION的情況下,同SESSION的請求會被序列處理。增加SessionStateBehavior.ReadOnly後不同action中,請求會被並行處理,但是相同action仍然是序列處理的。
即使是Task<ActionResult>
,任然無法逃脫序列執行的魔掌。