Платформы для запуска Perl веб-приложений — различия между версиями

Материал из YourcmcWiki
Перейти к: навигация, поиск
(SCGI)
(ngx_http_perl_module)
Строка 67: Строка 67:
  
 
=== ngx_http_perl_module ===
 
=== ngx_http_perl_module ===
 +
 +
Идея — вызов 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].
 +
 +
Плюсы:
 +
* Возможность вызова Perl-кода из SSI, что делает nginx почти шаблонизатором.
 +
* Простота, благодаря которой количество источников мистических ошибок, а также неуловимых утечек памяти сводится почти к нулю.
  
 
Минусы:
 
Минусы:
* Идея несколько лишает nginx легковесности. Появляются некоторые проблемы с масштабируемостью в случае простоев (ожиданий ответов от СУБД и т. п.).
+
* Идея размещения приложения внутри nginx несколько лишает его легковесности. Также появляются некоторые проблемы с масштабируемостью в случае простоев (ожиданий ответов от СУБД и т. п.).
 
* Жёсткая завязка на использование внутри nginx. Больше нигде приложения, написанные под nginx, не заработают.
 
* Жёсткая завязка на использование внутри nginx. Больше нигде приложения, написанные под nginx, не заработают.
 +
* Отсутствие поддержки CGI среды внутри ngx_http_perl_module, наподобие mod_perl апача.
 +
* Версии ngx_http_perl_module до 0.6.22 имели следующие особенности:
 +
: Значения, возвращаемые методами объекта запроса $r, во-первых, хранились в памяти, выделяемой не perl’ом, а nginx’ом из собственных пулов, что в большинстве случаев позволяло уменьшить число операций копирования, а во-вторых, не завершались нулевым байтом. В некоторых ситуациях это приводит к ошибкам, например, при попытке использования таких значений в численном контексте, или использования незавершённых нулём строк в именах файлов и тому подобном:
 +
:* (FreeBSD) <pre>nginx in realloc(): warning: pointer to wrong page
 +
Out of memory!
 +
Callback called exit.</pre>
 +
:* (Linux) <pre>*** glibc detected *** realloc(): invalid pointer: ... ***
 +
Out of memory!
 +
Callback called exit.</pre>
 +
: Обход этих особенностей простой — нужно просто скопировать возвращённое значение в скаляр. Например, <source lang="perl">my $i = $r->variable('counter') + 1;</source> заменить на <source lang="perl">my $i = $r->variable('counter'); $i++;</source>
  
 
== Standalone ==
 
== Standalone ==

Версия 13:04, 7 августа 2009

В статье рассматриваются платформы/библиотеки для запуска веб-приложений на языке Perl. Рассматриваются они именно как «серверы приложений» — в случае со standalone серверами не предъявляется требований гибкости обычного HTTP-сервера вроде nginx или Apache, безопасности, или производительности в обслуживании статических файлов. Задача платформы — предоставить удобный программный интерфейс, такой, чтобы приложение могло получить запрос, предназначающийся ему, и сформировать на него адекватный ответ.

Все они делятся на две категории — библиотеки, дающие возможность запуска Standalone демона, и интерфейсы, требующие для работы внешнего веб HTTP-сервера.

Требующие внешнего HTTP-сервера

CGI

Старый добрый Common Gateway Interface (CGI — статья в русскоязычной Википедии), впервые реализованный где-то в районе 1993 года в первом HTTP-сервере NCSA HTTPd 1.0. Идея — максимально простой интерфейс взаимодействия с программой — через переменные окружения, а также стандартные ввод и вывод (STDIN и STDOUT). Веб-сервер при каждом запросе, который нужно обработать CGI приложением, запускает новый процесс (это самое приложение), передавая некоторые заголовки и данные в переменных окружения, а адрес запроса и содержимое (например, загружаемого на сервер файла) через стандартный ввод, после чего читает ответ (заголовки и содержимое) со стандартного вывода приложения.

Плюсы:

  1. Универсальность — скрипт, написанный с использованием CGI.pm, будет работать на любой платформе, так как CGI поддерживают все (почти все?).
  2. Простые вещи реализовать на CGI просто. Чем-то эта идеология похожа на идеологию Perl в целом — простые задачи он делает простыми, а сложные — возможными.
  3. Большое количество наработок, то есть, модулей для работы в CGI среде. В частности, к счастью разработчиков, есть модуль PCGI.pm, являющий собой вменяемую PHP-подобную реализацию CGI-протокола.
  4. Запуск нового интерпретатора, помимо минуса (1), означает и плюс — свободу от свойственных долгоживущим Perl демонам утечек памяти.

А вот универсальность CGI в смысле возможности использования произвольного языка программирования для нас ни плюсом, ни минусом, очевидно, не является, так как рассматривается именно Perl.

