Domain sharding: реализация на Ruby on Rails и результаты применения

от автора

Решил я недавно на примере одного проекта узнать, насколько сильно влияет на скорость загрузки сайта domain sharding. Напомню, суть этой оптимизации в том, что статические файлы грузятся с разных доменов (которые, впрочем, могут указывать на один и тот же сервер), и это позволяет обходить ограничение браузеров на количество одновременных подключений к одному домену. Интуитивно кажется, что в случае большого количества мелких файлов это должно существенно ускорить загрузку сайта в целом. Проверим, так ли это на самом деле.

Вкратце опишу ситуацию: имеется довольно длинный одностраничный сайт, в процессе загрузки совершается чуть больше сотни запросов на загрузку статики (css, js, шрифты, изображения). Сайт написан с использованием Ruby on Rails 4.1.12, в качестве веб-сервера — puma-2.15.3, nginx отдаёт статику и смотрит на пуму. Запущено это всё на дроплете Digital Ocean в локации Frankfurt 1. И, имея такие начальные данные, нам надо перенести запросы на статику с доменов вида example.com на домены вида assets%{i}.example.com.

Прежде всего надо настроить отдачу статики с этих адресов. Для этого достаточно настроить соответствующие DNS-записи (у меня было просто установлена запись для *.example.com, в моём случае этого было достаточно), а затем изменить настройки nginx’а (в директиве server_name стоит регулярка, отлавливающая хосты вида assets0.example.com и assets0.example.ru, т.к. в моём случае сайт доступен с двух разных адресов):

server {   listen       80;   server_name  ~^assets\d\.(example\.com|example\.ru)$;    root /home/deployer/sites/example/current/public;    location ~ ^/assets/ {     expires 1m;     add_header Cache-Control public,max-age=259200;     break;   } } 

Затем необходимо изменить генерацию путей к статике на стороне приложения. В рельсах это элементарно: достаточно в config/production.rb добавить строчку

config.action_controller.asset_host = "assets%d.example.com" 

Тогда рельсы при генерации адресов будут чередовать хосты «assets0.example.com», …, «assets3.example.com». Кстати, я задавался вопросом, почему именно 4 адреса, а не 118 (по одному на каждый запрос, чтоб совсем прям параллельно-параллельно было). Во-первых, для каждого дополнительного хоста будет выполняться DNS lookup, и размещение на странице такого количества хостов только замедлит загрузку. Во-вторых, браузеры кроме лимита на количество одновременных запросов к одному хосту имеют лимит на общее количество одновременных запросов (конкретное значение лимита приведу в конце поста).

Магия рельс — это, конечно, хорошо, но в моём случае она не сработала бы из-за необходимости генерировать разные адреса при посещении сайта с разных доменов. Впрочем, настраивается это не сильно сложнее. Также я решил настроить возможность опционального включения/отключения domain sharding на сайте без необходимости изменения кода приложения. Проще всего это было сделать с использованием переменных окружения:

if ENV['DOMAIN_SHARDING'] == 'enabled'   config.action_controller.asset_host = Proc.new { |source, request|     if request       "assets#{rand(4)}.#{request.host_with_port}"     end   } end 

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

$ DOMAIN_SHARDING=enabled rails s -e production 

и всё работает! Ну, почти. Шрифты сломались, и в консоли браузера жалобы на Cross-Origin Resource sharing policy.

Текст сообщения

Font from origin ‘http://assets1.localhost:3000’ has been blocked from loading by Cross-Origin Resource Sharing policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin ‘http://localhost:3000’ is therefore not allowed access.

С грустной мыслью о том, что не всегда всё работает из коробки, рефлекторно полез узнавать что-нибудь про «Cross-Origin Resource sharing policy rails fonts». Увидел упоминание гема font_assets, в README нашёл строку «Sets Access-Control-Allow-Origin response headers for font assets» и решил, что это как раз то, что мне нужно.

Моя ошибка была в том, что нужно было сначала подумать. Тогда бы я сразу понял, что шрифты, как и остальная статика, на боевом сервере отдаются nginx’ом, который ни о каких гемах знать не знает. На деле же вышел небольшой квест: после подключения font_assets сломалось всё; нашёл, почему сломалось, поправил исходники, заработало; сделал форк, прописал его в Gemfile; обновил версию на продакшене; понял, что надо было сначала подумать; откатил версию, удалил форк.

Собственно, исправление ситуации на продакшене было простым: небольшая правка секции location решает проблему:

location ~ ^/assets/ {   expires 1m;   add_header Cache-Control public,max-age=259200;   add_header Access-Control-Allow-Origin *;   add_header Access-Control-Allow-Methods GET;   break; } 

В общем, на этом собственно настройка закончилась и я начал замеры.

Результаты измерений

Замерял так: открыл в инспекторе хрома вкладку Network, в фильтре ставил domain:*.example.com / domain:example.com, и обновлял страницу. Не самый высокотехнологичный способ, но позволяет отслеживать не только время загрузки, но и её характер. (На скриншотах показаны только нижние части графиков).

Со включенным кэшированием, без sharding
Со включенным кэшированием, с sharding
Со отключенным кэшированием, без sharding
Со отключенным кэшированием, с sharding
Со включенным кэшированием, без sharding, Firefox
Со включенным кэшированием, с sharding, Firefox

Со включенным кэшем итоговое время довольно сильно прыгало вокруг средних значений, но обычно с domain sharding загрузка происходила на ≈0.2-0.4 секунд быстрее. В FF окончание загрузки происходило примерно одинаково, но с включённым шардингом бОльшая часть файлов становилась доступной раньше. Также на графиках наглядно видны ограничения браузеров на количество одновременных соединений: максимум 6 к одному хосту, максимум 10 в целом.
С отключенным кэшем картина сглаживалась, но всё равно c шардингом было немного быстрее. Не совсем понял, почему, но при включении ограничения скорости до 750 kB/s без шардинга работало чуточку быстрее.

Подводя итоги: в общем, в те времена, когда браузеры разрешали всего два одновременных подключения, domain sharding улучшал ситуацию гораздо больше сильнее, но и сейчас его использование имеет смысл.

ссылка на оригинал статьи http://habrahabr.ru/post/274209/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *