Skip to content

使用 InProc Session 导致的性能问题

现象

最近生产环境偶尔会卡顿,服务器连接数飙升,ThreadAbortException 异常增多。客户端上页面经常打不开,但是关掉浏览器后再打开就又好了。

根据 会话状态 Session 做了个类似的 Sample,确实会出现性能问题。

在官方文档 Chapter 10 — Improving Web Services Performance # State Management 中查到了相关的描述。

Maintaining session state has an impact on concurrency. If you keep data in session state, Web services calls made by one client are serialized by the ASP.NET runtime. Two concurrent requests from the same client are queued up on the same thread in the Web service — the second request waits until the first request is processed. If you do not use session data in a Web method, you should disable sessions for that method.

Note Enabling session state pins each session to one thread (to protect session data). Concurrent calls from the same session are serialized once they reach the server, so they have to wait for each other, regardless of the number of CPUs.

从上可以看出多个同 Session Id 的请求同时访问时,后执行的请求会等待前面占用 Session 的请求结束后才会执行。(类似于多线程使用同一个实例时的锁等待)

解决方案

  1. MVC 4 在 Controller 上增加 SessionState 注解。

    csharp
    [SessionState(SessionStateBehavior.ReadOnly)]
  2. WebForm 中在 Page 标签中设置 EnableSessionState 属性(该属性有 3 个值:False、ReadOnly 和 True(参考 PagesEnableSessionState Enum))。

    javascript
    EnableSessionState="ReadOnly"
  3. Web.config -> pages 标签中设置 enableSessionState 属性。

    Web.configsystem.web -> pages 标签中设置 enableSessionStateReadOnly 后,所有页面的 Session 都将变成只读,不能保存 Session。即使在页面的代码里向 Session 中赋了值,也不会将 SessionId 设置到客服端(客户端还没有 SessionId 的情况下)。

    xml
    <system.web>
        <pages enableSessionState="ReadOnly">
        </pages>
    </system.web>
  4. 在 Web Services 中通过 WebMethod 特性的 EnableSession 来禁用 Session。

    csharp
    [WebMethod(EnableSession=true)]
    YourWebMethod() { ... }
  5. 如果使用了 WebService,由于 WebMethod 特性仅可以通过 EnableSession 属性设置是否启用 Session,所以无法将其设置为 ReadOnly

    由于我们的项目采用的是通过 SessionId + Redis 来保存用户信息,可以采用如下方法来解决。

    首先,在 Web.config -> pages 中设置 enableSessionStateReadOnly;

    其次,在 Global.asax 中增加 Session_Start 方法。无论页面的处理中是否使用了 Session,总是会将 SessionId 设置到客户端 Cookie(即使在 Web.configenableSessionState 设置为了 ReadOnly);

    csharp
    protected void Session_Start(object sender, EventArgs e)
    {
    
    }

    最后,将所有的 WebMethod 设置成 EnableSession = false,并通过如下方法获取当前请求的 Session Id;

    csharp
    // Get the Web application configuration object.
    System.Configuration.Configuration configuration =
        System.Web.Configuration.WebConfigurationManager.OpenWebConfiguration("/");
    
    // Get the section related object.
    System.Web.Configuration.SessionStateSection sessionStateSection =
        (System.Web.Configuration.SessionStateSection)
        configuration.GetSection("system.web/sessionState");
    
    // Get the session state's cookie name.(configed in Web.config)
    string cookieName = sessionStateSection.CookieName;
    
    // Get the current session id.
    string sessionId = HttpContext.Current.Request.Cookies[sessionStateSection.CookieName].Value;

参考

  1. Asp.net mvc 中处理同一个 session 的并行请求的问题
  2. asp.net Global.asax 注册 session_start 事件后引发的性能问题
  3. WebMethodAttribute.EnableSession Property
  4. Chapter 10 — Improving Web Services Performance
  5. Web Service 是否可以配置类似页面的 EnableSessionState
  6. Page 指令 的 EnableSessionState="ReadOnly",怎么在 web.config 里面设置
  7. 会话状态 Session
  8. 【异常记录 (九)】System.Threading.ThreadAbortException: 正在中止线程
  9. HttpRuntimeSection.ExecutionTimeout Property
  10. 如何设置 ASP.NET 页面的运行超时时间
  11. SessionStateModule Class
  12. PagesSection.EnableSessionState Property
  13. 会话状态事件
  14. 会话状态模式
  15. 性能概述
  16. WebMethodAttribute.EnableSession Property
  17. SessionStateSection Class
  18. SessionStateSection.CookieName Property