Pull to refresh

Comments 83

Отличная статья, узнал много нового
UFO landed and left these words here
search.cpan.org/~miyagawa/PSGI-1.03/PSGI/FAQ.pod#You_said_PSGI_is_similar_to_CGI._How_is_the_PSGI_interaface_different_from_CGI?
www.simon-cozens.org/content/i-finally-get-psgi-and-plack

Для меня лично профит в том, что вообще не нужно думать о способе подключения приложения к серверу — это чужая проблема, разработчику приложения нужно писать бизнес-логику приложения. Абсолютно прозрачно можно сменить способ подключения с CGI на FCGI, SCGI, mod_perl, whatever — лишь бы способ подключения удовлетворял требованиям приложения (если приложение может работать только при определенном значении переменных окружения psgi.*)

Сам лично PSGI/Plack не использовал, ибо увидел недавно, но хочу попробовать написать PSGI-сервер для моего AnyEvent::FCGI
UFO landed and left these words here
AnyEvent — абстракция для event loop, дающая единый интерфейс для программ, использующих event loop, сохраняющая возможность использовать любой event loop (EV, IO::Async), для которого есть поддержка. Чтобы многочисленные модули (AnyEvent::XMPP, AnyEvent::IRC) можно было писать для этой абстракции, а не для конкретного event loop.

DBI — абстракция над функциями работы с СУБД, дающая единый интерфейс для программ, взаимодействующих с СУБД, сохраняющая возможность использовать любую СУБД (MySQL, PostgreSQL), для которой есть поддержка. Чтобы многочисленные модули (DBIx::Class, Catalyst::Model::DBI) можно было писать для этой абстракции, а не для конкретной СУБД.

PSGI — абстракция для способов подключения веб-приложений к http-серверу, сохраняющая возможность использовать любой способ подключения (mod_perl, FastCGI), для которого есть поддержка. Чтобы многочисленные веб-приложения и веб-фреймворки можно было писать для этой абстракции, а не для конкретного способа подключения.
UFO landed and left these words here
Если вы говорите про CGI как способ подключения, а не про CGI.pm, то это не абстракция, а один из протоколов, над которым PSGI является асбтракцией.

Если вы сравниваете интерфейсы, предоставляемые конечному приложению, то они сильно похожи (также используются переменные окружения, и имена у них одинаковые).
UFO landed and left these words here
>а не для конкретного способа подключения
Зачем это делать? Для FCGI и mod_perl приложение всё равно пишется весьма разными способами. Одно приложение вряд ли будет запускаться 1-2-3мя методами — и PSGI выступает лишним слоем.
Если так судить, то и DBI не нужен — все равно в проекте будет использоваться только одна СУБД, зачем лишний слой абстракции?

Как минимум, такой подход нельзя использовать для фреймворков. Нельзя сказать, как программист захочет запускать приложение на фреймворке. Поэтому в каталисте, например, 4 разных файла для запуска приложения четырьмя разными способами (httpd, cgi, fcgi, mod_perl). В другом фреймворке эта проблема может быть решена по другому. Но такого бы не было, если бы существовал единый стандарт для подключения — теперь он есть.
DBI предоставляет удобный API. А здесь? Неочевидный формат обмена данными. Для того же CGI — целая пачка модулей, которые формируют куки, разбирают запрос — да что угодно.
Вы путаете CGI и CGI.pm. CGI — протокол, CGI.pm — это тоже своего рода абстракция (над тремя способами подключения — СGI, FCGI и mod_perl), плюс огромное количество в большинстве своем ненужных функций.
Вы видели CGI::Minimal, CGI::Simple, помимо CGI.pm? FCGI — это тоже CGI/Fast.pm, если уж идти дальше, а что даёт для удобства работы PSGI? На «голом» протоколе не пишет никто — или вы предлагаете изобретать велосипеды, без «в большинстве своем ненужных функций»? Ну так вы никогда проект не закончите, если будете от готовых библиотек отказываться.
Вы продолжаете путать CGI и CGI.pm (+ другие модули). Для конечного приложения CGI — это только то, что в %ENV лежат переменные окружения, а в STDIN — input stream. При использовании PSGI переменные окружения лежат в первом параметре функции (пусть будет $env), input stream — в $env->{'psgi.input'}. Эти различия можно устранить и использовать CGI.pm в PSGI-приложении, если вам так хочется. Пруф:

