Логика работы Catalyst. Часть 1

Заметка по логике работы Catalyst. В данной заметке я не ставлю перед собой цели написать понятный текст :) , не знаю, возможно ли это вообще, когда речь идет о логике Catalyst. Только сохранить для себя важные моменты, куски кода. Но возможно, кому-то пригодится.

Попытка разобраться в логике запуска стандартного сервера Catalyst-приложения.

Ниже приведено очень много кода из модулей Catalyst-фреймворка и простого Catalyst-приложения. Весь код приведен с большими сокращениями.

Запуск скрипта myapp_server.pl:

/script/myapp_server.pl :

/Catalyst/ScriptRunner.pm :

В обычном случае, метод find_script_class() вернет класс «Catalyst::Script::Server», потом будет вызван метод run() для указанного класса.

/Catalyst/Script/Server.pm :

Внутри метода run() производится вызов метода _run_application(), который находится в Catalyst::ScriptRole.

/Catalyst/ScriptRole.pm :

Внутри метода _run_application() производится вызов $app->run(). Ожидается, что метод run() реализован в модуле MyApp.pm, однако MyApp сам метода run() не содержит, он наследует его от Catalyst.pm .

/lib/MyApp.pm :

Метод run() модуля Catalyst.pm выбирает движок для работы и выполняет метод run(), который расположен в выбранном движке.

/Catalyst.pm :

/Catalyst/EngineLoader.pm :

Метод run() выбранного движка создает сокеты для работы, начинает прослушивать выбранный порт на предмет поступления запросов. Если пришел запрос, то движок может создать дочерний процесс и вызвать обработчик, например _handler . Этот обработчик получит данные из сокета и вызовет другой обработчик, в обычном случае — handle_request() модуля Catalyst.pm .

/Catalyst/Engine/HTTP.pm :

Если у нас нет никаких особых реализаций движка (методы могут быть переопределены),
то метод handle_request() вызовет в этом же модуле метод prepare(), а дальше начнется целый цикл поочередного вызова методов:

Если попытка выполнения всех этих обработчиков в какой-то момент окончится неудачей, будет запущена другая последовательность обработчиков:

главной целью которых будет вывод клиенту сообщения об ошибке и грамотное завершение обработки запроса.

Если выполнение первой цепочки методов прошло успешно, будут вызваны еще несколько обработчиков:

Естественно, закончится все это отправкой ответа пользователю на обработанный запрос.

catalyst_logic

Рис.1. Примерная логика запуска сервера для Catalyst-приложения.

Резюме: скрипт myapp_server.pl вызывает Catalyst::ScriptRunner->run(),
Catalyst::ScriptRunner->run() вызывает Catalyst::Script::Server->run(),
Catalyst::Script::Server->run() вызывает метод _run_application() (который «наследуется» от Catalyst::ScriptRole).
Внутри метода _run_application() производится вызов $app->run(). Вызывается метод run() модуля Catalyst.

Далее выбирается движок для сервера и выполняется метод run(), который расположен в модуле выбранного движка. Движок создает сокеты, процессы и т.п., и после этого приложение готово принимать запросы, обрабатывать их.

catalyst_logic2

Рис.2. Модули и их функции, которые имеют решающее значение при запуске сервера.

 

Краткие характеристики некоторых используемых выше методов

$c->prepare_request(@arguments);

Вызывает метод prepare_request() в выбранном движке (например, в Catalyst::Engine::HTTP), кроме этого ничего дополнительного не делает.

Если в движке метод prepare_request() не реализован, будет выполняться поиск в базовых классах Engine. Обработка строки запроса (QUERY_STRING) и извлечение параметров запроса.

$c->prepare_connection;

Выполняет вызов метода prepare_connection() из модуля Catalyst::Request . Метод prepare_connection() задает параметры запроса (address, hostname, protocol, remote_user, method, secure), получая их из переменных окружения.

Пример:

$c->prepare_query_parameters;

$c->prepare_headers;

Вызывает метод headers() модуля Catalyst::Request, в конечном счете идет обращение к HTTP::Headers, метод header().

$c->prepare_cookies;

Идет вызов $c->request->cookies.

$c->prepare_path;

Выполняет вызов $c->engine->prepare_path( $c, @_ ) . Метод prepare_path, реализованный в модуле движка, в свою очередь обрабатывает путь (URI), который будет передан $c->request->base() .

$c->prepare_read;

Вызывает $c->engine->prepare_read( $c, @_ ) . Получает информацию о «Content-Length» запроса.

$c->prepare_body;

Данный метод инициирует вызов еще несколько методов:

$c->prepare_action;

Подготовка экшена. Для этого вызывается $c->dispatcher->prepare_action( $c, @_) . Возвращает ссылку на объект. Подбирает подходящий dispatch type, который подбирается по $c->req->path:

$c->dispatch;

Вызывает $c->dispatcher->dispatch( $c, @_ ) .

$c->finalize;

Завершение обработки запроса, в том числе запись в логи.