Метод HttpModule Init вызывается несколько раз - почему? - PullRequest
31 голосов
/ 17 июля 2009

Я создавал модуль http и во время отладки заметил что-то, что поначалу (по крайней мере) казалось странным поведением.

Когда я устанавливаю точку останова в методе инициализации модуля httpmodule, я вижу, что метод инициализации модуля http вызывается несколько раз, хотя я только запустил веб-сайт для отладки и сделал один единственный запрос (иногда он вызывается только 1 раз, иногда 10 раз).

Я знаю, что я должен ожидать, что будет запущено несколько экземпляров HttpApplication, и для каждого будут созданы модули http, но когда я запрашиваю одну страницу, она должна обрабатываться одним объектом приложения http и, следовательно, запускать только события связан один раз, но все же он запускает события несколько раз для каждого запроса, что не имеет смысла - кроме того, что он должен быть добавлен несколько раз в этом httpApplication - что означает, что это тот же самый метод инициализации httpmodule, который вызывается каждый раз, а не каждое http-приложение создается каждый раз, когда оно достигает моей точки останова (см. мой пример кода внизу и т. д.).

Что здесь может пойти не так? потому что я отлаживаю и устанавливаю точку останова в модуле http?

Замечено, что если я запускаю веб-сайт для отладки и быстро перехожу через точку останова в httpmodule, он срабатывает только один раз в методе init, и то же самое относится и к обработчику событий. Если я вместо этого оставлю его висеть на точке останова на несколько секунд, метод init вызывается несколько раз (похоже, это зависит от того, сколько времени я жду, прежде чем перешагнуть через точку останова). Может быть, это может быть какая-то встроенная функция, чтобы убедиться, что модуль http инициализирован и приложение http может обслуживать запросы, но это также похоже на то, что может иметь катастрофические последствия.

Это может показаться логичным, поскольку он может пытаться завершить запрос, и, поскольку я установил точку останова, он думает, что что-то пошло не так, и пытается снова вызвать метод init? так он может обработать запрос?

Но это то, что происходит, и все в порядке (я просто догадываюсь), или это реальная проблема?

Что меня особенно беспокоит, так это то, что если что-то заставляет его зависать на сервере «production / live» на несколько секунд, то через init добавляется много обработчиков событий, и поэтому каждый запрос к странице внезапно запускает обработчик событий несколько раз. раз.

Такое поведение может быстро сломать любой сайт.

Я посмотрел «оригинальный» .net-код, используемый для http-модулей для проверки подлинности с помощью форм, модуля rolemanager и т. Д., Но мой код не отличается от того, что используют эти модули.

Мой код выглядит следующим образом.

    public void Init(HttpApplication app)
    {
        if (CommunityAuthenticationIntegration.IsEnabled)
        {
            FormsAuthenticationModule formsAuthModule = (FormsAuthenticationModule) app.Modules["FormsAuthentication"];         

            formsAuthModule.Authenticate += new FormsAuthenticationEventHandler(this.OnAuthenticate);
        }
    }

вот пример того, как это делается в RoleManagerModule из .NET framework

    public void Init(HttpApplication app)
    {
        if (Roles.Enabled)
        {
            app.PostAuthenticateRequest += new EventHandler(this.OnEnter);
            app.EndRequest += new EventHandler(this.OnLeave);
        }
    }

Кто-нибудь знает, что происходит?

(я просто надеюсь, что кто-то там может сказать мне, почему это происходит, и заверить меня, что все в порядке):)


UPDATE:

Я попытался сузить проблему, и до сих пор я обнаружил, что вызываемый метод Init всегда находится на новом объекте моего http-модуля (в отличие от того, что я думал раньше).

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

Если я запрашиваю другую страницу, все созданные HttpApplication (и их немодулированные) снова попытаются обработать запрос, вызывая многократное попадание в обработчик событий.

Но также кажется, что если я затем вернусь на первую страницу (или другую), только один HttpApplication начнет заботиться о запросе, и все будет так, как ожидалось - до тех пор, пока я не позволю ему зависнуть на перерыв точка.

