這一節翻譯一篇有關Session State性能問題的文章,非一字一句翻譯。瀏覽器
不知道咱們在真實環境中是否用到了Session State特性,它主要用來當在同一瀏覽器發出多個請求時來存儲數據,在如今咱們更多的爲了不整個頁面刷新,Web應用程序更多傾向於利用高擴展性的Ajax,可是不知道咱們是否注意到當咱們使用Session數據屢次請求MVC上的Action方法時產生的性能問題呢?緩存
在進行代碼演示時咱們首先得知道Session的工做原理:當一個新請求第一次到達服務器時,顯然在此以前Cookie中沒有SessionId的,此時服務器將建立一個新的Session標識,經過以下:服務器
System.Web.HttpContext.Current.Session.SessionID
可是並不意味着當有多個請求發送到服務器上時,服務器都會保存一個Session Cookie。只是在Session中保存具體請求的數據,換言之,ASP.NET Framework會首先會添加Session Cookie到響應流中,此時須要保存的數據將被保存在Session中。session
所以,說到這裏好像和咱們要講的主題半毛錢關係都沒有,那跟咱們的性能有什麼關係呢?ASP.NET可以處理來自同一瀏覽器的多個請求,以下:mvc
在上述圖片中,當瀏覽器未發出請求時顯然在服務器上不會存儲任何Session數據,若是服務器存儲了一些數據在Session中,此時將會添加一個Session Cookie到響應流中,接下來全部的子請求使用相同的Session Cookie,同時會以隊列的形式依次等待被處理。app
咱們可以想象到一種很常見的場景,要是多個請求同時去讀取或修改相同的Session值,此時則會形成不一致的數據。接下來進入咱們話題演示時間。dom
咱們在控制器中給出以下代碼:async
[OutputCache(NoStore = true, Duration = 0)] //不緩存數據 public class HomeController : Controller { public List<string> boxes = new List<string>() { "red", "green", "blue", "black", "gray", "yellow", "orange" }; // GET: Home public ActionResult Index() { return View(); } public string GetBox() //隨機獲取集合中顏色 { System.Threading.Thread.Sleep(10); Random rnd = new Random(); int index = rnd.Next(0, boxes.Count); return boxes[index]; } public ActionResult StartSession() //啓動Session並存值 { System.Web.HttpContext.Current.Session["Name"] = "Chris"; return RedirectToAction("Index"); } }
接下來咱們利用AngularJS在視圖中發出Ajax請求以及其餘操做,咱們看看視圖中代碼:性能
<body ng-controller="asyncCtrl" ng-init="getBoxes()"> <nav role="navigation" class="navbar navbar-default navbar-fixed-top"> <div class="container-fluid"> <!-- Brand and toggle get grouped for better mobile display --> <div class="navbar-header"> <button type="button" data-target="#navbarCollapse" data-toggle="collapse" class="navbar-toggle"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> </div> <!-- Collection of nav links and other content for toggling --> <div id="navbarCollapse" class="collapse navbar-collapse"> <ul class="nav navbar-nav"> <li class="active"><a href="#">Performace testing</a></li> <li> @Html.ActionLink("Start Session", "StartSession") </li> <li> <a class="links" ng-click="getBoxes()">Not resolved</a> </li> <li> <a class="links" ng-click="getBoxes(true)">Resolved</a> </li> <li> <form class="navbar-form"> <label class="checkbox" style="margin-top:5px"> @Html.CheckBox("isSessionNewChk", Session.IsNewSession, new { @disabled = "disabled" }) Is Session new </label> </form> </li> </ul> <ul class="nav navbar-nav navbar-right"> <li><a href="#">{{boxes.length}} Boxes</a></li> </ul> </div> </div> </nav> <br /><br /><br /> <div class="container"> <div class="row"> <div id="boxesContainer" ng-repeat="color in boxes track by $index"> <div class="box" ng-class="color" /> </div> </div> <br /> <div class="row"> <div id="timeOccured" ng-show="showResults" class="alert" ng-class="isResolved()" ng-bind="timeElapsed"></div> </div> </div> <script src="~/Scripts/app.js"></script> </body>
接下來咱們看看 app.js ui
angular.module('asyncApp', []) .value('mvcuri', 'http://localhost:49588/home/getbox') .value('mvcurisessionresolved', 'http://localhost:49588/SessionResolved/getbox') .controller('asyncCtrl', function ($http, $scope, mvcuri, mvcurisessionresolved) { $scope.boxes = []; $scope.showResults = false; var uri; $scope.getBoxes = function (resolved) { var start = new Date(); var counter = 300; if (resolved) uri = mvcurisessionresolved; else uri = mvcuri; // Init variables $scope.boxes = []; $scope.showResults = false; $scope.timeElapsed = ''; for (var i = 0; i < 300; i++) { $http.get(uri) .success(function (data, status, headers, config) { $scope.boxes.push(data); counter--; if (counter == 0) { var time = new Date().getTime() - start.getTime(); $scope.timeElapsed = 'Time elapsed (ms): ' + time; $scope.showResults = true; } }) .error(function (error) { $scope.timeElapsed = error.Message; }).finally(function () { }); } }; $scope.isResolved = function () { return uri == mvcuri ? 'alert-danger' : 'alert-success'; } });
上述AngularJS腳本比較簡單就不敘述。接下來再建立一個控制器 SessionResolvedController 來進行比較。
[OutputCache(NoStore = true, Duration = 0)] public class SessionResolvedController : Controller { public List<string> boxes = new List<string>() { "red", "green", "blue", "black", "gray", "yellow", "orange" }; public string GetBox() { try { System.Threading.Thread.Sleep(10); Random rnd = new Random(); int index = rnd.Next(0, boxes.Count); return boxes[index]; } catch(Exception ex) { return "red"; } } }
此時咱們看看運行效果:
當咱們運行程序時,此時複選框是勾上的,說明此時還未有添加數據到Session中,接下來咱們點擊 Start Session 看看效果:
上述咱們是啓動Start Session並控制整發出300個Ajax請求並返回隨機顏色。咱們看到花了8722毫秒。
接下來咱們點擊 Not resolved 看看耗時多少,以下:
耗時8166毫秒,看來和啓動Session沒什麼區別可言。由於每一個到服務器的請求都有一個Session Cookie,此時全部的請求將會被依次處理正如咱們以前所描述的那樣,因此接下來咱們進行以下操做:
[SessionState(SessionStateBehavior.Disabled)] public class SessionResolvedController : Controller {.....}
咱們禁用SessionState看看效果:
注意:上述程序運行建議在Release模式下進行演示,可能這樣的效果更加明顯。
ASP.NET MVC Session state Performance Issue
當有多個請求發送到服務器時此時若進行Session操做將會對性能產生必定影響。咱們經過設置 [SessionState(SessionStateBehavior.Disabled)] 特性最終驗證了這一觀點。可是這樣設置後咱們將沒法獲取Session中的值。因此在請求數量較多的狀況下,建議對於Ajax請求使用Web APi來完成,咱們經過Web APi來接收Ajax請求,可使用Session中的數據來渲染視圖或者提交到數據或者參數到MVC控制器的Action方法上。