Выпуск 8. Октябрь 2013
← Консольные приложения на Curses | Содержание | Обзор CPAN за сентябрь 2013 г. →Миграция веб-приложений с Dancer на Dancer2
Готова ли новая версия фреймворка Dancer2
для использования в промышленной эксплуатации? Сложно ли произвести миграцию существующего приложения на новую версию фреймворка? Какие преимущества даст переход, и есть ли недостатки? Данная статья попытается дать ответы на поставленные вопросы и осветить подводные камни миграции.
Что нового есть в Dancer2
На момент написания статьи на CPAN доступен дистрибутив Dancer2
версии 0.09, вышедший 2 сентября 2013 г., поэтому все факты, изложенные ниже, справедливы именно для этой версии.
Dancer2
— это легковесный веб-фреймворк для Perl нового поколения. Dancer2
был практически написан заново, чтобы решить некоторые серьёзные недостатки дизайна Dancer
, такие как использование глобальных переменных и синглтонов и отсутствие хорошего и ясного объектно-ориентированного внутреннего API.
В новой версии Dancer2
стал использоваться мощный и легковесный ООП-фреймворк Моо
, который позволил легко строить и расширять внутренние классы фреймворка. Особое внимание было уделено обратной совместимости, чтобы необходимость изменений в коде для перехода на новую версию фреймворка требовалось как можно меньше (в идеале не надо было исправлять ничего). Но, к сожалению, некоторые шероховатости в миграции и задержка с выпуском новой версии привели к тому, что Dancer2
был объявлен не как замещение, а как параллельный проект со своим пространством имён, в котором будут появляться новые фичи, в то время как Dancer
продолжит своё существование только в режиме поддержки и исправления ошибок.
Кроме внутренних изменений в Dancer2
на данный момент нет каких-то новых убийственных фич. На данном этапе Dancer2
лишь пытается догнать Dancer
по поддерживаемым возможностям.
Отличия Dancer2
При миграции веб-приложения на Dancer2
можно столкнуться с необходимостью внесения изменений как в код, так и в конфигурацию приложения. Попробуем разобрать, какие произошли изменения.
Отличия в конфигурации
В отличиe от Dancer
, в Dancer2
поддерживается конфигурационные файлы различного формата: yaml
, json
, apache
, ini
и прочие, которые понимает модуль Config::Any
. Таким образом, если кто-то испытывает дискомфорт в работе с YAML, может переписать конфигурационный файл config.yml
в удобный ему формат. Dancer2
будет пытаться найти конфигурационный файл нужного формата исходя из названия расширения файла.
Изменилось описание конфигурации шаблонизаторов и движков сессий, например:
template: "xslate"
engines:
xslate:
cache: 1
cache_dir: "cache"
в Dancer2
это нужно будет изменить на:
template: "Xslate"
engines:
template:
Xslate:
cache: 1
cache_dir: "cache"
Т.е. добавляется промежуточный параметр, указывающий тип движка: template
или session
.
Пример изменения конфигурации для движков сессий:
session: "memcached"
session_expires: 604800
memcached_servers: "127.0.0.1:11211"
Изменяется на:
session: "Memcached"
engines:
session:
Memcached:
session_duration: 604800
memcached_servers: "127.0.0.1:11211"
Значения каталогов, таких как confdir
и public
, невозможно установить в файле конфигурации или через вызов set
в приложении. Единственный пока способ изменить их значения с помощью переменных окружения:
DANCER_CONFDIR
DANCER_PUBLIC
Если вы устанавливаете эти переменные окружения в самом приложении, то это необходимо делать до вызова use Dancer2
, т.е. в секции BEGIN
.
Плагины
Dancer2
использует ООП-фреймворк Moo
, что серьёзно поменяло техническую реализацию модулей плагинов. Таким образом, использовать существующие плагины для Dancer
стало невозможно, и требуется переписывать их под Dancer2
, что в свою очередь может отразиться и на их API, и на опциях конфигурации, и в общем случае нельзя наверняка сказать — потребуется ли что-то менять в коде приложения, которое использовало возможности данного плагина.
Посмотреть список существующих плагинов для Dancer2
можно на странице проекта на github. Не все из них могут оказаться рабочими, так как API плагинов Dancer2
всё ещё в активной разработке.
Подключение Dancer2
В Dancer2
реализована поддержка лишь части аргументов при подключении модуля:
use Dancer2 qw( :tests :script !keyword )
Такой часто используемый аргумент загрузки модуля как :syntax
, в Dancer2
отсутствует. Несмотря на наличие опции :script
, в Dancer2
пока не реализована поддержка конфигурации через опции командной строки.
Изменения в DSL
В Dancer2
перенесены все директивы за исключением тех, которые помечены как устаревшие в Dancer
. Например, мне при переносе небольшого проекта на Dancer2
в коде не пришлось менять ничего, кроме замены use Dancer
на use Dancer2
.
Развёртывание веб-приложений
Наиболее рациональный способ развёртывания Dancer
-приложений — это запуск их в standalone-режиме с помощью plackup
и проксирование к ним запросов через фронтенд-веб-сервер Apache или Nginx. Также можно использовать запуск через FCGI. В этом отношении развёртывание веб-приложений на Dancer2
не изменилось.
Гораздо интересней вариант развёртывания с использованием mod_perl2 и Apache в режиме prefork или с MPM worker. Как известно, в Dancer
невозможно в рамках одного процесса запускать два разных приложения, так как они разделяют общие данные конфигурации и маршрутов.
Что же изменилось в Dancer2
? Рассмотрим конфигурацию с двумя виртуальными хостами:
<IfModule mpm_prefork_module>
StartServers 1
</IfModule>
PerlSwitches -I/srv/App1/lib
PerlSwitches -I/srv/App2/lib
<VirtualHost *>
ServerName app1.local
DocumentRoot "/srv/App1/public"
<Location />
SetHandler perl-script
PerlHandler Plack::Handler::Apache2
PerlSetVar psgi_app /srv/App1/bin/app.pl
</Location>
</VirtualHost>
<VirtualHost *>
ServerName app2.local
DocumentRoot "/srv/App2/public"
<Location />
SetHandler perl-script
PerlHandler Plack::Handler::Apache2
PerlSetVar psgi_app /srv/App2/bin/app.pl
</Location>
</VirtualHost>
Проверим их работу:
$ curl -s http://app1.local/ | grep '<title>'
<title>App1</title>
$ curl -s http://app2.local/ | grep '<title>'
<title>App1</title>
Как видно, в конфигурации с одним рабочим процессом первое запрошенное приложение фиксирует конфигурацию, и второе приложение начинает использовать шаблоны и маршруты первого приложения, т.е. в данной ситуации поведение Dancer2
нисколько не отличается от Dancer
.
Если обратиться к документации Dancer2
, то для объединения двух приложений в одно рекомендуется использовать Plack::Builder
в таком виде:
use App1;
use App2;
use Plack::Builder;
builder {
mount '/app1' => App1->dance;
mount '/app2' => App2->dance;
};
Такая конфигурация подразумевает работу двух приложений в рамках одного виртуального хоста, и приложения разделяются по префиксу в URI: /app1
и /app2
. К сожалению, на практике и такая конфигурация является нерабочей, поэтому вопрос об использовании двух и более приложений Dancer2
в рамках одного процесса остаётся открытым.
Производительность
Тест производительности не претендует на высокую точность, но позволяет примерно оценить относительные изменения в производительности между Dancer
и Dancer2
. С помощью утилит, которые генерирует шаблонное приложение, создадим веб-приложения на Dancer
и на Dancer2
:
$ dancer -a Bench::Dancer
$ dancer2 -a Bench::Dancer2
Запустим веб-сервер на основе Twiggy по очереди для каждого приложения и запустим утилиту для бенчмарка ab2
.
$ plackup -Ilib -E deployment -s Twiggy -a bin/app.pl -p 5001 &
$ ab2 -n 2000 http://127.0.0.1:5001/
Для Dancer
получим следующий отчёт:
Document Length: 5582 bytes
Time taken for tests: 10.657 seconds
Complete requests: 2000
Failed requests: 0
Requests per second: 187.66 [#/sec] (mean)
Time per request: 5.329 [ms] (mean)
Для Dancer2
отчёт будет такой:
Document Length: 5540 bytes
Time taken for tests: 12.192 seconds
Complete requests: 2000
Failed requests: 0
Requests per second: 164.04 [#/sec] (mean)
Time per request: 6.096 [ms] (mean)
Абсолютные величины здесь не интересны (тестовая система с весьма скромной производительностью), но можно оценить, что производительность в Dancer2
просела на ~13% при прочих равных условиях.
Объём занимаемой памяти
Объём занимаемой памяти Perl-программы зависит от версии интерпретатора и опций сборки. Для случая Perl 5.16.3, собранного в виде разделяемой библиотеки libperl.so для платформы x86_64 с поддержкой тредов, при запуске голого perl получаем такую статистику из /proc/$$/status
:
VmSize: 19108 kB
VmRSS: 2028 kB
VmData: 520 kB
VmLib: 4524 kB
Т.е. общий объём занятой виртуальной памяти — 19 МБ, из них 2 МБ находятся постоянно в памяти, 4,5 МБ — загруженные разделяемые библиотеки, 0,5 МБ — это объём сегмента данных.
Попробуем теперь сравнить объём занимаемой памяти минимальных Dancer
и Dancer2
приложений при запуске в standalone-режиме:
$ perl -Ilib bin/app.pl
Статистика для Dancer
:
VmSize: 59296 kB
VmRSS: 15940 kB
VmData: 13420 kB
VmLib: 4924 kB
Статистика для Dancer2
:
VmSize: 78848 kB
VmRSS: 23028 kB
VmData: 20252 kB
VmLib: 5512 kB
Таким образом, минимальное приложение на Dancer2
занимает уже на ~30% больше виртуальной памяти и на ~45% больше резидентной.
Зависимости
Dancer
и Dancer2
имеют довольно большое дерево зависимостей. С помощью утилиты strace
можно оценить количество модулей, которые требуются при загрузке минимального приложения:
$ strace -e open -o dancer.trace perl -Ilib bin/app.pl &
...
$ grep '\.pm' dancer.trace | wc -l
127
$ egrep 'perl5.+\.so' dancer.trace | wc -l
11
Таким образом, Dancer
в общей сложности загружает 127 модулей и 11 разделяемых библиотек XS-модулей.
Для Dancer2
статистика существенно тяжелее: 217 модулей и 18 разделяемых библиотек XS-модулей. То есть на ~70% больше загружаемых модулей, что и объясняет увеличение занимаемой памяти.
Для сравнения — Moose
загружает 109 модулей…
Выводы
Если после прочтения статьи у вас сложится впечатление, что мигрировать на Dancer2
не имеет смысла, то рекомендую вам всё же попробовать для эксперимента это сделать. Если что-то сломается, то информация об этом может оказаться очень полезной для разработчиков, чтобы к следующему релизу это могло быть исправлено.
← Консольные приложения на Curses | Содержание | Обзор CPAN за сентябрь 2013 г. →