2008-01-11

Сценарий обслуживания запроса.txt

  2008-01-11 20:31

IRequest Инициализация : Работа метода **publsih** начинается с цикла. о разнице между корнем Zope и Zodb]). ...

Сценарий обслуживания запроса :

ПРИМЕЧАНИЕ: Нижеследующий текст - вольная компиляция Zope 3 Book Продвинутым слушателям рекомендуется прочесть оба материала.

Запрос
объект, который реализуют интерфейс IRequest. Эти объекты должны содержать в себе специфичные детали протокола и предоставлять их для использования презентационными компонентами.

В то же время, на низком уровне, запрос - это все, что клиент посылает серверу.

Обслуживание запроса разбивается на несколько изолированных этапов, которые подробно рассмотрены ниже.

Диспетчер запросов:

Обслуживание запроса начинается с получения его из сети: запрос получает сервер, который реализует интерфейс IDispatcher и использует компонент с интерфейсом ISocket. Реализация сервера построена на библиотеке asyncore, входящей в стандартную питоновскую поставку. Архитектура сервера основана на обслуживании событий. Для каждого типа событий, сервер предоставляет метод с именем вида:

            handle_<event>

где event - тип события (полный список методов можно получить, просмотрев интерфейс IDisplatcherEventHandler).

Событие получения запроса обслуживается методом handle_accept, код которого можно посмотреть в реализации сервера, строка 132 zope.server.serverbase.ServerBase. Этот метод получает запрос методом accept() и создает для него компонент ServerChannel.

Интерфейсы, каталоги и исходники:

            zope.server.serverbase.ServerBase

            IDisplatcherEventHandler

            ISocket

            asyncore

Декодирование запроса:

Декодирование запроса происходит в компоненте ServerChannel (далее - канал), исходники которого можно найти в:

            zope.server.serverchannelbase.ServerChannelBase

            zope.server.http.httperverchanel.HttpServerChannel

Канал содержит две ссылки на фабрики, которые заполняются при наследовании:

parser_class
фабрика парсеров, зарегестрированный в канале, порождает компоненты с интерфейсом IStreamConsumer,
task_class
фабрика задач, зарегистрированная в канале, порождает компоненты с интерфейсом ITask.

Основная операция, выполняемая каналом - получение данных от сервера (см. received(data)) и передача их парсеру, произведенному фабрикой parser_class, эта операция завершается при заполнении парсера (или, иными словами словами, по достижении алгоритмом разбора точки выхода). При заполнении парсера у него устанавливаетcя флаг completed, и получение данных от сервера прекращается.

decode

decode

После этого, на основе парсера, фабрикой task_class, порождается задача (ITask).

Интерфейсы, каталоги и исходники:

            zope.server.serverchannelbase.ServerChannelBase

            zope.server.http.httperverchanel.HttpServerChannel

            IStreamConsumer

            ITask

Диспетчер задач:

Канал передает задачу диспетчеру задач (ITaskDispatcher), который поддерживает очередь задач и предоставляет возможность обслужить каждую задачу в отдельном треде или (теоретически) на отдельном сервере. По умолчанию используется мультитредовый диспетчер задач zope.server.taskthreads.ThreadedTaskDispatcher (мультисерверный еще не создан).

Когда задача запускается на исполнение, вызывается метод ITask.service(), который окончательно выполняет запрос. В случае HTTPServer'а и HTTPTask, для реального выполнения задачи вызывается метод executeRequest(task) HTTPServer'а (в базовой реализации он не делает ничего).

Интерфейсы, каталоги и исходники:

            ITaskDispatcher

            zope.server.taskthreads.ThreadedTaskDispatcher

            HTTPServer, HTTPTask

            zope.server.http.httperver.HTTPServer

            zope.server.http.httptask.HTTPTask

Публикация запроса:

В Zope3 используется т.н. "http-сервер с публикатором" zope.server.http.publisherhttperver.PublisherHTTPServer, который создает объект IHTTPRequest из задания и публикует его при помощи zope.publisher.publish(request).

Именно там, где запрос передается публикатору, начинается сам Zope. При этом, раз запрос предоставляет интерфейс IRequest, то совершенно не важно каким образом запрос был получен и как он устроен, все операции с ним проводятся на основе этого интерфейса. Метод публикатора publish() ответственен за интерпретацию информации запроса и выполнение соответствующих действий и, следоватетльно, является центральной частью архитектуры Zope. Обратите внимание на исходный код zope.publisher.publish, что поможет намного лучше понять дальнейшее изложение.

Интерфейсы, каталоги и исходники:

            zope.server.http.publisherhttperver.PublisherHTTPServer

            IHTTPRequest

            zope.publisher.publish

            IRequest

Инициализация :

Работа метода publsih начинается с цикла. Первый шаг внутри цикла состоит в том, чтобы получить компонент publication (публикация). Публикация предоставляет публикатору возможность выполнять специфические задачи приложения: взаимодействовать с хранилищами, транзакциями и безопасностью. Реализацию публикации можно найти в:

                zope.app.publication.zopepublication.

Следом вызывается метод request.processInputs(), чтобы обработать и привести к стандартному виду внутренние параметры запроса. Скажем, для BrowserRequest это приведет к первичному разбору данных форм.

