一、感慨 很久前看到一篇博客中有句話大致的意思是:“asp.net 程式性能低下的主要原因是開發人員技術參差不齊”,當時看到這句話不以為然,然而時間過的越久接觸的.net 開發人員越多就越認同這句話;特別最近發現非常一個成熟的項目中有些問題非常非常影響性能,最終影響的是用戶體驗,藉此給大家分享一下關 ...
一、感慨
很久前看到一篇博客中有句話大致的意思是:“asp.net 程式性能低下的主要原因是開發人員技術參差不齊”,當時看到這句話不以為然,然而時間過的越久接觸的.net 開發人員越多就越認同這句話;特別最近發現非常一個成熟的項目中有些問題非常非常影響性能,最終影響的是用戶體驗,藉此給大家分享一下關於asp.net中一個小小的點,但對項目有很大的性能提升;以前覺得自己接觸的項目少小,然後接觸的項目越多,越大就會越發現,同樣的問題依舊存在;
二、先從最簡單的asp.net mvc例子說起
1、Controller
public class HomeController : Controller { public ActionResult About() { return View(); } public ActionResult TestAjax() { Thread.Sleep(1500); return Json(new { Code = 1, LoginId = 0 }); } }
Controller里有兩個Action,一個是顯示的頁面,一個是需要請求的ajax,其中Thread.Sleep(1500)簡單模擬業務處理時間;
2、View
@{ ViewBag.Title = "About"; } @section scripts{ <script type="text/javascript"> $(function () { $("#btn-request").click(function () { $("#result").empty(); for (var i = 0; i < 5; i++) { $.ajax({ url: "/Home/TestAjax", type: "post", success: function (x) { $("#result").append("<div></div>") } }); } }); }); </script> } <h3> 模擬操作 </h3> <div> <input type="button" value="模擬請求" id="btn-request" /> </div> <h3> 結果 </h3> <div id="result"> </div>
頁面中更加簡單,一個按鈕同時請求5次Ajax,用於模擬頁面的多次非同步請求;
3、點擊按鈕顯示效果
結果和我們想象的一樣,5個非同步請求同時在1500毫秒左右響應;這個時候沒什麼問題;
4、添加一個簡單的登錄
1、添加登錄Controller
public class LoginController : Controller { public ActionResult Index() { Session["LoginId"] = 1; return Json(new { Code = 1 }, JsonRequestBehavior.AllowGet); } }
2、修改View,添加一個按鈕模擬Ajax登錄
@section scripts{ <script type="text/javascript"> $(function () { //...其他代碼... $("#btn-login").click(function () { $.ajax({ url: "/login", type: "post", success: function (x) { alert('登錄成功!'); } }); }); }); </script> } ...其他代碼... <div> <input type="button" value="模擬登錄" id="btn-login" /> </div> ...其他代碼...
3、 登錄以後的模擬結果
結果很明顯了,登錄以後,雖然前端是同時發送了5個Ajax請求,結果卻每隔1500毫秒返回一個請求;或者可以說後端做了同步的處理;
對於這個問題,寫到這已經很明顯了;Session的鎖預設對同一個Session做了同步處理;不管前端如何非同步請求,怎乃後端一直同步;
在開發中很多人遇到過,一個請求卡住了,導致其他所有ajax包括頁面請求全部卡主沒有任何反應。解決的方式要麼重啟瀏覽器,要麼清掉cookie;這個演示也同時解釋了遇到的這個問題;
4、使用Session後同步的原因
其實原因很簡單,試想一下,如果1個以上請求同時修改Session的值,那麼Session的值就會有不確定性,這個應該很好理解;為了保證Session值的準確性,只能同一個Session加上鎖同步操作;
三、解決Session同步執行方法
1、使用 Attribute [SessionState(SessionStateBehavior.ReadOnly)]
既然導致同步執行的原因是修改Session導致的,那麼在不需要修改Session的請求中使用只讀Session就可以解決問題了;
[SessionState(SessionStateBehavior.ReadOnly)] public class HomeController : Controller { public ActionResult About() { return View(); } public ActionResult TestAjax() { Thread.Sleep(1500); return Json(new { Code = 1, LoginId = Session["LoginId"] }); } }
只需要在Controller上加上特性SessionState,設置值為ReadOnly;這樣同一個用戶的請求也可以是非同步的;
在一般的項目開發中,Session修改添加操作會在用戶登錄的時候使用,那麼在除了登錄的請求中加入Session ReadOnly,就會根本解決問題;或者籠統的說,在不需要修改Session的請求中加入Session ReadOnly,你的項目性能會有很大的提升;
這種方式對於老項目存在同樣的問題,或者習慣使用Session的用戶是不錯的選擇;
2、 使用JWT等方式
既然Session存在這個問題,那麼可以使用其他方式替代Session,JWT就是一個很不錯的方式,特別現在前後端分離的主流下,JWT更是絕佳的選擇。詳情可以參考 https://jwt.io/
四、總結
1、如果你的項目正在使用Session,而且並未設置Session ReadOnly,簡單的設置會對你項目性能有很大的提升;
2、asp.net webform存在同樣的問題;
3、新項目選擇非Session也是不錯的選擇;
4、從asp.net core 2.1開始,使用Session並不會有此問題,同時兼顧了Session的使用習慣也不會導致同步的性能問題;asp.net 2.1的解決方式類jwt方式;
5、源碼 點擊下載
記得推薦 ^_^
系列課程
- [asp.net mvc 奇淫巧技] 01 - 封裝上下文 - 在View中獲取自定義的上下文
- [asp.net mvc 奇淫巧技] 02 - 巧用Razor引擎在Action內生成Html代碼
- [asp.net mvc 奇淫巧技] 03 - 枚舉特性擴展解決枚舉命名問題和支持HtmlHelper
- [asp.net mvc 奇淫巧技] 04 - 你真的會用Action的模型綁定嗎?
- [asp.net mvc 奇淫巧技] 05 - 擴展ScriptBundle,支持混淆加密javascript
- [asp.net mvc 奇淫巧技] 06 - 也許你的項目同一個用戶的請求都是同步的