前言
解决方案:VS2017创建的Web项目。
说明:尝试理解Session锁的运行机制以及对应的处理操作。
知识点:
1.在后台中进行了Session操作,会在请求结束时生成一个新的SessionId返回给浏览器,并以cookie的形式保存起来。
2.Session锁是针对同一个SessionId,才会发生堵塞。
3.目前使用Controller添加Session.ReadOnly特性的方法,处理
4.如果考虑ajax的性能,需要考虑后台操作Session时导致堵塞的问题。
过程
问题重现
1.页面代码
@{ ViewBag.Title = "Index"; Layout = "~/Views/Shared/_Layout.cshtml"; } <h2>测试Session锁</h2> <script src="~/Scripts/jquery-3.3.1.js"></script> <script type="text/javascript"> $(function () { $('#btn1').on('click', function () { urlAjax("/Pro/Test1", 3); }); $('#btn2').on('click', function () { urlAjax("/Pro/Test2", 3); }); }); </script> <div id="div1" style="100%; height:200px; border:1px solid #000;"> </div> <button id="btn1">不操作Session</button> <button id="btn2">操作Session</button> <script> function urlAjax(url, count) { for (var i = 0; i < count; i++) { $.ajax({ url: url, dataType: "text", async: true, success: function (response) { $("#div1").html($("#div1").html() + response + "<br/>"); } }); } } </script>
using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Web; using System.Web.Mvc; namespace webSession.Controllers { public class ProController : Controller { // GET: Pro public ActionResult Index() { return View(); } public ActionResult Test1() { Thread.Sleep(2000); return Content($"任务Test1完成,{DateTime.Now.ToString("yyyyMMddHHmmssfff")}"); } public ActionResult Test2() { Thread.Sleep(2000); Session["mysession"] = "admin"; return Content($"任务Test1完成,{DateTime.Now.ToString("yyyyMMddHHmmssfff")}"); } } }
2.准备:将浏览器保存的SessionId全部删除
3.运行结果
(1)点击“不操作Session”按钮
结果:
提交三次请求,三个请求同时进行。
说明:
ajax设置异步请求
此时没有SessionId,且没有进行Session操作,所以没有触发Session锁,请求会同时进行。
(2)点击“操作Session”按钮
结果:
提交三次请求,三个请求同时进行。
说明:
ajax设置异步请求
第一次请求是没有SessionId的,但是后台有Session操作,为什么没有触发Session锁呢?
因为SessionId是后台进行了Session操作后,在请求结束后,生成一个新的SessionId返回给浏览器,
而这三个请求是异步进行,在第二个第三个请求发起时,第一个请求并未结束,也就没有获取到,
这三个请求也是会生成三个SessionId。
可以看到这三个请求返回的SessionId不一样,也佐证了上面的描述。
此时,浏览器保存了最后一次请求返回的SessionId,之后再发起请求时就会发生Session锁堵塞的问题了。
(3)再次点击“不操作Session”按钮
结果:
提交三次请求,三个请求发生了堵塞。
说明:
浏览器有保存SessionId,发生了Session堵塞,下一个请求需要等待前一个请求结束才会响应。
(4)奇怪的事情
手贱,编译了下解决方案。然后把运行的页面F5刷新了下。
此时是有SessionId的了,按我的理解此时,无论我调用的接口是否操作Session应该都会堵塞,
但是实际上,我第一次发点击后,2个按钮请求的三个响应,都是同时进行,第二次点击才会被堵塞。
What The Hell ???我这个小朋友,现在有很多问号
解决办法
1.在web.config文件中关闭session
<system.web> <sessionState mode="Off" /> </system.web>
结果:
点击事件:点击“btn1”,点击“btn2”,点击“btn1”,点击“btn2”
未操作Session的接口正常执行,
操作Session的接口提示500,因为禁用了Session,后台抛异常了。
说明:
此解决方案就不能操作Session了,如果进行了Session操作,代码编译不会提示错误,运行时会抛出异常。
2.Controller添加[SessionState(System.Web.SessionState.SessionStateBehavior.ReadOnly)]特性
[SessionState(System.Web.SessionState.SessionStateBehavior.ReadOnly)]
结果:
点击事件:点击“btn1”,点击“btn2”,点击“btn1”,点击“btn2”
都正常执行,且没有被堵塞,操作Session的代码也没有抛异常。
说明:
该Controller无法操作Session,只能读取。
3.改用Customer模式,自己实现会话状态存储提供程序
<sessionState mode="Custom" customProvider="CustomSessionProvider" > <providers> <add name="CustomSessionProvider" type="sessionTest.Extensions.CustomSessionProvider" /> </providers> </sessionState>
此方法未做实现,详情参见