Минусы:

  1. Самый очевидный и повторяемый всеми аки мантра минус — слабая производительность из-за необходимости запуска нового процесса интерпретатора при каждом запросе. Причин две — во-первых, это постоянно создаваемые процессы, которые могут быть совсем не лёгкими — раз, и вообще ограничивать производительность ОС большим числом переключений при большой нагрузке; во-вторых, это постоянные компиляции и инициализации модулей, занимающие приличное время.
  2. Кривой и устаревший код многих модулей для работы с разными функциями CGI. Примеры:
    • CGI.pm написан без использования use strict. Это не страшно, но показатель раздолбайства авторов.
    • В районе года 2000-ного авторам CGI.pm вдруг взбрело в голову, что век амперсанда (&) в качестве разделителя параметров в строках запроса подошёл к концу, и что теперь все будут использовать вместо него точку с запятой (;). Соответственно поведение и разбора, и генерации URL изменилось — причём, если в случае с генерацией всё легко возвращается на свои места заданием опции -oldstyle_urls, то разбор URL неизменно разбивает их и по «&», и по «;», что влечёт за собой различные неприятные эффекты.
    • Многие модули CGI::xx исповедуют генерацию HTML кода без использования шаблонизаторов, обычными print()'ами, на содержимое которых повлиять без влезания в сами модули невозможно. XXI век на дворе, пора бы прекратить хотя бы это — ан нет, и в 2004, и даже в 2006 годах точно появлялись такие модули.
  3. Идеология CGI хоть чем-то и похожа на Перл, но фактически CGI больше рассчитан на простые задачи, нежели чем на сложные.
  4. Слабые возможности взаимодействия с HTTP-сервером — например, обычно в CGI даже не передаются все заголовки запроса. Кроме того, вывод ответа на STDOUT неудобен, если различные параметры задаются в различных местах программы.

FastCGI / SCGI

Идея FastCGI — ликвидировать недостатки CGI, сохранив интерфейс. Главный недостаток CGI — необходимость перезапуска приложения, поэтому его и ликвидировали в первую очередь: FastCGI-процесс обрабатывает не один запрос, а много — последовательно принимая их в цикле через Unix- или TCP-сокет. Таким образом, FastCGI-процессы, во-первых, могут быть запущены на другом физическом сервере, а во-вторых, становится возможно распределение запросов между несколькими процессами.

SCGI — практически это клон FastCGI, отличия лишь в формате передачи данных, который, как утверждают авторы, проще реализовать, но и возможностей у него меньше — например, STDERR не передаётся обратно HTTP-серверу. Поддерживается несколько менее широко: ngx_scgi_module, Apache, Lighttpd, Cherokee, Mathopd с неофициальным патчем.

Плюсы:

  1. Возможность запуска старых CGI-приложений в «ускоренном режиме» практически без дополнительных телодвижений. Хороший пример — awstats, представляющий из себя (внимание!) один CGI-скрипт на всё приложение, весом примерно 550 Кб. Один разбор такого скрипта занимает почти полсекунды… А под FastCGI это делается лишь однажды.
  2. Передача в скрипт любых HTTP-заголовков, какие душе угодны.
  3. Возможность самостоятельного управления пулом процессов или потоков.

Минусы:

  1. Склонность к утечкам памяти, особенно в случае использования большого числа устаревшего кода, рассчитанного на «умирание» скрипта после обработки каждого запроса в среде CGI.
  2. Взаимодействие по-прежнему ведётся через функции CGI, поэтому «интерфейсные» минусы CGI никуда не исчезают. Специальных библиотек для упрощения работы с протоколом FastCGI нет. Идеология, видимо, такова — а зачем, раз и так есть CGI.pm и компания?

mod_perl

Плюсы:

  1. Очень большая гибкость и возможность комбинирования с другими модулями Apache, в частности, засчёт наличия большого числа обработчиков разных стадий запроса.
  2. Весьма богатый и достаточно удобный программный интерфейс, через который с Apache можно делать практически всё, что душа пожелает.

