为什么Global.asax.cs中的Session_Start会导致性能问题?

发布于 2024-10-07 22:45:12 字数 1091 浏览 2 评论 0原文

当我在 Global.asax.cs 中创建一个空的 Session_Start 处理程序时,在将页面渲染到浏览器时会导致严重的影响。

如何重现:

创建一个空的 ASP.NET MVC 3 Web 应用程序(我使用的是 MVC 3 RC2)。 然后使用以下代码添加一个 Home 控制器:

public class HomeController : Controller
{
  public ActionResult Index()
  {
    return View();
  }
  public ActionResult Number(int id)
  {
    return Content(id.ToString());
  }
}

接下来创建一个视图 Home/Index.cshtml 并将以下内容放入 BODY 部分:

@for (int n = 0; n < 20; n++)
{ 
  <iframe src="@Url.Content("~/Home/Number/" + n)" width=100 height=100 />
}

当您运行此页面时,您将看到页面上出现 20 个 IFRAME,每个 IFRAME 中都有一个数字。我在这里所做的只是创建一个页面,在幕后加载另外 20 个页面。在继续之前,请记下这 20 个页面的加载速度(刷新页面几次以重复加载)。

接下来转到 Global.asax.cs 并添加此方法(是的,方法主体为空):

protected void Session_Start()
{
}

现在再次运行该页面。这次您会注意到 20 个 IFRAME 的加载速度要慢得多,一个接一个的间隔大约为 1 秒。这很奇怪,因为我们实际上并没有在 Session_Start 中执行任何操作...它只是一个空方法。但这似乎足以导致所有后续页面的速度变慢。

有谁知道为什么会发生这种情况,更好的是有人有修复/解决方法吗?

更新

我发现这种行为仅在附加调试器(使用 F5 运行)时发生。如果您在没有附加调试器(Ctrl-F5)的情况下运行它,那么它似乎没问题。所以,也许这不是一个严重的问题,但仍然很奇怪。

When I create an empty Session_Start handler in Global.asax.cs it causes a significant hit when rendering pages to the browser.

How to reproduce:

Create an empty ASP.NET MVC 3 web application (I am using MVC 3 RC2).
Then add a Home controller with this code:

public class HomeController : Controller
{
  public ActionResult Index()
  {
    return View();
  }
  public ActionResult Number(int id)
  {
    return Content(id.ToString());
  }
}

Next create a view Home/Index.cshtml and place the following in the BODY section:

@for (int n = 0; n < 20; n++)
{ 
  <iframe src="@Url.Content("~/Home/Number/" + n)" width=100 height=100 />
}

When you run this page, you'll see 20 IFRAMEs appear on the page, each with a number inside it. All I'm doing here is creating a page that loads 20 more pages behind the scenes. Before continuing, take note of how quickly those 20 pages load (refresh the page a few times to repeat the loads).

Next go to your Global.asax.cs and add this method (yes, the method body is empty):

protected void Session_Start()
{
}

Now run the page again. This time you'll notice that the 20 IFRAMEs load much slower, one after the other about 1 second apart. This is strange because we're not actually doing anything in Session_Start ... it's just an empty method. But this seems to be enough to cause the slowdown in all subsequent pages.

Does anybody know why this is happening, and better yet does anybody have a fix/workaround?

Update

I've discovered that this behavior only occurs when the debugger is attached (running with F5). If you run it without the debugger attached (Ctrl-F5) then it seems to be ok. So, maybe it's not a significant problem but it's still strange.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

两相知 2024-10-14 22:45:12

tl;dr:如果您在使用 Webforms 时遇到此问题,并且不需要对该特定页面中的会话状态进行写访问,请将 EnableSessionState="ReadOnly" 添加到您的 @Page 指令有帮助。


显然,仅 Session_Start 的存在就迫使 ASP.NET 按顺序执行源自同一会话的所有请求。但是,如果您不需要会话的写访问权限,则可以逐页修复此问题(见下文)。

