- PVSM.RU - https://www.pvsm.ru -
От переводчика: Это шестая статья из цикла о Node.js [1] от команды Mozilla Identity, которая занимается проектом Persona [2].
Эта статья посвящена etagify [9] — модулю middleware для Connect, который генерирует ETag-и на лету на основе MD5-хешей ответов, и хранит эти хеши в памяти. Etagify избавляет от лишней рутины при сборке проекта, предельно прост в использовании и увеличивает производительность больше, чем можно было бы ожидать (в своих тестах мы получили ускорение загрузки страниц на 9%):
myapp = require('express').createServer();
myapp.use(require('etagify')());
...
app.get('/about', function(req, res) {
res.etagify();
var body = ejs.render(template, options);
res.send(body);
});
Рассмотрим подробнее, как работает etagify, когда его стоит и когда не стоит использовать и как измерить результат его применения. На случай, если вы не совсем хорошо представляете, что такое Etag-и, и как с ними работать, я написал небольшую шпаргалку [10].
Благодаря тому, что etagify нацелена на один конкретный сценарий использования, эта крошечная библиотека имеет всего сотню строк вместе с документацией. Взглянем на ключевые пятнадцать строк, оставив за скобками обработку граничных случаев с заголовками Vary.
Etagify делает две вещи — считает хеши для отдаваемых клиенту ресурсов и хранит их в памяти, чтобы проверять версию ресурса при обработке условных GET-запросов.
Вот как формируется массив хешей:
// Принцип работы etagify.js
// В начале массив хешей пуст.
// вот как выглядит типичная запись:
// '/about': { md5: 'fa88257b77...' }
var etags = {};
var _end = res.end;
res.end = function(body) {
var hash = crypto.createHash('md5');
// если есть тело ответа, хешируем его
if (body) { hash.update(body); }
// и добавляем в наш массив
etags[req.path] = { md5: hash.digest('hex') };
// передаём управление дальше
_end.apply(res, arguments);
}
А вот так проверяется версия ресурса:
return function(req, res, next) {
var cached = etags[req.path]['md5'];
// Если у нас уже есть ETag, добавляем его в заголовок
if (cached) { res.setHeader('ETag', '"' + cached + '"' }
// если браузер отправил условный GET,
if (connect.utils.conditionalGET(req)) {
// Проверим, не совпадают ли If-None-Match и ETag
if (!connect.utils.modified(req, res)) {
// Закешированная браузером версия ресурса совпадает с актуальной.
// вырезаем из заголовка ETag и возвращаем клиенту статус 304 Not modified
res.removeHeader('ETag');
return connect.utils.notModified(res);
}
}
}
Подход etagify предельно прост и хорошо работает для динамически создаваемых страниц, которые не меняются во время работы сервера, например [11] мультиязычный статический контент. В других случаях использование etagify может вызвать проблемы.
Vary:cookie
для раздельного кеширования индивидуальных страниц, то массив хешей etagify быстро разрастётся до огромных размеров.Vary:cookie
отсутствует, то всем пользователям будет показана одна и та же версия, первой попавшая в кеш.Мы не ожидали серьёзного роста скорости от использования etagify, так как всё равно приходится гонять условные GET-запросы, и экономия составляет всего несколько килобайт на страницу. Тем не менее оптимизация с использованием etagify очень проста, так что даже небольшой прирост оправдает затраченные усилия.
Мы запустили экземпляр Persona на awsbox [12] (об awsbox речь пойдёт подробнее в последней статье цикла), открыли firebug и сделали по 50 замеров скорости загрузки страницы «about» с etagify и без (Время загрузки страницы имело большое значение для нас. В других проектах важнее могут быть другие метрики — время до начала отображения контента, или время до показа первого рекламного сообщения и т.п.).
Мы проанализировали полученные данные — вычислили среднее значение и стандартное отклонение для обоих наборов данных, предположив, что они распределены нормально.
К своему удивлению, мы обнаружили, что etagify уменьшает время загрузки страницы на 9% c 1,65 (с отклонением 0,19) до 1,50 (с отклонением 0,13) секунд. Неплохой результат, если учесть как мало усилий он потребовал.
Затем мы применили t-критерий Стьюдента [13], чтобы узнать, с какой вероятностью подобный результат мог быть достигнут без etagify. P-значение [14] оказалось меньше 0,01, то есть вероятность случайного появления такого же распределения меньше 1%. Таким образом измеренное улучшение статистически значимо.
Вот как это выглядит на графике:
etagify — крошечный, но очень полезный для нас модуль. Даже если в вашем проекте он не найдёт применения, мы надеемся что наш подход к разрешению проблем, который заключается в создании узко специализированных инструментов для каждой конкретной задачи и скрупулёзном замере их эффективности, даст вам вдохновение и пищу для ума.
Автор: ilya42
Источник [15]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/node-js/45230
Ссылки в тексте:
[1] цикла о Node.js: https://hacks.mozilla.org/category/a-node-js-holiday-season/
[2] Persona: http://ru.wikipedia.org/wiki/Mozilla_Persona
[3] Охотимся за утечками памяти в Node.js: http://habrahabr.ru/company/nordavind/blog/195494/
[4] Нагружаем Node под завязку: http://habrahabr.ru/company/nordavind/blog/195686/
[5] Храним сессии на клиенте, чтобы упростить масштабирование приложения: http://habrahabr.ru/company/nordavind/blog/196018/
[6] Производительность фронтэнда. Часть 1 — конкатенация, компрессия, кэширование: http://habrahabr.ru/company/nordavind/blog/196358/
[7] Пишем сервер, который не падает под нагрузкой: http://habrahabr.ru/company/nordavind/blog/196518/
[8] ETag: http://ru.wikipedia.org/wiki/HTTP_ETag
[9] etagify: https://github.com/lloyd/connect-etagify
[10] небольшую шпаргалку: https://gist.github.com/6a68/4971859
[11] например: http://habrahabr.ru/post/77004/
[12] awsbox: https://github.com/mozilla/awsbox
[13] t-критерий Стьюдента: http://ru.wikipedia.org/wiki/T-%D0%BA%D1%80%D0%B8%D1%82%D0%B5%D1%80%D0%B8%D0%B9_%D0%A1%D1%82%D1%8C%D1%8E%D0%B4%D0%B5%D0%BD%D1%82%D0%B0
[14] P-значение: http://ru.wikipedia.org/wiki/P-%D0%B7%D0%BD%D0%B0%D1%87%D0%B5%D0%BD%D0%B8%D0%B5
[15] Источник: http://habrahabr.ru/post/196818/
Нажмите здесь для печати.