Внутреннее устройство ASP.NET: архитектура запроса

Далее подробно объяснена архитектура запроса ASP.NET.

Введение

Большинство разработчиков знакомы с высокоуровневыми абстракциями, предоставляемыми ASP.NET, такими как веб-формы и веб-службы. Однако под этими абстракциями находится очень интересная и продвинутая архитектура. Знание внутренностей этой архитектуры не только повышает уровень ASP.NET разработчика, но и помогает ему писать лучше спроектированные приложения и решать сложные проблемы, возникающие значительно ниже высокого уровня, используемого ASP.NET.

Данная статья изучает, как ASP.NET (и IIS) обрабатывает запросы. Подробно рассмотрено, что происходит внутри архитектуры ASP.NET с момента ухода запроса из браузера до того, как он пройдет сквозь среду выполнения ASP.NET.

Замечание: После того как запрос покинет среду выполнения ASP.NET, начинает выполняться модель страницы ASP.NET. Это выходит за рамки данной статьи, но будет темой следующей статьи.

Браузер выдает запрос

Процесс начинается, как только пользователь запросит ресурс ASP.NET посредством браузера. Например, пользователь запросил следующий URL: http://www.myserver.com/myapplication/mypage.aspx. Запрос достигнет “myserver”, где установлены Windows Server 2003 и IIS 6.0.

Драйвер http.sys режима ядра перехватывает запрос

Как только запрос достигает IIS, запрос обнаруживается драйвером режима ядра http.sys. Дальше рассказано, что такое драйвер режима ядра http.sys и что он делает.

В общем, Windows предоставляет два режима: режим пользователя и режим ядра. Пользовательские приложения работают в режиме пользователя, а код операционной системы работает в режиме ядра. Если пользовательскому приложению надо работать непосредственно с оборудованием, эту конкретную операцию выполняет процесс режима ядра. Очевидное назначение этих режимов – защитить компоненты операционной системы от повреждения пользовательскими приложениями. Теперь, когда ясно, что такое режим пользователя и режим ядра, в чем заключается роль драйвера режима ядра http.sys?

При создании нового веб-сайта IIS IIS регистрирует сайт в http.sys, который далее получает все запросы HTTP для любого веб-приложения внутри сайта. http.sys работает как ретранслятор, направляя запросы HTTP процессу режима пользователя, выполняющего веб-приложение. В данном случае процесс режима пользователя является рабочим процессом, выполняющим пул приложений, в котором работает веб-приложение. http.sys реализует механизм ведения очередей, создавая столько очередей, сколько пулов приложений имеется в IIS.

Продолжая пример, как только запрос достигает “myserver”, http.sys перехватывает запрос. Допустим, “myapplication” настроено на работу в пуле приложений “myapplicationpool”; в таком случае http.sys проверяет очередь запросов “myapplicationpool” и пересылает запрос рабочему процессу, в котором работает “myapplicationpool”.

Запрос пересылается пулу приложений

Запрос пересылается пулу приложений, как сказано выше. Каждым пулом приложений управляет экземпляр рабочего процесса “w3wp.exe”. По умолчанию “w3wp.exe” выполняется под учетной записью “NetworkService”. Это можно изменить следующим образом: щелкнуть правой кнопкой мыши по пулу приложений, вмещающему приложение--Свойства—вкладка Идентичность. Вспомните, что пулом приложений управляет рабочий процесс “w3wp.exe”. Сейчас рабочий процесс берет управление на себя.

Рабочий процесс загружает ASP.NET ISAPI

Рабочий процесс “w3wp.exe” ищет URL запроса, чтобы загрузить нужное расширение ISAPI. Запрошенный ресурс в URL - “mypage.aspx”. Что происходит дальше? Полный разбор расширений ISAPI (и фильтров) не входит в статью, но вкратце, расширения ISAPI являются способом, посредством которого IIS обрабатывает запросы на разные ресурсы. После установки ASP.NET он устанавливает свое собственное расширение ISAPI (aspnet_isapi.dll) и добавляет привязку в IIS. IIS увязывает разные расширения с их расширениями ISAPI. Привязки в IIS можно посмотреть так: щелкнуть правой кнопкой мыши по веб-сайту-Свойства-вкладка Домашний каталог-кнопка Конфигурация-вкладка Привязки. Рисунок ниже показывает привязки:


 
Как видно, расширение “.aspx” увязано с расширением aspnet_isapi.dll. Рабочий процесс передает запрос расширению aspnet_isapi. Расширение aspnet_isapi, в свою очередь, загружает среду выполнения HTTP, и начинается обработка запроса.