我已经使用 Webforms 创建了自己的测试设置,它使用 aspx 页面来传递图像。1

这是测试页面(纯 HTML,项目的起始页):

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><title></title></head>
<body>
    <div>
        <img src="GetImage.aspx?text=A" />
        <img src="GetImage.aspx?text=B" />
        <img src="GetImage.aspx?text=C" />
        <img src="GetImage.aspx?text=D" />
        <img src="GetImage.aspx?text=E" />
        <img src="GetImage.aspx?text=F" />
        <img src="GetImage.aspx?text=G" />
        <img src="GetImage.aspx?text=H" />
        <img src="GetImage.aspx?text=I" />
        <img src="GetImage.aspx?text=J" />
        <img src="GetImage.aspx?text=K" />
        <img src="GetImage.aspx?text=L" />
    </div>
</body>
</html>

这是 aspx 页面 (GetImage.txt)。 aspx):

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="GetImage.aspx.cs" Inherits="CsWebApplication1.GetImage" %>

以及代码隐藏的相关部分 (GetImage.aspx.cs, usingnamespace 已跳过):

public partial class GetImage : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        Debug.WriteLine("Start: " + DateTime.Now.Millisecond);
        Response.Clear();
        Response.ContentType = "image/jpeg";

        var image = GetDummyImage(Request.QueryString["text"]);
        Response.OutputStream.Write(image, 0, image.Length);
        Debug.WriteLine("End: " + DateTime.Now.Millisecond);
    }

    // Empty 50x50 JPG with text written in the center
    private byte[] GetDummyImage(string text)
    {
        using (var bmp = new Bitmap(50, 50))
        using (var gr = Graphics.FromImage(bmp))
        {
            gr.Clear(Color.White);
            gr.DrawString(text,
                new Font(FontFamily.GenericSansSerif, 10, FontStyle.Regular, GraphicsUnit.Point),
                Brushes.Black, new RectangleF(0, 0, 50, 50),
                new StringFormat { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center });
            using (var stream = new MemoryStream())
            {
                bmp.Save(stream, ImageFormat.Jpeg);
                return stream.ToArray();
            }
        }
    }
}

测试运行

  • < strong>Run 1,未修改:页面加载速度快,输出窗口显示 StartEnd 的随机组合,这意味着请求已得到处理并行。

  • 运行2,将空的Session_Start添加到global.asax(需要在浏览器中按F5一次,不知道为什么这样is): StartEnd 交替出现,表明请求是按顺序处理的。多次刷新浏览器表明,即使未附加调试器,也会存在性能问题。

  • 运行 3,与运行 2 类似,但将 EnableSessionState="ReadOnly" 添加到 GetImage.aspx 的 @Page 指令中:调试输出在第一个 End 之前显示多个 Start。我们再次并行,并且性能良好。


1 是的,我知道这应该使用 ashx 处理程序来完成。这只是一个例子。

tl;dr: If you face this problem with Webforms and don't require write access to session state in that particular page, adding EnableSessionState="ReadOnly" to your @Page directive helps.


Apparently, the existance of Session_Start alone forces ASP.NET to execute all requests originating from the same Session sequentially. This, however, can be fixed on a page-by-page basis if you don't need write access to the session (see below).

I've created my own test setting with Webforms, which uses an aspx page to deliver images.1

Here's the test page (plain HTML, start page of the project):

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><title></title></head>
<body>
    <div>
        <img src="GetImage.aspx?text=A" />
        <img src="GetImage.aspx?text=B" />
        <img src="GetImage.aspx?text=C" />
        <img src="GetImage.aspx?text=D" />
        <img src="GetImage.aspx?text=E" />
        <img src="GetImage.aspx?text=F" />
        <img src="GetImage.aspx?text=G" />
        <img src="GetImage.aspx?text=H" />
        <img src="GetImage.aspx?text=I" />
        <img src="GetImage.aspx?text=J" />
        <img src="GetImage.aspx?text=K" />
        <img src="GetImage.aspx?text=L" />
    </div>
