ASP.NET MVC之Session State性能問題(七)

前言

這一節翻譯一篇有關Session State性能問題的文章,非一字一句翻譯。瀏覽器

話題

不知道咱們在真實環境中是否用到了Session State特性,它主要用來當在同一瀏覽器發出多個請求時來存儲數據,在如今咱們更多的爲了不整個頁面刷新,Web應用程序更多傾向於利用高擴展性的Ajax,可是不知道咱們是否注意到當咱們使用Session數據屢次請求MVC上的Action方法時產生的性能問題呢?緩存

將Session放入上下文中(Put Session into the Context)

在進行代碼演示時咱們首先得知道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方法上。

相關文章
相關標籤/搜索