Перед изучением того, что происходит внутри среды выполнения HTTP, рассмотрены детали того, как рабочий процесс загружает веб-приложение. Рабочий процесс загружает сборку веб-приложения, выделяя один домен приложения для приложения. Когда рабочий процесс запускает веб-приложение (в его домене приложения), веб-приложение наследует идентичность процесса (NetworkService по умолчанию), если перевоплощение запрещено. Но если перевоплощение разрешено, каждое веб-приложение работает под учетной записью, аутентифицированной IIS, или под учетной записью пользователя, настроенной в web.config.

•    Identity impersonate="true"
         o    Если IIS разрешает только анонимный доступ, переданной веб-приложению идентичностью будет [machine]\IUSR_[machine].
         o    Если только встроенная аутентификация Windows разрешена в IIS, переданной веб-приложению идентичностью будет аутентифицированный пользователь Windows.
         o    Если разрешены встроенная аутентификация Windows и анонимный доступ, переданная веб-приложению идентичность будет зависеть от той, которая была аутентифицирована IIS. IIS сначала пытается использовать анонимный доступ, чтобы предоставить пользователю доступ к ресурсу веб-приложения. Если эта попытка проваливается, то он пытается использовать аутентификацию Windows
•    Identity impersonate="true" username="username" password="password"
         o    Позволяет веб-приложению работать под конкретной идентичностью.

Замечание: IIS 6.0 и IIS 5.0 по-разному обрабатывают запросы. Режим ядра http.sys реализован только в IIS 6.0. Такого свойства у IIS 5.0 нет. В IIS 5.0 запрос перехватывается непосредственно модулем aspnet_asapi, передающим запрос рабочему процессу. Рабочий процесс и модуль ISAPI взаимодействуют посредством конвейеров, приводя к издержкам вызова. Более того, один экземпляр рабочего процесса обслуживает все веб-приложения; нет пулов приложений. Модель IIS 6.0 намного лучше модули IIS 5.0. Рабочим процессом в IIS 5.0 является “aspnet_wp.exe”, в отличие от “w3wp.exe” в IIS 6.0. Рабочий процесс “aspnet_wp.exe” работает под стандартной учетной записью “ASPNET”, в отличие от “NetworkService” в IIS 6.0. Можно изменить эту учетную запись, найдя элемент <processmodel /> в файле “machine.config”.

Замечание: IIS 7.0 предоставляет два способа обработки запросов ASP.NET. Первый – классический способ, работающий так же, как в IIS 6.0; полезен для совместимости. Второй – новый комплексный способ, в котором ASP.NET и IIS входят в один и тот же конвейер обработки запроса. Во втором способе модули и обработчики .NET подключаются непосредственно к общему конвейеру обработки запроса, что эффективней способа IIS 6.0.

В среду выполнения HTTP

Обобщено, что пока произошло: запрос был передан от браузера к http.sys, передавшему запрос пулу приложений. Рабочий процесс, управляющий пулом приложений, изучает URL запроса и использует привязку расширения приложения IIS для загрузки ASP.NET ISAPI “aspnet_isapi.dll”. ASP.NET ISAPI загружает среду выполнения HTTP, также называемую средой выполнения ASP.NET.

Начинается изучение того, что происходит внутри HTTP. Точка входа среды выполнения - класс HttpRuntime. Метод HttpRuntime.ProcessRequest сообщает о начале обработки. В следующих подразделах рассмотрено, что происходит внутри среды выполнения после вызова метода ProcessRequest:

Создается новый экземпляр HttpContext запроса

HttpContext существует в течение времени жизни запроса и доступен через статическое свойство HttpContext.Current. Объект HttpContext представляет контекст активного сейчас запроса, так как он содержит ссылки на объекты, к которым можно обратиться в течение времени жизни запроса, а именно Request,Response, Application, Server и Cache. В любой момент при обработке запроса HttpContext.Current дает доступ ко всем перечисленным объектам. Более того, объект HttpContext содержит коллекцию Items, которую можно использовать для хранения специфичной информации запроса.

HttpRuntime советует классу HttpApplicationFactory загрузить объект HttpApplication

Класс HttpApplicationFactory создает пул объектов HttpApplication для приложения ASP.NET и связывает каждый запрос с объектом HttpApplication из того пула. Если в момент запроса в пуле нет объектов, то HttpApplicationFactory создает объект и передает его запросу. В результате запрос направляется приложению, работающему в пространстве AppDomain, и объект HttpApplication назначается для запроса. Теперь, когда ясно, что происходит, возникает вопрос, зачем нужен объект HttpApplication. HttpApplication – внешний контейнер для конкретного веб-приложения, увязанный с классом, определенным в Global.asax. При изучении файла Global.asax выясняется, что он унаследован от класса System.Web.HttpApplication. Есть набор предопределенных событий, возбуждающихся в течение времени жизни запроса; эти события представлены в файле Global.asax. HttpApplication служит контроллером этих событий. К тому же, HttpApplication хранит список сконфигурированных HttpModules, загружаемых динамически в ходе обработки запроса. Где выполняются эти события и HttpModules и чем именно являются HttpModules и HttpHandlers? Ответы на эти вопросы находятся внутри конвейера HTTP:

