Выпуск 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 г.
Нас уже 1393. Больше подписчиков — лучше выпуски!

Комментарии к статье