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 origin from '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 поліпшував ситуацію набагато сильніше, але і зараз його використання має сенс.

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.