use CGI::Stateless;
sub {
    my $env = shift;
    local *STDIN; open STDIN, '<', $env->{'psgi.input'};
    local %ENV = %{$env};
    local $CGI::Q = new CGI::Stateless;
 
    [200, ['Content-Type' => 'text/plain'], ['Hi, ' . CGI::param('name')]]
}
Нет. Для конечного приложения голый протокол — бесполезен. Попытайтесь это понять.
Я это прекрасно понимаю, и не утверждаю обратное. Выше я привел способ использовать привычный всем CGI.pm для работы в PSGI-приложении.
UFO landed and left these words here
При использовании CGI сервер запускает приложение на каждый запрос, что приводит невозможности хранить состояние в памяти и к проблемам с производительностью. При использовании PSGI ваше приложение можно будет без проблем запустить и под CGI, и под FCGI, и под чем-либо угодно. Сегодня сделали возможность запуска на встроенном perl в nginx — PSGI-приложение можно будет без изменений запустить в nginx, завтра, например, сделают возможность запуска на Google AppEngine — изменять ничего не придется.
Нету ничего кроме CGI и FastCGI в живой природе. И оба они используются без изменения кода, если писать в стиле (из мануала):

#!/usr/bin/perl
use CGI::Fast;
&do_some_initialization();
while ($q = new CGI::Fast) {
&process_request($q);
}
Разработчики Catalyst считают иначе. Я уже писал где-то в комментариях, что только для запуска приложения разными способами сделано 4 разных .pl-файла
Иначе считают что?
И Ваш аргумент про Catalist только против Вас играет. Они же не поленятся и сделают пятый — для PSGI. Появится ещё какой-то гениальный способ сделать простую вещь — ещё одна абстракция над функцией param() — они и её будут поддерживать.

И всё это не отменяет того факта, что CGI::Fast достаточно для реальной жизни. Сейчас кто-то будет заморачиваться с mod_perl?
UFO landed and left these words here
К примеру, потому что GAE не будет поддерживать CGI, или будет предъявлять определенные требования к приложению, не суть — это просто пример для того, что сегодня CGI, завтра FCGI, послезавтра что-либо ещё. В случае использования PSGI все это — не ваша проблема.
UFO landed and left these words here
Если будет поддерживать хотя бы один из протоколов, для которых есть реализация серверов Plack, то PSGI-приложение можно будет запускать. Например, если поддерживаемым протоколом будет CGI: search.cpan.org/perldoc?Plack::Server::CGI
UFO landed and left these words here
UFO landed and left these words here
UFO landed and left these words here
Нет, я же выше по треду дал ссылку, как запустить PSGI-приложение, если есть поддержка, например, CGI.
UFO landed and left these words here
Просто в случае отсутствия поддержки PSGI и, например, наличия поддержки CGI нужно будет написать CGI-приложение, аналогичное тому что написано тут, из которого будет произведен запуск собственно PSGI-приложения
UFO landed and left these words here
Да, на plackperl.org в шапке написано, что «PSGI is inspired by Python's WSGI and Ruby's Rack.»
Какая-то непонятно зачем штука. mod_perl уже практически не используется, FCGI и CGI обрабатываются и так одинаково без изменения кода, других методов в жизни я не встречал. Вопрос: «зачем это понадобилось» так и не раскрыт. Чтобы «было как в Руби» — не аргумент.
В принципе затем же, зачем нужно, например, разделение бизнес-логики и логики отображения. Остальное я написал тут.
Какое отношение логика отображения имеет к вопросу?
Такое, что, к примеру, в веб-приложениях принято отделять логику отображения от бизнес-логики. PSGI позвоялет отделить бизнес-логику от логики взаимодействия с http-сервером.
Вся логика взаимодействия с http сервером начинается и заканчивается gjkextybtv параметров функцией param(), и чтением/установке кукисов — что делает обычно менеджер сессий и что непонятно как делает PSGI. Отделять бизнес-логику от взаимодействия с сервером позволяет Catalist. CGI интерфейс задаётся браузером, и от него абстрагироваться невозможно. В общем, как написал автор выше — словоблудие.
функцией param()

Видимо, вы имеете в виду CGI::param — это функция из модуля CGI.pm. Это уже не логика взаимодействия с сервером и даже не касается собственно интерфейса CGI.
чтением/установке кукисов — что делает обычно менеджер сессий и что непонятно как делает PSGI

Это тем более не задача интерфейса. Менеджер сессий, урл-диспатчер — это должно реализовываться во фреймворке.
Отделять бизнес-логику от взаимодействия с сервером позволяет Catalist

В Catalyst эта задача решена использованием HTTP::Engine — решение хорошее, на нем отдельно остановились в PSGI::FAQ — можно почитать по ссылке в статье.
CGI интерфейс задаётся браузером

