在web開發中,Session這個東西一直都很重要,至少伴隨我10年之久, 前一段時間發生一個性能問題,因為Redis session 問題,后來想想 其實我的項目session 是不需要的。
先看看 test 的code吧:
public class HomeController : Controller { public ActionResult Index() { Session["test"] = DateTime.Now; return View(); } } public class TestController : Controller { public ActionResult t1() { //Session["test"] = DateTime.Now; var a = Session["test"]; return new ContentResult() { Content = "t1Action" + DateTime.Now.ToString("HH:mm:ss ffff") }; } public ActionResult t2() { // Session["test"] = DateTime.Now; var a = Session["test"]; return new ContentResult() { Content = "t2Action" + DateTime.Now.ToString("HH:mm:ss ffff") }; } }
<script type="text/javascript" src="~/Scripts/jquery-1.10.2.min.js"></script> <script type="text/javascript"> $(function () { $.get("/test/t1"); $.get("/test/t2"); }); </script>
首先我們用 默認的session, 也就是 web iis 服務器內存管理

用 <sessionState mode="StateServer" stateConnectionString="tcpip=遠程服務器" timeout="20"></sessionState>

用 <sessionState mode="Custom" customProvider="MySessionStateStore">
<providers>
<add name="MySessionStateStore" type="Microsoft.Web.Redis.RedisSessionStateProvider"
host="遠程服務器"
port="6379"
ssl="false"
throwOnError="true"
retryTimeoutInMilliseconds="5000"
databaseId="1"
applicationName="gavinsessiontest1"
connectionTimeoutInMilliseconds="5000"
redisSerializerType="SessionTest.JsonSerializer,SessionTest,Version=1.0.0.0, Culture=neutral"
operationTimeoutInMilliseconds="5000" />
</providers>
</sessionState>

通過這3個圖我們發現, session在本地內存中,2個ajax請求所用時間差不多,用sessionState 和redis 2個ajax請求中第一個正常,第二個明顯要慢。微軟自帶的sessionState 和redis session性能差距不大。
我曾經做過測試 這里就不貼圖了, 直接說結果。 在用redis做session管理的時候,2個ajax 在begin_request時間基本一致,但是到達action的時間就有差距了,就像上面的截圖一樣,相差 在500毫秒左右, 所以差距處在session問題上,所以才有了本文的話題。
我以自己這2年多的項目為例吧,現在單頁面非常流行,同時程序的性能要求越來越高,我們后台開發很多時候都是做的webapi。不像很久以前需要往session里面放dataset、datatable之類的東西, 很多時候就存放一點用戶登錄信息(username,role等一些簡單信息)。 有關用戶登錄和授權大家可以參考我曾經轉載的 3種web會話管理的方式 里面提到的jwt方式。asp.Net jwt 有開源項目 stewartm83/Jwt-WebApi 。
看看開源項目運行效果吧:
login返回token

普通請求 帶上token值:

這里把token放到header里面的,如果是跨域請求 我們還需要注意是否是復雜跨域請求。
項目設計初期 我們真的需要想想我們真的需要用到session來保存我們的會話信息嗎? 用jwt是否可以替代session,如果業務上是可以的話,那我們為什么不用JWT了。
----------------2017-2-22--------------------------------------------------------------
最近在研究redis性能問題,無意發現redis 做sesssion保存比較慢:
如圖 查詢redis慢的語句:

通過這里的 code 我在RedisSessionStateProvider里面找到源碼:

查找該引用的地方是TryTakeWriteLockAndGetData方法,而調用TryTakeWriteLockAndGetData的方法是:

也就是說GetItem 不會執行該LUB腳本,GetItemExclusive會執行該腳本,可是SessionStateModule的code如下:

我在實際調試的過程中發現每次_rqReadonly都是false,也就是說這里的LUA腳本每次都會執行。具體大家可以參考: Asp.net Session認識加強-Session究竟是如何存儲你知道嗎?
-----------------------------------2017-3-7-------------------------------------------------------------------
以上的分析 主要是說GetItemExclusive方法會有鎖(這里LUA腳本),其實后來仔細看了一下 內存的session管理也是有鎖的開銷,網上的資料已經有很多了:
Handling concurrent access to ASP.NET MVC’s session state
My post requests are pending when multiple POST requests occur
Very long “Wait” time on ajax requests MVC4
ASP.NET Concurrent Ajax Requests and Session State Blocking
我的測試code:
public class TestController : Controller { DateTime begin, postmap, prerequest, actioning; public TestController() { begin = DateTime.Parse(System.Web.HttpContext.Current.Items["BeginRequest"].ToString()); postmap = DateTime.Parse(System.Web.HttpContext.Current.Items["PostMapRequestHandler"].ToString()); prerequest = DateTime.Parse(System.Web.HttpContext.Current.Items["PreRequestHandlerExecute"].ToString()); } protected override void OnActionExecuting(ActionExecutingContext filterContext) { actioning = DateTime.Now; } public ActionResult t1() { return new ContentResult() { Content = GetContent("t1Action") }; } public ActionResult t2() { return new ContentResult() { Content = GetContent("t2Action") }; } public ActionResult t3() { return new ContentResult() { Content = GetContent("t3Action") }; } public ActionResult t4() { return new ContentResult() { Content = GetContent("t4Action") }; } string GetContent(string actionName) { //Session["test"] = "123"; //string a = Session["test"].ToString(); string str = $"{actionName}:BeginRequest:" + (DateTime.Now - begin).TotalMilliseconds.ToString(); str += "---PostMapRequestHandler:" + (postmap - begin).TotalMilliseconds.ToString(); str += "---PreRequestHandlerExecute:" + (prerequest - begin).TotalMilliseconds.ToString(); str += "---actioning:" + (actioning - begin).TotalMilliseconds.ToString(); return str; } }
public override void Init() { BeginRequest += MvcApplication_BeginRequest; PostMapRequestHandler += MvcApplication_PostMapRequestHandler; PreRequestHandlerExecute += MvcApplication_PreRequestHandlerExecute; ModelValidatorProviders.Providers.Clear(); } private void MvcApplication_PreRequestHandlerExecute(object sender, EventArgs e) { System.Web.HttpContext.Current.Items["PreRequestHandlerExecute"] = DateTime.Now; } private void MvcApplication_PostMapRequestHandler(object sender, EventArgs e) { System.Web.HttpContext.Current.Items["PostMapRequestHandler"] = DateTime.Now; } private void MvcApplication_BeginRequest(object sender, EventArgs e) { System.Web.HttpContext.Current.Items["BeginRequest"] = DateTime.Now; }
<script type="text/javascript">
$(function () {
$.get("/test/t1");
$.get("/test/t2");
$.get("/test/t3");
$.get("/test/t4");
});
</script>
運行結果如圖:

沒有啟動session 時這幾個ajax請求所發用時間差不多。一旦我們啟用session后,總是有一個或2個出現阻塞。效果如圖:
、
有興趣的同學可以去調試一下源碼。