</body>
</html>

Here's the aspx page (GetImage.aspx):

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="GetImage.aspx.cs" Inherits="CsWebApplication1.GetImage" %>

And the relevant parts of the codebehind (GetImage.aspx.cs, using and namespace skipped):

public partial class GetImage : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        Debug.WriteLine("Start: " + DateTime.Now.Millisecond);
        Response.Clear();
        Response.ContentType = "image/jpeg";

        var image = GetDummyImage(Request.QueryString["text"]);
        Response.OutputStream.Write(image, 0, image.Length);
        Debug.WriteLine("End: " + DateTime.Now.Millisecond);
    }

    // Empty 50x50 JPG with text written in the center
    private byte[] GetDummyImage(string text)
    {
        using (var bmp = new Bitmap(50, 50))
        using (var gr = Graphics.FromImage(bmp))
        {
            gr.Clear(Color.White);
            gr.DrawString(text,
                new Font(FontFamily.GenericSansSerif, 10, FontStyle.Regular, GraphicsUnit.Point),
                Brushes.Black, new RectangleF(0, 0, 50, 50),
                new StringFormat { Alignment = StringAlignment.Center, LineAlignment = StringAlignment.Center });
            using (var stream = new MemoryStream())
            {
                bmp.Save(stream, ImageFormat.Jpeg);
                return stream.ToArray();
            }
        }
    }
}

Test runs

  • Run 1, unmodified: The page loads fast, the output window shows a random mix of Start and Ends, which means that the requests get processed in parallel.

  • Run 2, add empty Session_Start to global.asax (need to hit F5 once in the browser, don't know why this is): Start and End alternate, showing that the requests get processed sequentually. Refreshing the browser multiple times shows that this has performance issues even when the debugger is not attached.

  • Run 3, like Run 2, but add EnableSessionState="ReadOnly" to the @Page directive of GetImage.aspx: The debug output shows multiple Starts before the first End. We are parallel again, and we have good performance.


1 Yes, I know that this should be done with an ashx handler instead. It's just an example.

提笔落墨 2024-10-14 22:45:12

无法告诉您调试器正在做什么(intellitrace?详细日志记录?第一次机会异常?),但您仍然掌握会话处理并发请求的能力。

对 ASP.NET 会话状态的访问是每个会话独占的,这意味着如果两个不同的用户并发请求,则同时授予对每个单独会话的访问权限。 但是,如果对同一会话发出两个并发请求(通过使用相同的 SessionID 值),则第一个请求将独占访问会话信息。第二个请求只有在第一个请求完成后才会执行。(如果由于第一个请求超过锁定超时而释放了信息上的独占锁,则第二个会话也可以获得访问权限。)如果如果@Page指令设置为ReadOnly,则对只读会话信息的请求不会导致对会话数据的独占锁定。但是,对会话数据的只读请求可能仍需要等待对会话数据的读写请求设置的锁被清除。

资料来源:ASP.NET 会话状态概述,我的重点

Can't tell you what your debugger is doing (intellitrace? detailed logging? first-chance exceptions?), but you're still in the hands of the sessions ability to handle concurrent requests.

Access to ASP.NET session state is exclusive per session, which means that if two different users make concurrent requests, access to each separate session is granted concurrently. However, if two concurrent requests are made for the same session (by using the same SessionID value), the first request gets exclusive access to the session information. The second request executes only after the first request is finished. (The second session can also get access if the exclusive lock on the information is freed because the first request exceeds the lock time-out.) If the EnableSessionState value in the @ Page directive is set to ReadOnly, a request for the read-only session information does not result in an exclusive lock on the session data. However, read-only requests for session data might still have to wait for a lock set by a read-write request for session data to clear.

Source: ASP.NET Session State Overview, my emphasis

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文