Интерфейсы, каталоги и исходники:

                zope.app.publication.zopepublication

                zope.publisher.http.HTTPRequest

                zope.publisher.browser.BrowserRequest

                zope.publisher.browser.BaseRequest

Траверс:

Следующий шаг - преобразование указанного в запросе пути к объекту в сам объект. Это довольно сложный и многозначный алгоритм, известный как траверс. Практически все веб-серверы сводят траверс к последовательному получению элементов, соответствующих именам в контейнерах, что на питоне записывается как цикл вида :

                ob = getRoot()

                for item in path.split("/") :

                    ob = getattr(ob,item)

Однако практика эксплуатации Zope2 и многочисленные теоретические измышления показали, что траверсинг - это алгоритм, для которого невозомжно предложить универсальную реализацию, и поэтому его удобно вынести в отдельный компонент. Например, могут быть различные, достаточно сложные способы получения элемента по его имени:

  1. Создание объекта под запрос -- объект создается, а не активируется готовый;
  2. Запрос объекта во внешнем хранилище -- это расширение предыдущего варианта, но инициализация объекта происходит данными, взятыми из внешнего хранилища;
  3. Объект ищется, а не запрашивается непосредственно - т.е. имя не является идентификатором объекта, а лишь параметром поиска объекта, как это сделано, например, в нечетком тарверсере;
  4. Из имени извлекаются дополнительные параметры активации объекта;

Вынос реализации траверсера в самостоятельный компонент позволяет легко решать такие и более сложные задачи без перепрограммирования объектов, хранящих содержимое сервера.

traverser

traverser

Первый шаг траверса - получить корень приложения, или, как его называют, "Object Root". Для стандартной инсталляции Zope это объект, который хранится в корне ZODB под ключом Application (подробно о разнице между корнем Zope и Zodb). Затем вызывается метод traverse(object) запроса BrowserRequest (см. zope.publisher.browser.BrowserRequest). Рассмотрим этот метод подробнее.

Запрос BrowserRequest наследует от BaseRequest метод traverse(object), который реализует основную часть алгоритма траверса. Сам BrowserRequest делает только немногие шаги, специфичные для обработки веб-запросов: например еще один шаг траверса в ответ на наличие в параметрах формы атрибута с модификатором :method или вызывает объект просмотра по умолчанию.

В начале метода traverse(object) назначаются значения нескольким переменным алгоритма траверса:

publication
Это просто объект-публикация, с которым мы уже встречались до этого и который дает доступ к специфическим возможностям приложения;
_traversal_stack
Обычный стек (т.е. список) имен, которые должны быть пройдены.

Эти имена появляются при разборе пути в URL. Так, например:

                        "/path/to/foo/bar/index.html"

будет разобран так :

                        ['path',' to','foo','bar','index.html];

_traversed_names
Это список имен, которые уже были успешно пройдены. Имена здесь есть просто элементы, перенесенные из _traversal_stack;
_last_obj_traversed
Эта переменная сохраняет последний объект, который был найден в результате траверса.

Затем начинается работа с _traversal_stack до тех пор, пока он не опустеет. Интересный вызов здесь - это publication.traverseName(request,object,name), который пытается определить следующий объект, используя его имя и запрос.

В процессе траверса для каждого объекта проверяется возможность приведения его к интерфейсу IPublishTraverse (непосредственно или через адаптер). Если это удалось, шаг траверса выполняется через этот интерфейс. Таким образом, для каждого типа объекта может использоваться специальный алгоритм траверса, реализация которого не включена в сам объект.

По окончании траверса должны быть выполнены некоторые дополнительные шаги - приведение объекта к виду по умолчанию. Это делает функция BrowserPublication.getDefaultTraversal(), которая вызывается в BrowserRequest.traverse() по окончании траверса.

При успешном завершении траверса, найденный объект возвращается в функцию publish(). Следующий шаг - выполнить объект.

Интерфейсы, каталоги и исходники:

                zope.app.publication.publicationtraverse.PublicationTraverse

                zope.publisher.browser.BrowserRequest

                zope.app.publication.browser.BrowserPublication

Выполнение:

Выполнение объекта подразумевает, что он, как минимум, выполняемый. Следовательно, траверс должен всегда заканчиваться на виде или методе вида. Но, так как все контент-объекты имеют вид по умолчанию, такое свойство гарантировано. Объект выполняется вызовом метода publication.callObject(request,object), который на самом деле вызывает функцию mapply (zope.publisher.publish), которая не просто вызывает объект, но и по аргументам, декларированном в описании его вызова, находит подходящие значения в среде вызова объекта (запросе и некторых других компонентах).

Когда объект вызывается, он может записать результат в объект ответа на запрос (request.response) или вернуть его. В последнем случае, результат добавляется к телу ответа, при этом подразумевается что результат - юникодная строка. Очень важно, что после вызова вызывается процедура afterCall(), которая подтверждает транзакцию в ZODB.

Когда все завершено, вызывается метод outputBody() ответа, который передает тело ответа клиенту, возможно, перекодировав его в подходящую кодировку.

Ссылки на эту статью:

План лекций Траверсеры.txt
Официальный сайт Zope3 Московская группа изучения реактивного движения The Dream Bot Site noooxml