Дальше не читал

Ещё можно почитать этот комментарий. PSGI — это интерфейс, а не фреймворк или что-либо ещё. Такой же интерфейс, как DBI или AnyEvent.
DBI — унифицированный интерфейс к десяткам движков баз данных. PSGI — унифицированный интерфейс к нескольким вариантам запуска CGI скриптов — CGI, FastCGI, SCGI, mod_perl, из которых реально сейчас используются только два — CGI и FastCGI, которые между собой не имеют серьёзных отличий при использовании. Поэтому аналогия с DBI неприменима. Решается проблема унификации, когда унифицировать нечего — есть один вариант CGI/FCGI.
UFO landed and left these words here
Вот на днях запускал мелкий сайтец под Dancer + Plack. Шутки ради решил прогнать ab. Результат: каждый новый запрос работает медленнее предыдущего. После быстрой обработки 500 запросов стали заметны тормоза, между 900 и 1000-ным вообще целая вечность прошла. Проверял на своем компе, на сервере — то же самое. Плюс апач при обычном сёрфе по сайту начинает иногда тупить с ответом, даже на статику.

Вещь хорошая, но по-моему сыроватая.
Вполне может быть, технологии полгода от силы.
UFO landed and left these words here
Чем будет являться PSGI-приложение будет зависеть от того, как вы его запустили. Через nginx — оно будет выполняться в рабочем процессе nginx. Через mod_psgi — будет частью апача. При запуске через Plack это будет зависеть от указанного в параметре --server имени при вызове plackup. В статье описан такой запуск:
plackup --server Coro --port 9090 --host 127.0.0.1 test.psgi

Эта команда запускает test.psgi на неблокирующем standalone http-сервер на Plack::Server::Coro. Если указать --server FCGI, то приложение будет запущено как FastCGI-сервер через Plack::Server::FCGI. Если указать --server CGI, то как CGI-приложение, через Plack::Server::CGI.
UFO landed and left these words here
На второй. Сейчас отвечу на первый:
Браузер -> вебсервер -> PSGI-модуль, общающийся с сервером по выбранному протоколу -> Middleware -> приложение
далее:
1) синхронный вариант: приложение -> Middleware -> PSGI-модуль -> вебсервер -> Браузер
2) асинхронный вариант: приложение (которое сохраняет колбэк, передаваемый в качестве второго параметра) -> ожидание вызова колбэка -> Middleware -> записать ответа окончена? (PSGI-модуль -> вебсервер -> Браузер): (возвращаемся в ожидание вызова колбэка)

Вроде так.
UFO landed and left these words here
1. В случае использования Apache это будет mod_psgi, в случае nginx — патч, прививающий поддержку PSGI, в случае Plack — Plack и Plack::Server:: выбранный_модуль

2. См. 1, Plack будет PSGI-модулем если приложение запускать используя его, то есть через вызов plackup
UFO landed and left these words here
Да, middleware получает и может анализировать и изменять переменные окружения, которое получит приложение, и получает и может менять ответ приложения.
UFO landed and left these words here
UFO landed and left these words here
Можно, демонизацию, разделение master-worker и рожание воркеров делает Plack::Server::FCGI, опции к нему описаны тут и могут передаваться через командную строку. Вот, например, запуск приложения в четыре воркера:
plackup --server FCGI --nproc 4 app.psgi
UFO landed and left these words here
Запускает приложение используя сервер указанный в параметре --server на порту, указанном в --port (по умолчанию 5000)
UFO landed and left these words here
Да, и в конфиге nginx можно указывать fastcgi_pass localhost:5000
UFO landed and left these words here
# тут код инициализации
# ...
sub {
    # тут то что будет исполняться воркерами
}
UFO landed and left these words here
Форк и поднимание упавших воркеров внутри Plack::Server::FCGI. В приложении нужно только написать код инициализации и код воркера.
UFO landed and left these words here
Нет, не в каждом, только при инициализации. Приложение — это обычный модуль, вначале которого делается вся инициализация, а потом return sub {а тут уже код воркера}. Просто во всех примерах return опущен за ненадобностью.
UFO landed and left these words here
Было бы, конечно, здорово, но в мои планы входил краткий обзор технологии, а в статье приведены ссылки на полную документацию. Перевод всей документации я вряд ли осилю. Самый простой способ понять — взять и попробовать, все заводится легко и быстро :)
UFO landed and left these words here
Полностью поддерживаю. Очередная странная дырявая абстракция. Наверняка станет очень популярной, как многие другие аналогичные вещи. :)