Минусы:

  1. Чрезмерная завязка на внутреннее устройство веб-сервера Apache. Фактически — когда вы разрабатываете на mod_perl’е, вы фактически разрабатываете полноценный модуль Apache.
  2. Склонность к утечкам памяти. mod_perl течёт всегда, хоть ты его режь. Решение, правда, тоже несложное — MaxRequestsPerChild.
  3. Серьёзное увеличение размеров потомков Apache.
  4. Проблемы с перезагрузкой модулей в процессе обслуживания. Оговорка: это проблема Perl’а в целом, не только mod_perl’а. Но по крайней мере можно было бы предусмотреть быстрый «сброс» интерпретаторов по сигналу.
  5. Время от времени в mod_perl всплывают совершенно неуловимые глюки, особенно в необычных режимах вроде taint, и при использовании с некоторыми модулями или движками Apache. «Потому что Perl и mod_perl — это как бэ немного разные языки» (c). Например:
    • При использовании mpm_itk, 2-го мод_перла и PerlOptions +Parent (дающей отдельный пул интерпретаторов виртхосту) глобальные переменные в пакетах (как my, так и our) перестают сохранять свои значения между запросами.
    • При включённом taint mode и тоже в отдельном интерпретаторе, в составе Bugzilla 3.x проявляется следующий мистический баг:
    На входе строки $oldstr и $newstr, обе не taint’ченные, в $newstr есть запятые, в $oldstr нет. Пишем два идентичных по семантике фрагмента кода:
    • Если написать $oldstr =~ s/[,\s]+/ /g; $newstr =~ s/[,\s]+/ /g;, то $newstr почему-то становится tainted.
    • Если же написать s/[,\s]+/ /g for $oldstr, $newstr;, то обе, как и положено, остаются не tainted.
    Баг воспроизводится только в составе Bugzilla и только под мод_перлом, из контекста выдернуть его не получается.

ngx_http_perl_module

Идея — вызов Perl-кода из nginx.

Ссылка на документацию: http://sysoev.ru/nginx/docs/http/ngx_http_perl_module.html.

Плюсы:

  • Возможность вызова Perl-кода из SSI, что делает nginx почти шаблонизатором.
  • Простота, благодаря которой количество источников мистических ошибок, а также неуловимых утечек памяти сводится почти к нулю.

Минусы:

  • Идея размещения приложения внутри nginx несколько лишает его легковесности. Также появляются некоторые проблемы с масштабируемостью в случае простоев (ожиданий ответов от СУБД и т. п.).
  • Жёсткая завязка на использование внутри nginx. Больше нигде приложения, написанные под nginx, не заработают.
  • Отсутствие поддержки CGI среды внутри ngx_http_perl_module, наподобие mod_perl апача.
  • Версии ngx_http_perl_module до 0.6.22 имели следующие особенности:
Значения, возвращаемые методами объекта запроса $r, во-первых, хранились в памяти, выделяемой не perl’ом, а nginx’ом из собственных пулов, что в большинстве случаев позволяло уменьшить число операций копирования, а во-вторых, не завершались нулевым байтом. В некоторых ситуациях это приводит к ошибкам, например, при попытке использования таких значений в численном контексте, или использования незавершённых нулём строк в именах файлов и тому подобном:
  • (FreeBSD)
    nginx in realloc(): warning: pointer to wrong page

Out of memory!

Callback called exit.
  • (Linux)
    *** glibc detected *** realloc(): invalid pointer: ... ***

Out of memory!

Callback called exit.
Обход этих особенностей простой — нужно просто скопировать возвращённое значение в скаляр. Например,
my $i = $r->variable('counter') + 1;
заменить на
my $i = $r->variable('counter'); $i++;

Standalone

Альтернативой встраиванию интерпретатора в процесс HTTP-сервера является запуск собственного HTTP-сервера, рассчитанного на расположение за обратным прокси (reverse proxy) — mod_proxy Апача, nginx'ом, или Squid'ом — в качестве фронтенда. Так делают, например, Jetty (Java) и Zope (Python). Это работает точно так же, как обработка запросов внутри сервера, только запросы отправляются другому HTTP-серверу.

У такого подхода — использования HTTP вместо CGI — есть несколько приятных преимуществ. Фронтенд может балансировать и распределять по нескольким серверам нагрузку одновременно с передачей запроса. Работа администраторов упрощается, потому что конфигурация фронтенда для передачи запроса разным приложениям идентична, а сами приложения могут быть запущены под любым системным пользователем, на другом сервере, в jail’е, на виртуальной машине, за аппаратным firewall’ом или внутри системы безопасности. А при отладке разработчик может взаимодействовать напрямую со своим приложением без необходимости запускать отдельный HTTP-сервер. Кроме того, для обычного HTTP существует много готовых инструментов — прокси-серверов, балансировщиков нагрузки и прочих.

LWP (HTTP::Daemon)

LWP (libwww-perl) — библиотека для создания как клиентов, так и серверов, полностью совместимых со спецификацией HTTP/1.1, на чистом Perl’е.

Плюсы:

  • Очень логичный и правильный программный интерфейс — HTTP::Request, HTTP::Response, HTTP::Body и т. п., причём все эти модули используются очень широко и, в отличие от CGI.pm, выверены многими разработчиками.

Минусы:

  • Необходимость реализации алгоритма управления пулом процессов.

HTTP::Server::Simple