Тяжёлый back-end своими руками

Материал из Eludia
Перейти к: навигация, поиск

Содержание

В данной статье описывается один из вариантов настройки тяжёлого back-end'а на базе Eludia.pm, который можно использовать в комплекте с различными реверсными proxy-серверами. Критериями оптимизации в данном случае являются гибкость настройки, управляемость и защита от перерасхода ресурсов сервера (прежде всего, оперативной памяти).

Предыстория вопроса

Идея выделения "тяжёлого back-end'а" у сервера WEB-приложения применяется и развивается, пожалуй, на протяжении всей истории динамического WEB-программирования. Сочетание таких факторов как:

  • требование большой конкурентности;
  • большой процент запросов на статические файлы в общем потоке обращений;
  • весьма малые ресурсы, потребные для обслуживания статического запроса;
  • чрезвычайно большие ресурсы, потребные для обслуживания динамического запроса

естественно приводит к мысли о том, чтобы обслуживать статические и динамические запросы серверными процессами, у которых разделены как минимум адресные пространства. В конце 1990-х годов применялись конфигурации с 2 и более инсталляциями Apache 1.3, был активизирован mod_proxy, а у другой — например, mod_perl.

Эволюция mod_proxy сначала привела к возникновению специализированных Apache-модулей, таких как mod_accel, а затем отдельных серверов для высокопроизводительного реверсного проксирования: nginx, lighttpd, varnish.

Другие варианты развития той же идеи связаны с варьированием способов связи между front- и back-end'ом:

  • применение протоколов, отличных от HTTP (прежде всего, FastCGI и его аналогов);
  • общение не через TCP (как с применением FastCGI, так и безотносительно к нему: например, mod_pipe).

Минимальный WEB-сервер

Eludia.pm содержит собственный однозадачный WEB-сервер, базирующийся на стандартном Perl-модуле HTTP::Daemon и поэтому не требующий установки какого-либо ПО, кроме Perl. Например, для 8000-го порта он запускается командой

perl -MEludia::Content::HTTP::Server -e"start (':8000')"

в контексте директории приложения. В таком варианте сервер вполне пригоден для отладки, однако обладает 3 существенными недостатками:

  • однозадачный;
  • медленно обрабатывает статические запросы;
  • не отдаёт единожды занятую память ОС (что в классическом mod_perl решается методом "рождения и гибели": заданием конечного MaxRequestsPerChild).

Запуск в цикле

Последняя проблема решается тривиально: при помощи скрипта, который в бесконечном цикле запускает тот же WEB-сервер, указывая ему максимальное количество запросов (например, 100):

while [ 1 ]; do 

 perl -MEludia::Content::HTTP::Server -e"start (':8000', 100)" 2>>logs/error.log 
	
done

Исчерпав ресурс, сервер отправляется в перезагрузку. При использовании такой схемы напрямую может возникнуть возникнуть проблема: рестартующий сервер может быть сочтён несуществующим.

Настройка реверсного proxy

Такого рода затруднения отлично решаются современными multithreaded-серверами с серьёзной поддержкой реверсного проксирования и высокой эффективностью в работе со статикой. Это и сглаживает перебои, и вообще заметно разгружает back-end.

nginx

По нашим ощущениям, лучшим ПО такого рода на сегодня является nginx. Фрагмент его конфигурации для рассматриваемого случая имеет вид:

    upstream my_application {
        server 127.0.0.1:8000 max_fails=3 fail_timeout=30s;
    }
    
    sever {
    
        listen 80;
    
        root /var/projects/my_application/docroot; 

        location /i/ {
            expires 30d;
        }

        location = / {
            proxy_pass       http://my_application;
            proxy_set_header X_Forwarded_For $remote_addr;
            proxy_buffering  off;
        }

        location / {
            return 403;
        }
    }

Apache 2

Впрочем, увы, nginx доступен не столь повсеместно, как хотелось бы. В частности, под Win32 он пока доступен в несколько урезанном варианте: с установкой в фиксированный каталог и, главное, без возможности запуска в качестве сервиса.

А вот Apache 2 как раз изначально задуман так, чтобы на каждой платформе выжимать максимум из местных достопримечательностей. И этим вполне можно воспользоваться:

DocumentRoot "c:/Program Files/my_application/docroot"

ProxyPassMatch ^(/.*/.*) !
ProxyPass / "balancer://my_application/" maxattempts=3 timeout=2

<Location /i>
   SetHandler default
   ExpiresActive on
   ExpiresDefault "now plus 1 days"
   AddDefaultCharset windows-1251
</Location>

<Proxy balancer://polouchka>
	BalancerMember http://127.0.0.1:8000
</Proxy>

А кому-то нравится Apache 2 под UNIX. Ради-бога.

В любом случае — не забыть активировать модули:

LoadModule proxy_module modules/mod_proxy.so
LoadModule proxy_balancer_module modules/mod_proxy_balancer.so
LoadModule proxy_http_module modules/mod_proxy_http.so