Если надо подтвердить, что я тоже немного разбираюсь в способах взаимодействия приложения с веб-сервером — вот моя реализация FastCGI для EV-ориентированных приложений.

Кстати, описываемый Вами в статьях про FCGI модуль FCGI::ProcManager написан несколько наивно (его автор, судя по всему, APUE Стивенсона не читал). Вообще, надёжная реализация менеджера процессов — это очень нетривиальная задача. Поэтому я и предпочёл избежать работы с процессами, а вместо неё использовать event-ориентированный подход.
Блин, топик создавал совсем не для фаллометрии, но раз уж на то пошло, то я автор аналогичной вещи для AnyEvent :) При создании модуля больше всего использовал FCGI::Async, но ваш модуль тоже смотрел, о чем написал в POD.

Судя по вашему комментарию в одном из моих топиков, вам и AnyEvent не нужен :) По сути своей PSGI похож на AnyEvent и предназначен для тех же целей.
По сути своей PSGI похож на AnyEvent и предназначен для тех же целей.

Предназначен для тех же целей — я тут имел в виду не «предназначен для подключения приложений к серверам», а «предназначен для создания единого интерфейса для множества похожих модулей/способов»
вам и AnyEvent не нужен
Вы меня не правильно поняли. Не «не нужен» — я считаю что использование AnyEvent — это плохая идея. Как я уже писал в том комментарии, мы тестировали все доступные на тот момент реализации event loop. На простых задачах они все работают одинаково, и разница только в удобстве интерфейсов и количестве и качестве кода в реализации модуля. Но под высокой нагрузкой и/или при длительной активной работе у всех event loop, кроме EV, всплывали серьёзные проблемы, такие как: потеря событий, низкая производительной, memory leaks, и segfault-ы. Простите, но я не могу рекомендовать использование таких модулей. Это звучит несколько высокопарно, и я это уже писал в том комментарии, но event loop это действительно сердце event-ориентированных приложений, и он обязан работать как часы. А использование AnyEvent, с моей точки зрения, это и есть рекомендация использовать любые event loop, а не тот единственный, который надёжно работает.

Ещё одна проблема с AnyEvent связана с тестированием. Если вы заявляете, что ваше приложение кросс-платформенное — будьте добры проверить перед релизом, что оно действительно работает на всех заявленных платформах. Если мы пишем модуль на AnyEvent, то перед релизом нам нужно его протестировать со всеми event loop, которые поддерживается AnyEvent… причём протестировать в т.ч. под нагрузкой и при длительной работе. Это заметно добавляет работы, даже если забыть про баги разных event loop-ов, которые при этом всплывут. Причём работы не интересной, здесь нет fun-а, без которого бесплатный открытый софт писать мало кто будет.
Статьи у вас замечательные, мне помогли в свое время, спасибо :)

Особенно мне понравился один из последних Ваших комментариев, типа «если сервер не поддерживает PSGI то надо просто написать скрипт CGI который будет запускать PSGI». Офигенно. Зачем мне тогда было писать скрипт PSGI, если все-равно нужно будет писать скрипт CGI?Этот комментари был к тому что PSGI-приложение можно запустить везде, где нет явной поддержки PSGI, но есть любой способ подключения, для которого есть реализация Plack::Server (там был CGI)

Смысла бросаться переделывать все на PSGI нет, но для своих новых приложений я буду использовать PSGI. Почему? Как минимум для меня это удобно — для тестирования приложение я запущу как однопоточный standalone HTTP-сервер, в продакшене это будет FCGI, причем для такой смены мне не придется прилагать никаких усилий, и вообще думать о тонкостях реализации подключения не буду — всё уже сделано до меня, нужно просто выбрать полностью готовый способ подключения.
Пример, который я привёл, Вы не поняли? Там как раз демонстрируется возможность использования CGI как в «однопоточный standalone HTTP-сервер», так и в FCGI, без изменения в исходных кодах.
Вы про этот пример? Я не вижу там HTTP-сервера, вижу CGI или FastCGI приложение. Отсутствие различий достигается использование модуля CGI::Fast, который устраняет различия для двух методов подключения — CGI и FastCGI. Использование PSGI + CGI::Stateless (как здесь) устраняет различия для всех методов подключения, которые поддерживаются PSGI, а не только для двух.
А что такое «однопоточный standalone HTTP-сервер»?
Pure-perl HTTP-сервер. Пример — сервер, на котором работает Catalyst-приложение, запускаемое через script/app_server.pl
Sign up to leave a comment.

Articles