Внутри конвейера HTTP

Конвейер HTTP является, как намекает имя, конвейером для обработки запроса. Он называется конвейером, потому что содержит набор HttpModules, перехватывающих запрос на пути к HttpHandler. HTTPModules являются классами, имеющими доступ к входящему запросу. Эти модули проверяют входящий запрос и принимают решения, влияющие на внутренний поток запроса. Пройдя через заданные HTTPModules, запрос достигает обработчика HTTP, чьей задачей является генерация вывода, отправляемого обратно запрашивающему браузеру.

Запрос проходит через модули HTTP

HttpModules конфигурируются на уровне машины (machine.config) и на уровне приложения (web.config). В ASP.NET есть много встроенных HttpModules, осуществляющих доступ к запросу и выполняющих разные функции. Этими HttpModules являются аутентификация, управление состоянием и модули кеширования. ASP.NET 2.0 добавляет дополнительные модули, а именно членство, управление ролями и персонализация. Рисунок ниже взят из файла “machine.config” и показывает, как определены встроенные модули:

Разработчики могут написать свои собственные модули и подключить их в “machine.config”, чтобы применить модули ко всем приложениям, или в “web.config” конкретного приложения, чтобы применить модули только к нему. Запросы внутри конвейера HTTP проходят через все модули, определенные в “machine.config” и “web.config”. Как сказано в предыдущем разделе, эти модули хранятся внутри HttpApplication и динамически загружаются при выполнении.

Запрос попадает в обработчик HTTP

Обработчики HTTP являются конечными точками в конвейере HTTP. Задача обработчика HTTP – сгенерировать вывод для запрошенного ресурса. Для страниц ASPX это означает перевод страниц в HTML и возврат этого HTML. Обработчики HTTP конфигурируются на уровне машины (machine.config) и на уровне приложения (web.config). Рисунок ниже взят из “machine.config” и показывает, как устанавливаются обработчики HTTP.

Как видно на рисунке выше, разные ресурсы настраиваются на использование разных обработчиков. Страницы ASP.NET ASPX настроены на использование “PageHandlerFactory”. Задача “PageHandlerFactory” – предоставить экземпляр HTTP, способный обработать запрос. “PageHandleFactory” пытается найти скомпилированный класс, представляющий запрошенную страницу “mypage.aspx”. В случае успеха он передает данный скомпилированный класс в качестве обработчика HTTP. Если нет скомпилированного класса, представляющего запрошенную страницу, потому что запрос первый или потому что страницу изменили с момента последнего запроса, то “PageHandlerFactory” компилирует запрошенную страницу “mypage.aspx” и возвращает скомпилированный класс. Все последующие запросы будут обслуживаться тем же самым скомпилированным классом, пока страницу не изменят.

Возникает вопрос, почему “PageHandlerFactory” возвращает экземпляр скомпилированного класса, выступающий обработчиком запроса. Является ли скомпилированным класс фактическим обработчиком HTTP? Ответ очевиден из изучения отделенного кода страницы, “mypage.aspx.cs”. Страница унаследована от класса “System.Web.UI.Page”; этот класс реализует интерфейс “IHttpHandler”, что делает его пригодным в качестве обработчика HTTP.

Замечание: Компиляция страницы не входит в эту статью, но будет описана в следующей статье, рассматривающей модель страницы ASP.NET.

Начинается выполнение страницы

Наконец, запрос был передан нужному обработчику HTTP. Далее среда выполнения вызывает метод IHttpHandler.ProcessRequest, и начинается жизненный цикл страницы ASP.NET. Происходящее далее не входит в эту статью и будет подробно объяснено в следующей статье.

Вывод

Данная статья рассмотрела скрытые детали того, что происходит при каждом запросе страницы ASP.NET через браузер. Чтобы быстро обобщить процесс:
•    Запрос на “mypage.aspx” веб-приложения “myapplication” выдается из браузера
•    Запрос достигает IIS и перехватывается драйвером http.sys
•    Драйвер http.sys пересылает запрос пулу приложений, на работу в котором настроено “myapplication”
•    Рабочий процесс пула приложений “w3wp.exe” загружает ASP.NET ISAPI “aspnet_isapi.dll”, изучая URL и увязывая расширение “.aspx” с “aspnet_isapi” (эти расширения определены в IIS)
•    ISAPI “aspnet_isapi” загружает среду выполнения HTTP
•    Среда выполнения HTTP создает HttpContext и связывает запрос с HttpApplication
•    Запрос проходит через конвейер HTTP
•    Модули HTTP выполняются для запроса, пока запрос не попадет в обработчик HTTP страницы ASP.NET
•    Как только запрос покидает конвейер HTTP, начинается жизненный цикл страницы