redis儲存session效能怎麼樣?
在web開發中,Session這個東西一直都很重要,至少伴隨我10年之久, 前一段時間發生一個性能問題,因為Redis session 問題,後來想想 其實我的專案session 是不需要的。
先看看 test 的code吧:
public class HomeController : Controller { public ActionResult Index() { Session["test"] = DateTime.Now; return View(); } } publicclass 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管理也是有鎖的開銷,網上的資料已經有很多了:
我的測試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 () {(function () {.get("/test/t1");
.get("/test/t2");.get("/test/t2");.get("/test/t3");
$.get("/test/t4");
});
</script>
執行結果如圖:
沒有啟動session 時這幾個ajax請求所發用時間差不多。一旦我們啟用session後,總是有一個或2個出現阻塞。效果如圖:
、
有興趣的同學可以去除錯一下原始碼。