1. 程式人生 > >redis儲存session效能怎麼樣?

redis儲存session效能怎麼樣?

在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管理也是有鎖的開銷,網上的資料已經有很多了:

我的測試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個出現阻塞。效果如圖:

有興趣的同學可以去除錯一下原始碼。