Что роднит веб-девелоперов и стрит-рейсеров? Стремление выжать из своей “лошадки” максимум возможного. И для тех, и для других лишняя миллисекунда - это потенциальное поражение в конкурентной борьбе за славу и уважение миллионов фанатов. Возможно, с миллионами я слегка загнул, по крайней мере для гонщиков :)
Даже когда счет идет на тысячи постоянных посетителей, лишняя миллисекунда может вылиться в многие и многие бесцельно потраченные серверо-часы. Но что самое страшное - лишнее время загрузки страницы увеличивает шансы на то, что особо торопливый пользователь просто не станет дожидаться пока ваш сервер соизволит выдать ему требуемую информацию, плюнет и уйдет в Google искать ваших конкурентов. Менеджер проекта в ярости, инвесторы спрашивают где бабки, у вас стресс. Занавес.
Статика
Если вы занимаетесь веб-разработкой не первый день (а хотя бы второй), то вы наверняка знаете, что в процессе загрузки получение непосредственно самой страницы (HTML-кода) может занимать порядка 10-15%, редко когда больше. Львиную же долю времени отнимает загрузка статичных файлов - картинок, javascript’ов, CSS-файлов, флэшек и прочих прелестей.
Для быстрой отгрузки статичных файлов очень часто используется сервер Nginx. Если ничего не поменялось за последнее время, то он до сих пор считается самым быстрым веб-сервером для раздачи статики. Старикашка Apache за ним угнаться не может точно, другие тоже отстают. Поправьте если ошибаюсь.
Динамика
Однако, старикашка Apache тоже не лыком шит, у него есть свои плюсы. В контексте разработки Rails-проектов - это Passenger (он же mod_rails), который есть только под Apache.
Развертывая проекты под Nginx, для обработки динамики приходится стартовать отдельные rails-процессы (например, с использованием Thin, Mongrel и собратьев), причем чем активнее используется приложение, тем больше надо процессов. До поры до времени такой подход имеет право на жизнь, но однажды наступает один из возможных вариантов:
- Вы хотите сократить время загрузки на секунду-другую
- Вам надо запустить полсотни мелких рельсовых проектов с мизерной посещаемостью
- Вам надо запустить толстый проект на сервере с малым объемом памяти (например, на VPS-хостинге от Agava, Linode и других)
- У ваших инвесторов кончилось бабло на закупку железа под ваш мега-стартап
- Вас парит что в памяти сидят и курят бестолковые процессы по сотне метров
Меня настигли одновременно 4 пункта из 5, угадайте какие. Поэтому единственный вариант для меня - это переход на Passenger.
Лед и пламень
Однако, решил я, если уж браться за оптимизацию и минимизацию, то нужно свести воедино преимущества двух серверов - быструю отдачу статики Nginx’ом и экономию памяти за счет Passenger под Apache. Задача не очень сложная и просто требует понимания того, кто за что отвечает. В моем случае получается вот так:
- Apache - обрабатывает динамические запросы к Rails
- Nginx - отдает статичные файлы (картинки, css, js, flash), закэшированные страницы и осуществляет поддержку фрагментарного кэширования через SSI+memcached
Входящий запрос попадает на Nginx, который определяет, можно ли отдать статику. Если результат запроса нельзя отдать из статичного файла - Nginx проксирует запрос на Apache. Nginx вешается на порт 80, а Apache например на 3000.
Конфигурация Nginx (размещаем ее в /my_project_folder/nginx.conf):
upstream apache {
server 127.0.0.1:3000 max_fails=3 fail_timeout=30s;
}
server {
listen 80;
server_name myproject.com www.myproject.com somealias.com;
proxy_next_upstream error;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_redirect false;
proxy_max_temp_file_size 0;
# Задаем корневую папку из которой будет браться статика
root /my_project_folder/public;
location / {
proxy_set_header Host myproject.com;
if (-f /my_project_folder/_cache/$uri.html) {
root /my_project_folder/_cache;
rewrite (.*) $1.html break;
}
if (-f /my_project_folder/_cache/$uri/index.html) {
root /my_project_folder/_cache;
rewrite (.*) $1/index.html break;
}
if (!-f $request_filename) {
proxy_pass http://apache;
break;
}
}
}
Обратите внимание, в инструкции server_name мы прописываем все возможные доменные алиасы, по которым мы хотим видеть наш проект, однако в location мы через proxy_set_header задаем вполне конкретный Host. Это нужно для того, чтобы в виртуальных хостах Apache можно было прописать только главный домен. Если вам требуется обрабатывать запросы в зависимости от домена - пропишите так:
proxy_set_header Host $http_host;
Далее настраиваем Apache. Прежде всего нужно установить mod_rails следуя официальной инструкции. Затем прописываем виртуальные хосты (размещаем в /my_project_folder/apache.conf):
# Это лучше вынести в основной конфиг
Listen 3000
NameVirtualHost *
ServerName localhost
# Виртуальный хост, на который проксируются запросы
<VirtualHost *>
ServerName myproject.com
DocumentRoot /my_project_folder/public
</VirtualHost>
Собственно, теперь конфиг для виртуального хоста **Nginx** нужно подключить в основной конфиг **Nginx**:
http {
...
include /my_project_folder/nginx.conf;
}
А в основном конфиге Apache подключить виртуальный хост Apache:
Include /my_project_folder/apache.conf
Перезапускаем оба сервера и наслаждаемся :) Статику отдает Nginx, динамику Apache+Passenger.

Отличная статья. Задумался о переходе на mod_rails…