И, да, разумеется, ставить bash под Windows вовсе не обязательно, но об этом чуть ниже.

Многозадачность

Задействовав, помимо 8000, ещё 1-2 порта, и добавив пару строк в конфигурацию proxy:

   upstream my_application {
       server 127.0.0.1:8000 max_fails=3 fail_timeout=30s;
       server 127.0.0.1:8001 max_fails=3 fail_timeout=30s;
       server 127.0.0.1:8002 max_fails=3 fail_timeout=30s;
       ...
   }

мы получим уже полноценный отказоустойчивый сервер приложения.

Скрипты запуска / останова

А как же "задействовать эти порты"? Не запускать же все процессы вручную. Для этого мы напишем 3 скрипта. 1 изних назовём my_application_loop.sh, его содержимое приведено выше.

Второй — запуск my_application_loop.sh в фоновом режиме для заданного диапазона портов:

for PORT in `seq 8000 8002`; do
	/var/projects/my_application/bin/my_application_loop.sh $PORT 10 &
done

И, наконец, последний — останавливающий — скрипт:

cd /var/projects/my_application 
killall -9 ea_my_application_loop.sh
for PORT in `seq 8000 8002`; do
	PIDFILE="logs/$PORT.pid"
	if [ -f $PIDFILE ]; then 
		PID=`cat $PIDFILE`
		kill $PID;  
	fi
done

Скрипты для Windows

Во всех вышеприведённых скриптах мы использовали bash и предполагали, что события разворачиваются под UNIX/Linux.

Для Windows не составляет особого труда сделать всё по аналогии, применив в качестве скриптового языка сам Perl. Удобно, к примеру, воспользоваться сборкой wperl.exe, которая запускает процесс без видимого окна и, таким образом, снимает необходимость наблюдать лишние консольные окна или рыть на стороне какие-нибудь хитрые утилиты.

Можно (наверное) прописать наш back-end как множество Windows-сервисов средствами Perl-модулей, но дело это крайне муторное. К примеру, если базироваться на дистрибутиве ActivePerl 5.8, то легко убедиться, что Win32::Daemon::Simple вроде бы устанавливается командой ppm, но имеет недокументированные зависимости, в частности, от Win32::Daemon, который уже доступен лишь в стороннем репозитории. И это только начало неприятностей.

В общем, на сегодняшний день апробирована кустарная, топорная, но понятная и надёжная схема управления процессами при помощи простых скриптов. Их автозапуск пока сочтём отдельной задачей. А исходные тексты не приведены здесь, поскольку имеет место...

Автоматизация процесса

Все необходимые скрипты и конфигурационные файлы генерируются по команде

perl -MEludia::Install -e bin

в директории приложения. У Вас запросят параметры (MaxRequestsPerChild и диапазон портов), после чего будет сформирован необходимый пакет файлов "под ключ". Если ядро Eludia.pm установлено где-то вне стандартного site/lib, не забудьте указать путь к нему в опции -I. Во все генерируемые файлы этот путь будет добавлен автоматически.

Параметры запрашиваются и файлы генерируются для 2 параллельных конфигураций: sea и sky, сразу на тот случай, если Вы решите организовать перезапуск WEB-сервера с нулевым временем простоя. Если нет, просто воспользуйтесь лишь одной из конфигураций.

Некоторые замечания

Разработка "на коленке" Perl WEB сервера, управляемого этажеркой скриптов, при живом mod_perl, вполне может показаться дикостью. Это бы так и было, если бы не ряд досадных обстоятельств:

  • вывод из эксплуатации Apache 1 даже под Linux (в частности, отсутствует в родном Debian 5);
  • однозадачность mod_perl 1 под Win32;
  • многочисленные конфликты mod_perl 2 со сторонними XS-модулями, в особенности под Win32;
  • отсутствие хорошего, стабильного (по крайней мере сопоставимого с mod_perl) многозадачного FastCGI-контейнера для Perl (в отличие от, скажем, PHP, поддерживающего PHP_FCGI_CHILDREN / PHP_FCGI_MAX_REQUESTS на уровне интерпретатора).

Поддержка фиксированного числа рабочих процессов также, возможно, на первый взгляд смотрится несуразно, однако практика показывает, что такая конфигурация ведёт себя гораздо более устойчиво и предсказуемо, чем схема с порождением дополнительных процессов по запросу. В реальности определяющим параметром настройки сервера является доступный объём памяти.

Далее, однозадачное программирование уже на уровне Perl-модулей, конечно, блокировало возможность экономии памяти за счёт загрузки модулей в shared memory, зато резко упростило отладку и ликвидировало привязку к платформе. Собственно, как показывают наблюдения за рабочими системами, даже под UNIX/Linux эффект от shared memory не особенно велик (порядка 10%) и абсолютно не прогнозируем, поскольку copy-on-write случаются, увы, весьма часто.

Персональные инструменты
Пространства имён

Варианты
Действия
Навигация
Разработчику
Администратору
Инструменты