Почему Session_Start в Global.asax.cs вызывает проблемы с производительностью? - PullRequest
9 голосов
/ 15 декабря 2010

Когда я создаю пустой обработчик Session_Start в Global.asax.cs, он вызывает значительный удар при отображении страниц в браузере.

Как воспроизвести:

Создайте пустое веб-приложение ASP.NET MVC 3 (я использую 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 КАДРОВ, каждая с номером внутри. Все, что я делаю здесь, это создание страницы, которая загружает еще 20 страниц за кулисами. Прежде чем продолжить, обратите внимание на то, как быстро загружаются эти 20 страниц (обновите страницу несколько раз, чтобы повторить загрузку).

Далее перейдите к вашему Global.asax.cs и добавьте этот метод (да, тело метода пусто):

protected void Session_Start()
{
}

Теперь снова запустите страницу. На этот раз вы заметите, что 20 IFRAME загружаются намного медленнее, один за другим, с интервалом около 1 секунды. Это странно, потому что мы на самом деле ничего не делаем в Session_Start ... это просто пустой метод. Но этого, по-видимому, достаточно, чтобы вызвать замедление на всех последующих страницах.

Кто-нибудь знает, почему это происходит, и еще лучше, если у кого-нибудь есть исправление / обходной путь?

Обновление

Я обнаружил, что это происходит только при подключенном отладчике (работает с F5). Если вы запустите его без подключенного отладчика (Ctrl-F5), то все будет в порядке. Так что, может быть, это не является серьезной проблемой, но все же странно.

Ответы [ 2 ]

12 голосов
/ 15 февраля 2012

tl; dr : Если вы столкнулись с этой проблемой при работе с веб-формами и вам не требуется доступ на запись к состоянию сеанса на этой конкретной странице, добавление 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.aspx):

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

И соответствующие части кода позади (GetImage.aspx.cs, using и namespace пропущены):

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();
            }
        }
    }
}

Тестовые прогоны

  • Запуск 1 , без изменений: страница загружается быстро, в окне вывода отображается случайное сочетание Start и End с, что означает, что запросы обрабатываются параллельно.

  • Выполнить 2 , добавить пустое Session_Start к global.asax (нужно нажать F5 один раз в браузере, не знаю, почему это так): Start и End поочередно, показывая, что запросы обрабатываются последовательно. Повторное обновление браузера показывает, что это приводит к проблемам с производительностью, даже если отладчик не подключен.

  • Выполнить 3 , как Выполнить 2, но добавить EnableSessionState="ReadOnly" к директиве @Page GetImage.aspx: выходные данные отладки показывают несколько Start с перед первым End , Мы снова параллельны, и у нас хорошие показатели.


1 Да, я знаю, что это следует делать с помощью обработчика ashx. Это просто пример.

3 голосов
/ 09 мая 2011

Не могу сказать вам, что делает ваш отладчик (intellitrace? Подробное ведение журнала? Исключения первого шанса?), Но у вас все еще есть возможность сеансов обрабатывать параллельные запросы.

Доступ к состоянию сеанса ASP.NET является исключительным для каждого сеанса, что означает, что если два разных пользователя делают параллельные запросы, доступ к каждому отдельному сеансу предоставляется одновременно. Однако, если два одновременных запроса сделаны для одного и того же сеанса (с использованием одного и того же значения SessionID), первый запрос получает эксклюзивный доступ к информации сеанса. Второй запрос выполняется только после того, как первый запрос завершен. (Второй сеанс также может получить доступ, если исключительная блокировка информации освобождается, поскольку первый запрос превышает время ожидания блокировки.) Если значение EnableSessionState в Директива @ Page имеет значение ReadOnly, запрос информации о сеансе только для чтения не приводит к исключительной блокировке данных сеанса. Однако запросы только для чтения данных сеанса, возможно, все еще должны ждать блокировки, установленной запросом чтения-записи, для очистки данных сеанса.

Источник: Обзор состояния сеанса ASP.NET , мой акцент

...