Однажды разглядывая «водопадный» дисплей работы сети в Опере по мере того, как в него грузился один из моих проектов, я понял, что пора и на нем сделать оптимизацию того типа, который указан в заголовке статьи. При довольно быстром в пол-секунды получении кода страницы, полсотни вставленных в нее картинок секунд на пять оттягивали заветное событие DOM loaded, да и разница между DOM loaded и Page loaded тоже была того же порядка. Растём-с.
Спецификация HTTP 1.1 рекомендует браузерам запрашивать не более двух соединений на хост одновременно. Принята спецификация довольно давно, еще когда деревья были кустарниками, а интернет приходил в дом с телефонного коммутатора, и тогда это имело смысл. Сейчас интернет изрядно потолстел и большинство разработчиков браузеров эту рекомендацию уже давно не соблюдают. Но тем не менее типичные умолчания болтаются где-то в районе 4-х коннектов на хост, а простых пользователей интернета, что способны ускорить его себе, просто увеличив это значение до доступного максимума, я пока не встречал. Так что рекомендации увеличить количество параллельных загрузок, доступных для клиента на серверной стороне, если число объектов веб-страницы превышает десятки штук, уже давно прописались во всех руководствах по оптимизации сайтов. Достигается это довольно простым способом: часть внедряемого контента страниц размещается на других поддоменах вашего сайта и соответственно меняются ссылки в его страницах. Физически эти файлы или скрипты могут располагаться на том же сервере, что и сайт, более того, в той же самой корневой папке.
Сразу коснусь того, что способ оптимизации, использованный мной, рассчитан на частный случай конфигурации доступного мне серверного ПО. Это кэширующий сервер Nginx, основной Apache2 с mod_php и ISP Manager Lite для управления хостингом. Сайт сделан на Drupal 6. Если ваше ПО другое, то добро пожаловать в комментарии для обсуждения.
Идея настройки заключалась в том что, раздавая статический контент - изображения Imagecache, агрегированные CSS и JS файлы, то есть все, что лежит в папке /sites/default/files/, вообще исключить Apache и PHP из процесса, а обойтись для быстроты одним Nginx. Начнём.
Поддомены
Добавляем поддомены в ISP. Я сразу создал 5 штук, чтобы потом уменьшить их число по результатам тестов на быстродействие. Например, такие:
static-0.moisait.ru
static-1.moisait.ru
static-2.moisait.ru
static-3.moisait.ru
static-4.moisait.ru
Если тема вашего сайта нагружена графикой, то сделайте поддомен и для нее, например, theme.moisait.ru.
ISP при этом добавляет в файлы конфигурации серверов настройки по умолчанию, которые надо изменить.
Конфигурация Apache
В файле /etc/apache2/apache2.conf находим секции каждого нашего нового поддомена и делаем их комментариями, дописав # в начале каждой строки. Если их просто удалить, то ISP будет восстанавливать умолчания при последующих изменениях конфигурации. С Apache закончено, он нам больше не нужен.
Конфигурация Nginx
В файле /etc/nginx/nginx.conf тем же способом делаем комментариями все новые секции server, относящиеся к нашим поддоменам и настраиваем свои.
Для каждого поддомена добавляем свою секцию server
# Слушаем 80 порт на IP-адресе сервера XXX.XXX.XXX.XXX
listen XXX.XXX.XXX.XXX:80;
# Имя и стандартный алиас поддомена
server_name static-0.moisait.ru www.static-0.moisait.ru;
# Корневая папка поддомена совпадает с папкой основного
root /var/www/путь/к/папке/основного/домена/moisait.ru;
# Если запрос браузера берет ресурс из файловой системы Drupal то ищем его там
location /sites/default/files/ {
# Если файл найден, то отдаем браузеру, иначе передаем на именованный location
try_files $uri [user=drupal]drupal[/user];
# Разрешаем браузеру кэшировать контент до 2 недель
expires 14d;
}
# Именованный location перенаправляет на основной сайт.
# Это позволяет работать Imagecache
location [user=drupal]drupal[/user] {
rewrite ^(.*)$ http://moisait.ru$1 break;
}
# Все файлы запрашиваемые не из файловой системы Drupal перенаправляем на основной сайт,
# чтобы исключить дублирование контента на разных доменах
location / {
rewrite ^(.*)$ http://moisait.ru$1 break;
}
}
Секции server остальных поддоменов настраиваются аналогично, различаясь только цифрой в поддомене
Настройки для поддомена theme.moisait.ru
listen XXX.XXX.XXX.XXX:80;
server_name theme.moisait.ru www.theme.moisait.ru;
root /var/www/путь/к/папке/основного/домена/moisait.ru;
# Если запрос браузера берет ресурс из папки темы сайта то ищем его там
location /sites/all/themes/ваша-тема/ {
# Если файл найден, то отдаем браузеру, иначе 404 ошибка
try_files $uri =404;
expires 14d;
}
location / {
rewrite ^(.*)$ http://moisait.ru$1 break;
}
}
Drupal
Теперь научим Drupal работать с контентом на нескольких поддоменах. Я не стал для этого делать отдельный модуль, а разместил в /sites/default/settings.php функцию custom_url_rewrite_outbound(), которая позволяет переписывать исходящие ссылки. Думаю принцип её работы понятен из кода.
static $number; # Номер ресурса для статического контента
if(strpos($path, 'sites/default/files/') === 0) {
if(!isset($number)) $number = 0;
$options['absolute'] = TRUE;
$options['base_url'] = 'http://static-'. ($number % 5) .'.moisait.ru'; // Один из пяти ресурсов
$number++;
}
elseif(strpos($path, 'sites/all/themes/ваша-тема/') === 0) {
$options['absolute'] = TRUE;
$options['base_url'] = 'http://theme.moisait.ru';
}
}
Тестирование
Теперь можно вооружаться тестером скорости загрузки страниц и уменьшая цифру в коде функции <?php($number % 5)?>
искать подходящий для вашего сайта оптимум опытным путём. В соответствующей статье советуют 2-3 хоста, но мне кажется, что это очень проектозависимая величина.
Заключение
В заключении скажу, что мне впервые пришлось заниматься такой оптимизацией да и с Nginx я знаком слабо. Есть еще кое-какие идеи, но нет времени на эксперименты, но может кто-то уже это делал. По этой причине предлагаю открыть здесь дискуссию по следующим вопросам.
1. Все настройки без изменений, на мой взгляд, подойдут для сервера nginx+php-fast-cgi+drupal
2. Какие будут настройки Apache, если нет Nginx?
3. Я не искал модули для данного функционала, но если они есть, то какие?
4. Есть ли в моем решении подводные камни и как все это с точки зрения безопасности?
5. Есть идея сократить адреса убрав системные пути.
6. Конфигурация автоматического превращения пресетов imagecache в поддомены?
7. Заработает ли это на Drupal 7?
8. Каждая картинка дублируется на нескольких адресах, повлияет ли на SEO?
9. Файлы, генерируемые модулем Advanced CSS/JS aggregation почему-то в данную функцию не попадают.
Welcome to comments
Источники
Оптимизация параллельных загрузок для минимизации издержек
RFC 2068. Hypertext Transfer Protocol -- HTTP/1.1 (8.1.4, стр. 46).
GTmetrix - Analyze Performance of...
Статья в блоге автора:
Быстрый способ оптимизации параллельных загрузок на Друпал и nginx
Комментарии
А как же [module=cdn] ?
Я смотрел этот модуль, но у меня 250 включенных и нужных модулей на этом проекте. Все, что можно сделать не касаясь drupal, делаю не касаясь.
Плюс этого своего решения я вижу в том, что на отдаче контента Drupal, PHP и Apache вообще не участвуют.
И нет ли где хорошей статьи о настройках CDN?
3. Модуль CDN - сам пробовал его. Решение очень простое. Модуль решает и 7, 9.
8. Судя по всему в поисковиках надо склеивать поддомены, или редирект 301?
Я вот для D7 хардкорно запилил замену путей .css .js и .img через file_url_alter(&$uri).
Пока что разбил на 3 поддомена.
К сожалению сайт не на своем сервере поэтому настраивать Nginx я не могу.
Сейчас все файлы переношу вручную, системные пути убраны. Я думаю можно зазюзать функцию - if (file_exists($filename)) в директории else копируем файл на нужный cdn.
Последний location 301 редирект и делает для всего, что не лежит в папке files. Но меня другое интересовало. Порядок картинок в страницах может быть разный и одна и та же на одних страницах может браться с поддомена static-0, а на других со static-1 и так далее. Как повлияет это на выдачу изображений в поиске?