Платформы для запуска Perl веб-приложений — различия между версиями
(→mod_perl) |
|||
(не показано 65 промежуточных версий этого же участника) | |||
Строка 1: | Строка 1: | ||
− | В статье рассматриваются платформы/библиотеки для запуска веб-приложений на языке [[Perl]]. Рассматриваются они именно как «серверы приложений» — в случае со standalone серверами не предъявляется требований гибкости обычного HTTP-сервера вроде [http://nginx.ru nginx] или [http://httpd.apache.org/ Apache], безопасности, или производительности в обслуживании статических файлов. Задача платформы — предоставить удобный программный интерфейс, такой, чтобы приложение могло получить запрос, предназначающийся ему, и сформировать на него адекватный ответ. | + | [[Изображение:Perl.appservers.benchmark.png|right|thumb|Тест производительности некоторых серверов]] В статье рассматриваются платформы/библиотеки для запуска веб-приложений на языке [[Perl]]. Рассматриваются они именно как «серверы приложений» — в случае со standalone серверами не предъявляется требований гибкости обычного HTTP-сервера вроде [http://nginx.ru nginx] или [http://httpd.apache.org/ Apache], безопасности, или производительности в обслуживании статических файлов, так как предполагается, что Perl-сервер будет всё равно «спрятан» за фронтенд наподобие nginx. Задача платформы — предоставить удобный программный интерфейс, такой, чтобы приложение могло получить запрос, предназначающийся ему, и сформировать на него адекватный ответ. |
Все они делятся на две категории — библиотеки, дающие возможность запуска Standalone HTTP-сервера, и интерфейсы, требующие для работы внешнего HTTP-сервера. | Все они делятся на две категории — библиотеки, дающие возможность запуска Standalone HTTP-сервера, и интерфейсы, требующие для работы внешнего HTTP-сервера. | ||
+ | |||
+ | Фреймворки для построения веб-приложений — всевозможные {{CPAN|Mason}}, {{CPAN|Catalyst}} и т. п. — '''не''' рассматриваются. | ||
== Требующие внешнего HTTP-сервера == | == Требующие внешнего HTTP-сервера == | ||
Строка 7: | Строка 9: | ||
Сюда относятся, во-первых, все реализации, предназначенные для встраивания Perl-интерпретатора в тот или иной веб-сервер, а во-вторых, дополнительные протоколы взаимодействия HTTP-сервера с Perl-программами или демонами, такие, как CGI. | Сюда относятся, во-первых, все реализации, предназначенные для встраивания Perl-интерпретатора в тот или иной веб-сервер, а во-вторых, дополнительные протоколы взаимодействия HTTP-сервера с Perl-программами или демонами, такие, как CGI. | ||
− | === CGI === | + | === CGI, PCGI === |
Старый добрый Common Gateway Interface ([[rupedia:CGI|CGI]] — статья в русскоязычной Википедии), впервые реализованный где-то в районе 1993 года в первом HTTP-сервере [[rupedia:NCSA_HTTPd|NCSA HTTPd]] 1.0. Идея — максимально простой интерфейс взаимодействия с программой — через переменные окружения, а также стандартные ввод и вывод (STDIN и STDOUT). Веб-сервер при каждом запросе, который нужно обработать CGI приложением, запускает новый процесс (это самое приложение), передавая некоторые заголовки и данные в переменных окружения, а адрес запроса и содержимое (например, загружаемого на сервер файла) через стандартный ввод, после чего читает ответ (заголовки и содержимое) со стандартного вывода приложения. | Старый добрый Common Gateway Interface ([[rupedia:CGI|CGI]] — статья в русскоязычной Википедии), впервые реализованный где-то в районе 1993 года в первом HTTP-сервере [[rupedia:NCSA_HTTPd|NCSA HTTPd]] 1.0. Идея — максимально простой интерфейс взаимодействия с программой — через переменные окружения, а также стандартные ввод и вывод (STDIN и STDOUT). Веб-сервер при каждом запросе, который нужно обработать CGI приложением, запускает новый процесс (это самое приложение), передавая некоторые заголовки и данные в переменных окружения, а адрес запроса и содержимое (например, загружаемого на сервер файла) через стандартный ввод, после чего читает ответ (заголовки и содержимое) со стандартного вывода приложения. | ||
Строка 13: | Строка 15: | ||
Плюсы: | Плюсы: | ||
− | # Универсальность — | + | # Универсальность — запуск CGI скриптов поддерживается почти везде. |
− | + | ||
− | + | ||
# Запуск нового интерпретатора, помимо минуса (1), означает и плюс — свободу от свойственных долгоживущим Perl демонам утечек памяти. | # Запуск нового интерпретатора, помимо минуса (1), означает и плюс — свободу от свойственных долгоживущим Perl демонам утечек памяти. | ||
− | + | Универсальность CGI в смысле возможности использования произвольного языка программирования для нас ни плюсом, ни минусом, очевидно, не является, так как рассматривается именно Perl. | |
− | + | На этом плюсы заканчиваются и начинаются сплошные минусы: | |
# Самый очевидный и повторяемый всеми аки мантра минус — слабая производительность из-за необходимости запуска нового процесса интерпретатора при каждом запросе. Причин две — во-первых, это постоянно создаваемые процессы, которые могут быть совсем не лёгкими — раз, и вообще ограничивать производительность ОС большим числом переключений при большой нагрузке; во-вторых, это постоянные компиляции и инициализации модулей, занимающие приличное время. | # Самый очевидный и повторяемый всеми аки мантра минус — слабая производительность из-за необходимости запуска нового процесса интерпретатора при каждом запросе. Причин две — во-первых, это постоянно создаваемые процессы, которые могут быть совсем не лёгкими — раз, и вообще ограничивать производительность ОС большим числом переключений при большой нагрузке; во-вторых, это постоянные компиляции и инициализации модулей, занимающие приличное время. | ||
− | # | + | # Второй наиболее неприятный минус — очень неудобный API наиболее стандартного модуля {{CPAN|CGI|CGI.pm}}. Например: |
− | #* CGI.pm | + | #* Нельзя нормально получить хеш со всеми параметрами, так как CGI->Vars работает криво: если в запросе передано несколько значений одного параметра (&a=1&a=2), то в Vars значением этого параметра будет конкатенация всех этих значений (то есть { a => '12' }). Хотя по логике вещей там должен быть массив [ '1', '2' ]. |
− | #* В районе года 2000-ного авторам | + | #* CGI->cookie() не дружит с UTF-ными ключами. |
− | #* | + | #* Следить за своевременным выводом заголовков и текста ответа нужно самому. |
− | + | #* У различных функцих весьма странные интерфейсы — с ключами, начинающимися на «-» и так далее. | |
− | + | #* В CGI часто даже передаются не все заголовки запроса. | |
+ | # Говнокод. | ||
+ | #* На код самого CGI.pm страшно смотреть. В принципе, уже одно отсутствие <code>use strict</code> говорит о раздолбайстве авторов. | ||
+ | #* В районе года 2000-ного авторам вдруг взбрело в голову, что век амперсанда (&) в качестве разделителя параметров в строках запроса подошёл к концу, и что теперь все будут использовать вместо него точку с запятой (;). Соответственно поведение и разбора, и генерации URL изменилось — причём, если в случае с генерацией всё легко возвращается на свои места заданием опции -oldstyle_urls, то разбор URL неизменно разбивает их и по «&», и по «;», что влечёт за собой различные неприятные эффекты. | ||
+ | #* Большинство модулей типа CGI::''что-нибудь'' — дикая древность, исповедующая генерацию HTML кода без использования шаблонизаторов print()'ами, на содержимое которых повлиять без влезания в сами модули невозможно. ''XXI век на дворе, пора бы прекратить хотя бы это'' — ан нет, и в 2004, и даже в 2006 годах такие модули появлялись. | ||
+ | |||
+ | {{CPAN|PCGI}} — те же яйца, только в профиль, чуть более аккуратно написанные и с защитой от слишком больших запросов. Все интерфейсные минусы сохраняются. | ||
=== FastCGI / SCGI === | === FastCGI / SCGI === | ||
− | Идея [[rupedia:FastCGI|FastCGI]] — ликвидировать | + | Идея [[rupedia:FastCGI|FastCGI]] — ликвидировать тормоза CGI, сохранив интерфейс. FastCGI-процесс обрабатывает не один запрос, а много — последовательно принимая их в цикле через Unix- или TCP-сокет. Таким образом, FastCGI-процессы, во-первых, могут быть запущены на другом физическом сервере, а во-вторых, становится возможно распределение запросов между несколькими долгоживущими процессами. |
− | [[rupedia:SCGI|SCGI]] — | + | [[rupedia:SCGI|SCGI]] — фактически клон FastCGI, отличия лишь в формате передачи данных, который, как утверждают авторы, проще реализовать, но и возможностей у него меньше — например, STDERR не передаётся обратно HTTP-серверу. Поддерживается чуть менее широко: [http://wiki.nginx.org/NginxNgxSCGIModule ngx_scgi_module], Apache, [http://redmine.lighttpd.net/wiki/lighttpd/Docs:ModSCGI Lighttpd], [http://www.cherokee-project.com/ Cherokee], Mathopd с неофициальным [http://www.mail-archive.com/mathopd@mathopd.org/msg00369.html патчем]. |
Плюсы: | Плюсы: | ||
Строка 41: | Строка 47: | ||
# Передача в скрипт любых HTTP-заголовков, какие душе угодны. | # Передача в скрипт любых HTTP-заголовков, какие душе угодны. | ||
# Возможность самостоятельного управления пулом процессов или потоков. | # Возможность самостоятельного управления пулом процессов или потоков. | ||
+ | # Возможность запуска FastCGI-скриптов на серверах, поддерживающих только CGI — в этом случае модуль {{CPAN|FCGI}} обрабатывает 1 запрос и выходит. | ||
Минусы: | Минусы: | ||
# Склонность к утечкам памяти, особенно в случае использования большого числа устаревшего кода, рассчитанного на «умирание» скрипта после обработки каждого запроса в среде CGI. | # Склонность к утечкам памяти, особенно в случае использования большого числа устаревшего кода, рассчитанного на «умирание» скрипта после обработки каждого запроса в среде CGI. | ||
− | # Взаимодействие по-прежнему ведётся через функции CGI, поэтому «интерфейсные» минусы CGI никуда не исчезают. Специальных обвязок для упрощения взаимодействия с сервером по протоколу FastCGI нет, кроме самой примитивной реализации — модуля | + | # Взаимодействие по-прежнему ведётся через функции CGI, поэтому «интерфейсные» минусы CGI никуда не исчезают. Специальных обвязок для упрощения взаимодействия с сервером по протоколу FastCGI нет, кроме самой примитивной реализации — модуля {{CPAN|FCGI}}. |
=== mod_perl === | === mod_perl === | ||
− | [http://perl.apache.org/ mod_perl] — модуль HTTP-сервера [http://httpd.apache.org/ Apache], предназначенный для | + | [http://perl.apache.org/ mod_perl] — модуль HTTP-сервера [http://httpd.apache.org/ Apache], предназначенный для разработки модулей Apache на языке Perl. «Модулей» — потому, что API mod_perl’а почти полностью повторяет C’шное API апача. Поэтому же API сильно различается в 1-ой и 2-ой версиях. Хотя какая разница, всё равно про httpd 1.x и mod_perl 1.x уже никто не помнит. |
Плюсы: | Плюсы: | ||
− | # | + | # Большая степень гибкости и возможности комбинирования с другими модулями Apache, в частности, засчёт наличия большого числа обработчиков разных стадий запроса. Своим Perl-модулем можно осуществлять не всю обработку запроса, а, например, только авторизацию. |
− | # Весьма богатый и | + | # Весьма богатый и довольно удобный программный интерфейс, через который с Apache можно делать практически всё, что душа пожелает. |
− | # Возможность с небольшими телодвижениями запускать CGI-приложения в среде mod_perl с помощью модуля Apache::Registry. Для примера использования доработанного модуля Registry можно посмотреть реализацию [http://mxr.mozilla.org/bugzilla/source/mod_perl.pl mod_perl.pl] из Bugzilla | + | # Возможность с небольшими телодвижениями запускать CGI-приложения в среде mod_perl с помощью модуля {{CPAN|Apache::Registry}}. Для примера использования доработанного модуля Registry можно посмотреть реализацию [http://mxr.mozilla.org/bugzilla/source/mod_perl.pl mod_perl.pl] из Bugzilla. |
+ | # Модуль популярен. Есть множество наработок, многие из которых представляют собой весьма приятные продукты. Хороший пример — профилировщик {{CPAN|NYTProf}}, разработанный именно для профилирования мод_перла. | ||
Минусы: | Минусы: | ||
− | # Чрезмерная завязка на внутреннее устройство веб-сервера [http://httpd.apache.org/ Apache]. | + | # Чрезмерная завязка на внутреннее устройство веб-сервера [http://httpd.apache.org/ Apache]. На mod_perl’е вы разрабатываете ''модуль Apache''. Приложения, написанные под mod_perl, не запустятся больше нигде. |
− | # В mod_perl2 для различных функций существует по нескольку где-то конкурирующих, где-то дополняющих друг друга, а где-то сходных по функционалу, но разных по интерфейсу библиотек. Частично это диктуется совместимостью с mod_perl1. ''Примеры — куки: Apache2::Cookie, APR::Request::Cookie, запрос: Apache2::RequestRec, Apache2::RequestUtil, Apache2::Request, APR::Request'' | + | # В mod_perl2 для различных функций существует по нескольку где-то конкурирующих, где-то дополняющих друг друга, а где-то сходных по функционалу, но разных по интерфейсу библиотек. Частично это диктуется совместимостью с mod_perl1. ''Примеры — куки: Apache2::Cookie, APR::Request::Cookie, запрос: Apache2::RequestRec, Apache2::RequestUtil, Apache2::Request, APR::Request. Пока я в первый раз допёр, что для того, чтобы получить в пользование <code>$r->dir_config()</code>, нужно сделать <code>use Apache2::RequestUtil</code>…'' Это не так страшно, но некоторую путаницу всё-таки вносит. |
− | # Склонность к утечкам памяти. ''mod_perl течёт всегда, хоть ты его режь.'' | + | # Склонность к утечкам памяти. ''mod_perl течёт всегда, хоть ты его режь.'' Более-менее решается MaxRequestsPerChild. |
− | # | + | # Проблемы с перезагрузкой модулей без перезапуска сервера. Apache2::Reload — кривой; перезагрузку можно реализовать ручками (см. https://github.com/vitalif/bugzilla4intranet), но и такой вариант всё равно иногда срывает апачу крышу, он забывает про все константы и валит Internal Server Error до ближайшего рестарта. |
− | # | + | # Большое потребление памяти, ибо Perl сосуществует в одних и тех же процессах с остальными модулями Apache. |
− | # Время от времени в mod_perl всплывают совершенно неуловимые глюки, особенно в необычных режимах вроде [http://perldoc.perl.org/perlsec.html taint], | + | # Если нужно запустить несколько разных приложений или несколько экземпляров одного приложения, mod_perl создаёт проблемы по причине отсутствия изоляции загруженного кода модулей между приложениями. Решается это PerlOptions +Parent (в mod_perl 1.x не решалось), но в этом случае дочерние процессы Apache вырастают до совсем неприличных размеров по причине работы в каждом нескольких Perl-интерпретаторов вместо одного. Пул процессов-то общий. |
+ | # Время от времени в mod_perl всплывают совершенно удивительные и неуловимые глюки, особенно в необычных режимах вроде [http://perldoc.perl.org/perlsec.html taint], или при использовании с некоторыми модулями или движками Apache. ''«Потому что Perl и mod_perl — это как бэ немного разные языки»'' (c). Например: | ||
#* При использовании [http://mpm-itk.sesse.net/ mpm_itk], 2-го мод_перла и PerlOptions +Parent (дающей отдельный пул интерпретаторов виртхосту) глобальные переменные в пакетах (как my, так и our) перестают сохранять свои значения между запросами. | #* При использовании [http://mpm-itk.sesse.net/ mpm_itk], 2-го мод_перла и PerlOptions +Parent (дающей отдельный пул интерпретаторов виртхосту) глобальные переменные в пакетах (как my, так и our) перестают сохранять свои значения между запросами. | ||
− | #* При включённом taint mode и тоже в отдельном интерпретаторе, в составе [http://www.bugzilla.org/ Bugzilla] | + | #* При включённом taint mode и тоже в отдельном интерпретаторе, в составе [http://www.bugzilla.org/ Bugzilla] проявляется следующий мистический баг: |
#: На входе строки <code>$oldstr</code> и <code>$newstr</code>, обе не taint’ченные, в <code>$newstr</code> есть запятые, в <code>$oldstr</code> нет. Пишем два идентичных по семантике фрагмента кода: | #: На входе строки <code>$oldstr</code> и <code>$newstr</code>, обе не taint’ченные, в <code>$newstr</code> есть запятые, в <code>$oldstr</code> нет. Пишем два идентичных по семантике фрагмента кода: | ||
#:* Если написать <code>$oldstr =~ s/[,\s]+/ /g; $newstr =~ s/[,\s]+/ /g;</code>, то <code>$newstr</code> почему-то становится tainted. | #:* Если написать <code>$oldstr =~ s/[,\s]+/ /g; $newstr =~ s/[,\s]+/ /g;</code>, то <code>$newstr</code> почему-то становится tainted. | ||
#:* Если же написать <code>s/[,\s]+/ /g for $oldstr, $newstr;</code>, то обе, как и положено, остаются не tainted. | #:* Если же написать <code>s/[,\s]+/ /g for $oldstr, $newstr;</code>, то обе, как и положено, остаются не tainted. | ||
#: Баг воспроизводится только в составе Bugzilla и только под мод_перлом, из контекста выдернуть его не получается. | #: Баг воспроизводится только в составе Bugzilla и только под мод_перлом, из контекста выдернуть его не получается. | ||
+ | #* Ещё пример в taint mode из Багзиллы: | ||
+ | #: Где-то в глубине души модифицированного модуля <code>Bugzilla::Field::Choice</code> конкатенируются <code>Bugzilla::Product->DB_COLUMNS->[0]</code> и <code>Bugzilla::Product->DB_TABLE</code>. При проверке {{CPAN|Scalar::Util}}::tainted в момент конкатенации сами по себе они оба безгрешны, однако, грех (флаг tainted) зарождается при их конкатенации. ''Вот и как это понимать? Адам и Ева сами по себе были безгрешны, но соединившись?..'' | ||
+ | # Некоторые затрудения в ''автоматизированных'' отладке и профилировании приложений в среде mod_perl из-за неочевидности команд запуска. Многие инструменты эта неочевидность «смущает». | ||
+ | # Отсутствие mod_perl на подавляющем большинстве веб-хостингов. Потому что для админов серверов с кучами хомячков это — головная боль, в многопользовательской среде влекущая проблемы как с безопасностью, так и с надёжностью и производительностью. Всё по причине уже описанных минусов. | ||
+ | #: ''Как вы думаете, почему так широко распространился язык [[rupedia:PHP|PHP]]? Именно по причине простоты обслуживания.'' | ||
− | === ngx_http_perl_module === | + | === <s>ngx_http_perl_module</s> === |
− | Идея — вызов Perl-кода из [http://nginx.ru/ nginx]. | + | Идея — вызов Perl-кода из [http://nginx.ru/ nginx]. Скорее мёртв, чем жив. |
Ссылка на документацию: [http://sysoev.ru/nginx/docs/http/ngx_http_perl_module.html http://sysoev.ru/nginx/docs/http/ngx_http_perl_module.html]. | Ссылка на документацию: [http://sysoev.ru/nginx/docs/http/ngx_http_perl_module.html http://sysoev.ru/nginx/docs/http/ngx_http_perl_module.html]. | ||
Строка 85: | Строка 99: | ||
* Идея размещения приложения внутри nginx несколько лишает его легковесности. Также появляются некоторые проблемы с масштабируемостью в случае простоев (ожиданий ответов от СУБД и т. п.). | * Идея размещения приложения внутри nginx несколько лишает его легковесности. Также появляются некоторые проблемы с масштабируемостью в случае простоев (ожиданий ответов от СУБД и т. п.). | ||
* Жёсткая завязка на использование внутри nginx. Больше нигде приложения, написанные под nginx, не заработают. | * Жёсткая завязка на использование внутри nginx. Больше нигде приложения, написанные под nginx, не заработают. | ||
+ | * Поразительное качество: невозможно получить список ''всех'' HTTP-заголовков, присутствующих в запросе, а можно лишь считывать их по одному. Остаётся только применять [{{SVN|vitalif/trunk/scripts/nginx.xs.diff}} патч к src/http/modules/perl/nginx.xs]. | ||
* Отсутствие поддержки CGI среды внутри ngx_http_perl_module, наподобие mod_perl апача. | * Отсутствие поддержки CGI среды внутри ngx_http_perl_module, наподобие mod_perl апача. | ||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
− | |||
== Standalone == | == Standalone == | ||
Строка 102: | Строка 106: | ||
Альтернативой встраиванию интерпретатора в процесс HTTP-сервера является запуск собственного HTTP-сервера, рассчитанного на расположение за обратным прокси (reverse proxy) — [http://httpd.apache.org/docs/2.0/mod/mod_proxy.html mod_proxy] Апача, [http://nginx.ru/ nginx]'ом, или [http://www.squid-cache.org/ Squid]'ом — в качестве фронтенда. Так делают, например, [http://www.mortbay.org/ Jetty] (Java) и [http://www.zope.org/ Zope] (Python). Это работает точно так же, как обработка запросов внутри сервера, только запросы отправляются другому HTTP-серверу. | Альтернативой встраиванию интерпретатора в процесс HTTP-сервера является запуск собственного HTTP-сервера, рассчитанного на расположение за обратным прокси (reverse proxy) — [http://httpd.apache.org/docs/2.0/mod/mod_proxy.html mod_proxy] Апача, [http://nginx.ru/ nginx]'ом, или [http://www.squid-cache.org/ Squid]'ом — в качестве фронтенда. Так делают, например, [http://www.mortbay.org/ Jetty] (Java) и [http://www.zope.org/ Zope] (Python). Это работает точно так же, как обработка запросов внутри сервера, только запросы отправляются другому HTTP-серверу. | ||
− | У такого подхода — использования HTTP вместо встраивания интерпретатора в сервер, или вариаций на тему CGI — есть несколько приятных преимуществ. Фронтенд может балансировать и распределять по нескольким серверам нагрузку одновременно с передачей запроса. Работа администраторов упрощается, потому что конфигурация фронтенда для передачи запроса разным приложениям идентична, а сами приложения могут быть запущены под любым системным пользователем, на другом сервере, в jail’е, | + | У такого подхода — использования HTTP вместо встраивания интерпретатора в сервер, или вариаций на тему CGI — есть несколько приятных преимуществ. Фронтенд может балансировать и распределять по нескольким серверам нагрузку одновременно с передачей запроса, причём для этого существует множество готовых качественных инструментов. Работа администраторов упрощается, потому что конфигурация фронтенда для передачи запроса разным приложениям идентична, а сами приложения могут быть запущены под любым системным пользователем, на другом сервере, в jail’е, виртуальной машине, за аппаратным firewall’ом или внутри какой-нибудь другой системы безопасности. При отладке разработчик может взаимодействовать напрямую со своим приложением без необходимости запускать отдельный HTTP-сервер. Потребление памяти сокращается засчёт изоляции каждого придожения в своём наборе процессов. |
− | + | Кроме того, в этом случае приложение свободно от накладных расходов веб-сервера — для того же mod_perl’а генерация страниц без кэширования (но и без особых изысканий) за 1.5 мс почти «фантастика». | |
− | [http:// | + | === {{CPAN|PSGI}}/{{CPAN|Plack}} === |
+ | |||
+ | Можно смело сказать, что {{CPAN|PSGI}}/[http://plackperl.org Plack] — самый современный и удобный интерфейс для Perl веб-приложений. | ||
+ | |||
+ | PSGI — Perl-реализация питонячьего [http://wsgi.readthedocs.org/ WSGI]. «Низкоуровневый» интерфейс взаимодействия тривиален: | ||
+ | * Приложение = функция. | ||
+ | * На входе 1 хешреф «окружения», в который включаются, во-первых, параметры, аналогичные %ENV в CGI (REQUEST_URI, PATH_INFO и т. п.)… | ||
+ | * …а во-вторых, некоторые PSGI-специфичные ключи, например, «input», содержащий входной поток. | ||
+ | * На выходе массив из 3-х элементов: численный статус ответа, массив заголовков в виде [ Header => Value, Header => Value, … ] и текст ответа. | ||
+ | |||
+ | Однако без дополнительных обёрток такой интерфейс неудобен, поэтому есть {{CPAN|Plack}}, причём имеющий вполне вменяемый интерфейс — например, Plack::Request->parameters возвращает в виде хеша все GET и POST параметры, Plack::Request->cookies — хеш кукисов и т. п. Это — в отличие от модулей типа CGI и PCGI, в которых параметры и куки нужно читать по отдельности через param() и cookie(). | ||
Плюсы: | Плюсы: | ||
+ | * Грамотный интерфейс без глобальных переменных. | ||
+ | * Совместимость с кучей разных серверов: можно запускать хоть под CGI/FastCGI (с помощью Plack), хоть в Apache ([http://github.com/spiritloose/mod_psgi/ mod_psgi]), хоть в [https://uwsgi-docs.readthedocs.org/ uWSGI], хоть в виде standalone демона (Starman, Twiggy, Corona и т.п, полный список на http://plackperl.org). Причём, standalone-реализации по отзывам вполне производительны. | ||
− | * | + | Минусы: |
+ | * Разве что отсутствие совместимости со «старыми» приложениями — то есть, нельзя просто обернуть CGI скрипт в функцию и превратить его таким образом в PSGI. | ||
+ | * (Не проверено) возможно, в PSGI серверах отсутствует поддержка перезагрузки модулей. | ||
+ | * В остальном, похоже, всё хорошо. | ||
+ | |||
+ | === HTTP::Server::Simple === | ||
+ | |||
+ | {{CPAN|HTTP::Server::Simple}} — простая реализация HTTP-сервера на Perl. | ||
+ | |||
+ | Плюсы: | ||
+ | |||
+ | * Интерфейс — CGI. В режиме '''nph''' — «Non-Parsed Headers» — то есть на STDOUT нужно выводить просто HTTP-ответ. Это устраняет часть интерфейсных ограничений CGI, сохраняя совместимость с CGI скриптами. | ||
+ | * Можно использовать стандартные модули Net::Server::'''xx''' для выбора поведения сервера — например, можно использовать как TCP, как и UNIX сокеты, можно создать prefork ({{CPAN|Net::Server::PreFork}} или {{CPAN|Net::Server::PreForkSimple}}) или мультиплексирующий однопоточный сервер ({{CPAN|Net::Server::Multiplex}}). | ||
+ | * Существует некоторое количество модулей для расширения данного сервера — в частности, для запуска приложений на некоторых фреймворках через HTTP::Server::Simple, например, для {{CPAN|Mason}}. Собственно, есть и вариант для запуска PSGI/Plack приложений. | ||
+ | * Модуль достаточно широко используется при тестировании веб-приложений, в качестве легковесного тестового сервера: {{CPAN|Test::HTTP::Server::Simple}}. | ||
Минусы: | Минусы: | ||
− | * | + | * Интерфейс CGI всё-таки требует по крайней мере выбора нужных модулей для разбора запросов и объединения всего этого функционала в своём приложении. |
+ | * Странная реализация parse_request() и parse_headers(), по меньшей мере, вплоть до версии 0.40 (можно полечить переопределением соответствующей функции). | ||
+ | ** Читает из стандартного ввода запрос и заголовки она по 1 символу функцией [http://perldoc.perl.org/functions/sysread.html sysread()], что весьма негативно сказывается на производительности. | ||
+ | ** В качестве разделителя строк, согласно всем стандартам, при обмене данными по сети, должно выступать сочетание CR-LF в платформо-независимом варианте: «\015\012». Тем не менее, функции HTTP::Server::Simple используют просто «\n», что тоже работает, но не является идеально переносимым вариантом. | ||
− | === HTTP::Server:: | + | === <s>LWP (HTTP::Daemon)</s> === |
+ | |||
+ | {{CPAN|LWP}} (''libwww-perl'') — библиотека для создания как клиентов, так и серверов, полностью совместимых со спецификацией HTTP/1.1, на чистом Perl’е. | ||
+ | |||
+ | В качестве единственной готовой, пусть и исключительно простой, платформы, исповедующей идеологию получения запросов в форме HTTP::Request и ответа HTTP::Response’ами можно рассмотреть {{CPAN|HTTP::Server::Brick}}, построенный на основе HTTP::Daemon. | ||
+ | |||
+ | Плюсы: | ||
+ | |||
+ | * Очень логичная и правильная '''идея''' программного интерфейса — {{CPAN|HTTP::Request}}, {{CPAN|HTTP::Response}}, {{CPAN|HTTP::Body}} и т. п. (HTTP::Body, кстати, использует {{CPAN|Catalyst}})… | ||
+ | |||
+ | Минусы: | ||
+ | |||
+ | * …Увы, оставшаяся без полной реализации. | ||
+ | * Главный минус в том, что LWP в качестве основы HTTP-сервера не использует '''практически никто'''. Посему, сколь логичным бы ни выглядел сервер, получающий на вход HTTP::Request и отвечающий HTTP::Response’ами, реально готовых наработок для такого подхода фактически нет. Например, нет ничего похожего на удобный Apache2::Request, с готовыми функциями для доступа к запросу, разобранному на URI, параметры POST, заголовки и куки. | ||
+ | |||
+ | == Заключение == | ||
+ | |||
+ | «Финалистами» являются [http://perl.apache.org/docs/2.0/ mod_perl 2.x], {{CPAN|CGI}} и {{CPAN|Plack}}. Остальное распространено слабо. | ||
+ | |||
+ | Победитель — однозначно {{CPAN|Plack}}. Он наиболее современный, простой и продуманный. | ||
+ | |||
+ | CGI — всё ещё жив, но безнадёжно устарел. Старые приложения, на нём написанные, нужно либо превращать в FastCGI, либо превращать в HTTP::Server::Simple (достаточно удобно и сделано в https://github.com/vitalif/bugzilla-4intranet), либо, на крайний случай, просто запускать через {{CPAN|FCGI::Daemon}}. | ||
+ | |||
+ | mod_perl — тоже популярен, хотя ни универсальностью, ни надёжностью от него не пахнет. «Perl и mod_perl — это разные языки». | ||
− | |||
[[Категория:Разработка]] | [[Категория:Разработка]] | ||
+ | [[Категория:Perl]] |
Текущая версия на 00:28, 22 ноября 2014
В статье рассматриваются платформы/библиотеки для запуска веб-приложений на языке Perl. Рассматриваются они именно как «серверы приложений» — в случае со standalone серверами не предъявляется требований гибкости обычного HTTP-сервера вроде nginx или Apache, безопасности, или производительности в обслуживании статических файлов, так как предполагается, что Perl-сервер будет всё равно «спрятан» за фронтенд наподобие nginx. Задача платформы — предоставить удобный программный интерфейс, такой, чтобы приложение могло получить запрос, предназначающийся ему, и сформировать на него адекватный ответ.Все они делятся на две категории — библиотеки, дающие возможность запуска Standalone HTTP-сервера, и интерфейсы, требующие для работы внешнего HTTP-сервера.
Фреймворки для построения веб-приложений — всевозможные Mason, Catalyst и т. п. — не рассматриваются.
Содержание
Требующие внешнего HTTP-сервера
Сюда относятся, во-первых, все реализации, предназначенные для встраивания Perl-интерпретатора в тот или иной веб-сервер, а во-вторых, дополнительные протоколы взаимодействия HTTP-сервера с Perl-программами или демонами, такие, как CGI.
CGI, PCGI
Старый добрый Common Gateway Interface (CGI — статья в русскоязычной Википедии), впервые реализованный где-то в районе 1993 года в первом HTTP-сервере NCSA HTTPd 1.0. Идея — максимально простой интерфейс взаимодействия с программой — через переменные окружения, а также стандартные ввод и вывод (STDIN и STDOUT). Веб-сервер при каждом запросе, который нужно обработать CGI приложением, запускает новый процесс (это самое приложение), передавая некоторые заголовки и данные в переменных окружения, а адрес запроса и содержимое (например, загружаемого на сервер файла) через стандартный ввод, после чего читает ответ (заголовки и содержимое) со стандартного вывода приложения.
Плюсы:
- Универсальность — запуск CGI скриптов поддерживается почти везде.
- Запуск нового интерпретатора, помимо минуса (1), означает и плюс — свободу от свойственных долгоживущим Perl демонам утечек памяти.
Универсальность CGI в смысле возможности использования произвольного языка программирования для нас ни плюсом, ни минусом, очевидно, не является, так как рассматривается именно Perl.
На этом плюсы заканчиваются и начинаются сплошные минусы:
- Самый очевидный и повторяемый всеми аки мантра минус — слабая производительность из-за необходимости запуска нового процесса интерпретатора при каждом запросе. Причин две — во-первых, это постоянно создаваемые процессы, которые могут быть совсем не лёгкими — раз, и вообще ограничивать производительность ОС большим числом переключений при большой нагрузке; во-вторых, это постоянные компиляции и инициализации модулей, занимающие приличное время.
- Второй наиболее неприятный минус — очень неудобный API наиболее стандартного модуля CGI.pm. Например:
- Нельзя нормально получить хеш со всеми параметрами, так как CGI->Vars работает криво: если в запросе передано несколько значений одного параметра (&a=1&a=2), то в Vars значением этого параметра будет конкатенация всех этих значений (то есть { a => '12' }). Хотя по логике вещей там должен быть массив [ '1', '2' ].
- CGI->cookie() не дружит с UTF-ными ключами.
- Следить за своевременным выводом заголовков и текста ответа нужно самому.
- У различных функцих весьма странные интерфейсы — с ключами, начинающимися на «-» и так далее.
- В CGI часто даже передаются не все заголовки запроса.
- Говнокод.
- На код самого CGI.pm страшно смотреть. В принципе, уже одно отсутствие
use strict
говорит о раздолбайстве авторов. - В районе года 2000-ного авторам вдруг взбрело в голову, что век амперсанда (&) в качестве разделителя параметров в строках запроса подошёл к концу, и что теперь все будут использовать вместо него точку с запятой (;). Соответственно поведение и разбора, и генерации URL изменилось — причём, если в случае с генерацией всё легко возвращается на свои места заданием опции -oldstyle_urls, то разбор URL неизменно разбивает их и по «&», и по «;», что влечёт за собой различные неприятные эффекты.
- Большинство модулей типа CGI::что-нибудь — дикая древность, исповедующая генерацию HTML кода без использования шаблонизаторов print()'ами, на содержимое которых повлиять без влезания в сами модули невозможно. XXI век на дворе, пора бы прекратить хотя бы это — ан нет, и в 2004, и даже в 2006 годах такие модули появлялись.
- На код самого CGI.pm страшно смотреть. В принципе, уже одно отсутствие
PCGI — те же яйца, только в профиль, чуть более аккуратно написанные и с защитой от слишком больших запросов. Все интерфейсные минусы сохраняются.
FastCGI / SCGI
Идея FastCGI — ликвидировать тормоза CGI, сохранив интерфейс. FastCGI-процесс обрабатывает не один запрос, а много — последовательно принимая их в цикле через Unix- или TCP-сокет. Таким образом, FastCGI-процессы, во-первых, могут быть запущены на другом физическом сервере, а во-вторых, становится возможно распределение запросов между несколькими долгоживущими процессами.
SCGI — фактически клон FastCGI, отличия лишь в формате передачи данных, который, как утверждают авторы, проще реализовать, но и возможностей у него меньше — например, STDERR не передаётся обратно HTTP-серверу. Поддерживается чуть менее широко: ngx_scgi_module, Apache, Lighttpd, Cherokee, Mathopd с неофициальным патчем.
Плюсы:
- Возможность запуска старых CGI-приложений в «ускоренном режиме» практически без дополнительных телодвижений. Хороший пример — awstats, представляющий из себя (внимание!) один CGI-скрипт на всё приложение, весом примерно 550 Кб. Один разбор такого скрипта занимает почти полсекунды… А под FastCGI это делается лишь однажды.
- Передача в скрипт любых HTTP-заголовков, какие душе угодны.
- Возможность самостоятельного управления пулом процессов или потоков.
- Возможность запуска FastCGI-скриптов на серверах, поддерживающих только CGI — в этом случае модуль FCGI обрабатывает 1 запрос и выходит.
Минусы:
- Склонность к утечкам памяти, особенно в случае использования большого числа устаревшего кода, рассчитанного на «умирание» скрипта после обработки каждого запроса в среде CGI.
- Взаимодействие по-прежнему ведётся через функции CGI, поэтому «интерфейсные» минусы CGI никуда не исчезают. Специальных обвязок для упрощения взаимодействия с сервером по протоколу FastCGI нет, кроме самой примитивной реализации — модуля FCGI.
mod_perl
mod_perl — модуль HTTP-сервера Apache, предназначенный для разработки модулей Apache на языке Perl. «Модулей» — потому, что API mod_perl’а почти полностью повторяет C’шное API апача. Поэтому же API сильно различается в 1-ой и 2-ой версиях. Хотя какая разница, всё равно про httpd 1.x и mod_perl 1.x уже никто не помнит.
Плюсы:
- Большая степень гибкости и возможности комбинирования с другими модулями Apache, в частности, засчёт наличия большого числа обработчиков разных стадий запроса. Своим Perl-модулем можно осуществлять не всю обработку запроса, а, например, только авторизацию.
- Весьма богатый и довольно удобный программный интерфейс, через который с Apache можно делать практически всё, что душа пожелает.
- Возможность с небольшими телодвижениями запускать CGI-приложения в среде mod_perl с помощью модуля Apache::Registry. Для примера использования доработанного модуля Registry можно посмотреть реализацию mod_perl.pl из Bugzilla.
- Модуль популярен. Есть множество наработок, многие из которых представляют собой весьма приятные продукты. Хороший пример — профилировщик NYTProf, разработанный именно для профилирования мод_перла.
Минусы:
- Чрезмерная завязка на внутреннее устройство веб-сервера Apache. На mod_perl’е вы разрабатываете модуль Apache. Приложения, написанные под mod_perl, не запустятся больше нигде.
- В mod_perl2 для различных функций существует по нескольку где-то конкурирующих, где-то дополняющих друг друга, а где-то сходных по функционалу, но разных по интерфейсу библиотек. Частично это диктуется совместимостью с mod_perl1. Примеры — куки: Apache2::Cookie, APR::Request::Cookie, запрос: Apache2::RequestRec, Apache2::RequestUtil, Apache2::Request, APR::Request. Пока я в первый раз допёр, что для того, чтобы получить в пользование
$r->dir_config()
, нужно сделатьuse Apache2::RequestUtil
… Это не так страшно, но некоторую путаницу всё-таки вносит. - Склонность к утечкам памяти. mod_perl течёт всегда, хоть ты его режь. Более-менее решается MaxRequestsPerChild.
- Проблемы с перезагрузкой модулей без перезапуска сервера. Apache2::Reload — кривой; перезагрузку можно реализовать ручками (см. https://github.com/vitalif/bugzilla4intranet), но и такой вариант всё равно иногда срывает апачу крышу, он забывает про все константы и валит Internal Server Error до ближайшего рестарта.
- Большое потребление памяти, ибо Perl сосуществует в одних и тех же процессах с остальными модулями Apache.
- Если нужно запустить несколько разных приложений или несколько экземпляров одного приложения, mod_perl создаёт проблемы по причине отсутствия изоляции загруженного кода модулей между приложениями. Решается это PerlOptions +Parent (в mod_perl 1.x не решалось), но в этом случае дочерние процессы Apache вырастают до совсем неприличных размеров по причине работы в каждом нескольких Perl-интерпретаторов вместо одного. Пул процессов-то общий.
- Время от времени в mod_perl всплывают совершенно удивительные и неуловимые глюки, особенно в необычных режимах вроде taint, или при использовании с некоторыми модулями или движками Apache. «Потому что Perl и mod_perl — это как бэ немного разные языки» (c). Например:
- При использовании mpm_itk, 2-го мод_перла и PerlOptions +Parent (дающей отдельный пул интерпретаторов виртхосту) глобальные переменные в пакетах (как my, так и our) перестают сохранять свои значения между запросами.
- При включённом taint mode и тоже в отдельном интерпретаторе, в составе Bugzilla проявляется следующий мистический баг:
- На входе строки
$oldstr
и$newstr
, обе не taint’ченные, в$newstr
есть запятые, в$oldstr
нет. Пишем два идентичных по семантике фрагмента кода:- Если написать
$oldstr =~ s/[,\s]+/ /g; $newstr =~ s/[,\s]+/ /g;
, то$newstr
почему-то становится tainted. - Если же написать
s/[,\s]+/ /g for $oldstr, $newstr;
, то обе, как и положено, остаются не tainted.
- Если написать
- Баг воспроизводится только в составе Bugzilla и только под мод_перлом, из контекста выдернуть его не получается.
- Ещё пример в taint mode из Багзиллы:
- Где-то в глубине души модифицированного модуля
Bugzilla::Field::Choice
конкатенируютсяBugzilla::Product->DB_COLUMNS->[0]
иBugzilla::Product->DB_TABLE
. При проверке Scalar::Util::tainted в момент конкатенации сами по себе они оба безгрешны, однако, грех (флаг tainted) зарождается при их конкатенации. Вот и как это понимать? Адам и Ева сами по себе были безгрешны, но соединившись?..
- Некоторые затрудения в автоматизированных отладке и профилировании приложений в среде mod_perl из-за неочевидности команд запуска. Многие инструменты эта неочевидность «смущает».
- Отсутствие mod_perl на подавляющем большинстве веб-хостингов. Потому что для админов серверов с кучами хомячков это — головная боль, в многопользовательской среде влекущая проблемы как с безопасностью, так и с надёжностью и производительностью. Всё по причине уже описанных минусов.
- Как вы думаете, почему так широко распространился язык PHP? Именно по причине простоты обслуживания.
ngx_http_perl_module
Идея — вызов Perl-кода из nginx. Скорее мёртв, чем жив.
Ссылка на документацию: http://sysoev.ru/nginx/docs/http/ngx_http_perl_module.html.
Плюсы:
- Возможность вызова Perl-кода из SSI, что делает nginx почти шаблонизатором.
- Простота, благодаря которой количество источников мистических ошибок, а также неуловимых утечек памяти сводится почти к нулю.
Минусы:
- Идея размещения приложения внутри nginx несколько лишает его легковесности. Также появляются некоторые проблемы с масштабируемостью в случае простоев (ожиданий ответов от СУБД и т. п.).
- Жёсткая завязка на использование внутри nginx. Больше нигде приложения, написанные под nginx, не заработают.
- Поразительное качество: невозможно получить список всех HTTP-заголовков, присутствующих в запросе, а можно лишь считывать их по одному. Остаётся только применять патч к src/http/modules/perl/nginx.xs.
- Отсутствие поддержки CGI среды внутри ngx_http_perl_module, наподобие mod_perl апача.
Standalone
Альтернативой встраиванию интерпретатора в процесс HTTP-сервера является запуск собственного HTTP-сервера, рассчитанного на расположение за обратным прокси (reverse proxy) — mod_proxy Апача, nginx'ом, или Squid'ом — в качестве фронтенда. Так делают, например, Jetty (Java) и Zope (Python). Это работает точно так же, как обработка запросов внутри сервера, только запросы отправляются другому HTTP-серверу.
У такого подхода — использования HTTP вместо встраивания интерпретатора в сервер, или вариаций на тему CGI — есть несколько приятных преимуществ. Фронтенд может балансировать и распределять по нескольким серверам нагрузку одновременно с передачей запроса, причём для этого существует множество готовых качественных инструментов. Работа администраторов упрощается, потому что конфигурация фронтенда для передачи запроса разным приложениям идентична, а сами приложения могут быть запущены под любым системным пользователем, на другом сервере, в jail’е, виртуальной машине, за аппаратным firewall’ом или внутри какой-нибудь другой системы безопасности. При отладке разработчик может взаимодействовать напрямую со своим приложением без необходимости запускать отдельный HTTP-сервер. Потребление памяти сокращается засчёт изоляции каждого придожения в своём наборе процессов.
Кроме того, в этом случае приложение свободно от накладных расходов веб-сервера — для того же mod_perl’а генерация страниц без кэширования (но и без особых изысканий) за 1.5 мс почти «фантастика».
PSGI/Plack
Можно смело сказать, что PSGI/Plack — самый современный и удобный интерфейс для Perl веб-приложений.
PSGI — Perl-реализация питонячьего WSGI. «Низкоуровневый» интерфейс взаимодействия тривиален:
- Приложение = функция.
- На входе 1 хешреф «окружения», в который включаются, во-первых, параметры, аналогичные %ENV в CGI (REQUEST_URI, PATH_INFO и т. п.)…
- …а во-вторых, некоторые PSGI-специфичные ключи, например, «input», содержащий входной поток.
- На выходе массив из 3-х элементов: численный статус ответа, массив заголовков в виде [ Header => Value, Header => Value, … ] и текст ответа.
Однако без дополнительных обёрток такой интерфейс неудобен, поэтому есть Plack, причём имеющий вполне вменяемый интерфейс — например, Plack::Request->parameters возвращает в виде хеша все GET и POST параметры, Plack::Request->cookies — хеш кукисов и т. п. Это — в отличие от модулей типа CGI и PCGI, в которых параметры и куки нужно читать по отдельности через param() и cookie().
Плюсы:
- Грамотный интерфейс без глобальных переменных.
- Совместимость с кучей разных серверов: можно запускать хоть под CGI/FastCGI (с помощью Plack), хоть в Apache (mod_psgi), хоть в uWSGI, хоть в виде standalone демона (Starman, Twiggy, Corona и т.п, полный список на http://plackperl.org). Причём, standalone-реализации по отзывам вполне производительны.
Минусы:
- Разве что отсутствие совместимости со «старыми» приложениями — то есть, нельзя просто обернуть CGI скрипт в функцию и превратить его таким образом в PSGI.
- (Не проверено) возможно, в PSGI серверах отсутствует поддержка перезагрузки модулей.
- В остальном, похоже, всё хорошо.
HTTP::Server::Simple
HTTP::Server::Simple — простая реализация HTTP-сервера на Perl.
Плюсы:
- Интерфейс — CGI. В режиме nph — «Non-Parsed Headers» — то есть на STDOUT нужно выводить просто HTTP-ответ. Это устраняет часть интерфейсных ограничений CGI, сохраняя совместимость с CGI скриптами.
- Можно использовать стандартные модули Net::Server::xx для выбора поведения сервера — например, можно использовать как TCP, как и UNIX сокеты, можно создать prefork (Net::Server::PreFork или Net::Server::PreForkSimple) или мультиплексирующий однопоточный сервер (Net::Server::Multiplex).
- Существует некоторое количество модулей для расширения данного сервера — в частности, для запуска приложений на некоторых фреймворках через HTTP::Server::Simple, например, для Mason. Собственно, есть и вариант для запуска PSGI/Plack приложений.
- Модуль достаточно широко используется при тестировании веб-приложений, в качестве легковесного тестового сервера: Test::HTTP::Server::Simple.
Минусы:
- Интерфейс CGI всё-таки требует по крайней мере выбора нужных модулей для разбора запросов и объединения всего этого функционала в своём приложении.
- Странная реализация parse_request() и parse_headers(), по меньшей мере, вплоть до версии 0.40 (можно полечить переопределением соответствующей функции).
- Читает из стандартного ввода запрос и заголовки она по 1 символу функцией sysread(), что весьма негативно сказывается на производительности.
- В качестве разделителя строк, согласно всем стандартам, при обмене данными по сети, должно выступать сочетание CR-LF в платформо-независимом варианте: «\015\012». Тем не менее, функции HTTP::Server::Simple используют просто «\n», что тоже работает, но не является идеально переносимым вариантом.
LWP (HTTP::Daemon)
LWP (libwww-perl) — библиотека для создания как клиентов, так и серверов, полностью совместимых со спецификацией HTTP/1.1, на чистом Perl’е.
В качестве единственной готовой, пусть и исключительно простой, платформы, исповедующей идеологию получения запросов в форме HTTP::Request и ответа HTTP::Response’ами можно рассмотреть HTTP::Server::Brick, построенный на основе HTTP::Daemon.
Плюсы:
- Очень логичная и правильная идея программного интерфейса — HTTP::Request, HTTP::Response, HTTP::Body и т. п. (HTTP::Body, кстати, использует Catalyst)…
Минусы:
- …Увы, оставшаяся без полной реализации.
- Главный минус в том, что LWP в качестве основы HTTP-сервера не использует практически никто. Посему, сколь логичным бы ни выглядел сервер, получающий на вход HTTP::Request и отвечающий HTTP::Response’ами, реально готовых наработок для такого подхода фактически нет. Например, нет ничего похожего на удобный Apache2::Request, с готовыми функциями для доступа к запросу, разобранному на URI, параметры POST, заголовки и куки.
Заключение
«Финалистами» являются mod_perl 2.x, CGI и Plack. Остальное распространено слабо.
Победитель — однозначно Plack. Он наиболее современный, простой и продуманный.
CGI — всё ещё жив, но безнадёжно устарел. Старые приложения, на нём написанные, нужно либо превращать в FastCGI, либо превращать в HTTP::Server::Simple (достаточно удобно и сделано в https://github.com/vitalif/bugzilla-4intranet), либо, на крайний случай, просто запускать через FCGI::Daemon.
mod_perl — тоже популярен, хотя ни универсальностью, ни надёжностью от него не пахнет. «Perl и mod_perl — это разные языки».