Если я позволю ему зависнуть в точке останова, он начинает создавать новые объекты HttpApplication и начинает добавлять HttpApplications (более 1) для обслуживания / обработки запроса (который уже обрабатывается HttpApplication, который в данный момент остановлен на точка останова).

Я предполагаю или надеюсь, что это может быть какой-то интеллектуальный способ "закулисного" помочь распределить и обработать нагрузку и / или ошибки. Но я понятия не имею. Я надеюсь, что некоторые из них могут заверить меня, что это прекрасно и как это должно быть?

Ответы [ 4 ]

42 голосов
/ 10 марта 2010

Обычно метод Init () вызывается несколько раз. Когда приложение запускается, процесс ASP.NET Worker создает столько объектов HttpApplication, сколько ему нужно, а затем объединяет их (например, повторно использует для новых запросов, аналогично пулу соединений с базой данных).

Теперь для каждого объекта HttpApplication он также создает одну копию каждого зарегистрированного модуля IHttpModule и многократно вызывает метод Init. Таким образом, если будет создано 5 объектов HttpApplication, будет создано 5 копий вашего IHttpModule, а ваш метод Init будет вызван 5 раз. Имеет смысл?

Теперь, почему это говорит о создании 5 объектов HttpApplications? Ну, может быть, на вашей странице ASPX есть ссылки на другие ресурсы, которые ваш браузер попытается загрузить, css, javascript, WebResource.aspx, может быть, где-нибудь iframe. Или, может быть, рабочий процесс ASP.NET «настроен» запустить более 1 объекта HttpApplication, это действительно внутренняя деталь / оптимизация процесса ASP.NET, работающего под IIS (или встроенного веб-сервера VS).

Если вы хотите, чтобы код гарантированно выполнялся только один раз (и не хотите использовать событие Application_StartUp в Global.asax), вы можете попробовать следующее в вашем IHttpModule:

private static bool HasAppStarted = false;
private readonly static object _syncObject = new object();

public void Init(HttpApplication context)
{
    if (!HasAppStarted)
    {
        lock (_syncObject)
        {
            if (!HasAppStarted)
            {
                // Run application StartUp code here

                HasAppStarted = true;
            }
        }
    }
}

Я сделал что-то похожее, и это похоже на работу, хотя я бы приветствовал критику моей работы на случай, если я что-то пропустил.

8 голосов
/ 17 июля 2009
  1. Проверьте HttpContext.Current.Request, чтобы узнать, по какому запросу запускается init модуля. Может быть браузер отправляет несколько запросов.

  2. Если вы подключены к IIS, проверьте журналы IIS, чтобы узнать, получен ли какой-либо запрос за время пребывания в точке останова.

2 голосов
/ 17 июля 2009

Вот небольшое объяснение того, что вы должны использовать, когда и как они работают. Когда использовать Application_Start против Init в Global.asax?

Редактировать: Подробнее

Столбец ASP: HTTP-модули

ИНФОРМАЦИЯ: Экземпляры приложения, события приложения и состояние приложения в ASP.NET

1 голос
/ 16 марта 2010

Пример выше блокирует IHttpModule для всех запросов, а затем блокирует все приложение. Если ваш запрос вызовов IHttpModule несколько раз необходим для вызова метода HttpApplication CompleteRequest и удаления экземпляра HttpApplication события IHttpModule в EndRequest, чтобы удалить экземпляр приложения HttpApplication следующим образом:

public class TestModule :IHttpModule
    {
        #region IHttpModule Members

        public void Dispose()
        {

        }

        public void Init(HttpApplication context)
        {
            context.BeginRequest += new EventHandler(context_BeginRequest);
            context.EndRequest += new EventHandler(context_EndRequest);
        }

        void context_EndRequest(object sender, EventArgs e)
        {
            HttpApplication app = sender as HttpApplication;
            app.CompleteRequest();
            app.Dispose();
        }

        void context_BeginRequest(object sender, EventArgs e)
        {
            //your code here
        }

        #endregion
    }

Если вам нужно, чтобы запросы IHttpModule каждый раз без повторных запросов на обратную передачу, используйте этот код выше.

...