踩坑
近日在对一个ASP.NET WEBFORM项目进行调优过程中,偶然发现页面POSTBACK事件是串行处理的,甚至不同页面的请求都是串行处理的(一个页面加载完毕后,才开始加载第二个页面)。但是网站明明是并发处理请求的啊,当时十分不解,把代码翻来覆去排查了大半天,寻找业务框架中什么地方是不是锁了资源。各种断点、调试、日志全部用上,还是找不到原因。
仔细思考了一下,ASP.NET肯定不会是串行处理的,难道是某一个Session中,ASP.NET对其串行处理请求?
排雷
在项目中新增一个页面,只能加最简单的事件代码:
log.Info("start");
System.Threading.Thread.Sleep(5000);
log.Info("end");
浏览器中开打两个标签页同时访问该页面,结果请求时并行处理的。
16:24:44[9]start
16:24:45[10]start
16:24:49[9]end
16:24:50[10]end
接着先访问下登陆页面,然后再重复上次动作,请求变成串行处理了!
16:26:11[9]start
16:26:16[9]end
16:26:16[10]start
16:26:21[10]end
换成两个浏览器,分别登陆后访问该页面,请求被并行处理。
自此确认肯定是SESSION的问题!
GOOGLE搜索了一番,找到原因及解决方案:
The session state module implements a locking mechanism and queues the access to state values. A page that has session-state write access will hold a writer lock on the session until the request finishes. A page gains write access to the session state by setting the EnableSessionState attribute on the @Page directive to True. A page that has session-state read access -- for example, when the EnableSessionState attribute is set to ReadOnly -- will hold a reader lock on the session until the request finishes.
原来SessionStateModule模块实现了一个写入锁,并对其进行排队处理,所以同一SESSION下的所有请求都会被串行执行。
解决方案也很简单,在页面上增加EnableSessionState="ReadOnly"指令即可。 当然,增加了这个指令后,这个页面也就不能修改SESSION值了。
衍生
GOOGLE的过程中,发现.NET MVC同样也有这个问题,其解决方案是在controller上增加特性[SessionState(SessionStateBehavior.ReadOnly)]
随手实验一番,不加以上特性时,在有SESSION的情况下,同SESSION的请求会被串行处理。增加SessionStateBehavior.ReadOnly后不同action中,请求会被并行处理,但是相同action仍然是串行处理的。
即使是Task<ActionResult>,任然无法逃脱串行执行的魔掌。