Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
223 changes: 223 additions & 0 deletions index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<title>Компания Pravo.ru</title>
</head>
<body>
<header>
<h1>Блог компании Pravo.</h1>
</header>
<main>
<section>
<header>
<h2>КАК МЫ ИЗМЕРЯЕМ СКОРОСТЬ ЗАГРУЗКИ И УЛУЧШАЕМ ЕЁ</h2>
</header>
<article>
<p>Если ваш сайт медленно грузится, вы рискуете тем, что люди не оценят ни то, какой он красивый, ни то,
какой он удобный.
Никому не понравится, когда все тормозит. Мы регулярно добавляем в <a href="https://doc.one/ru/"
target="_blank">Doc.One</a>
новую функциональность, иногда — исправляем ошибки, а это значит, у нас постоянно появляются новый
код и новая логика.
Всё это напрямую влияет на скорость работы интерфейса.</p>
<h3>Что мы измеряем</h3>
<p>Этапы первой загрузки:</p>
<ul>
<li>подготовка;</li>
<li>загрузка статики (<abbr title="HyperText Transfer Protocol">HTTP</abbr>-запрос и парсинг);</li>
<li>исполнение модулей;</li>
<li>инициализация базовых объектов;</li>
<li>отрисовка.</li>
</ul>
<p>Этапы отрисовки любой страницы:</p>
<ul>
<li>подготовка к запросу на сервер;</li>
<li>запрос данных с сервера;</li>
<li>шаблонизация;</li>
<li>обновление <abbr title="Document Object Model">DOM</abbr>.</li>
</ul>
<blockquote>
— «Ок, теперь у нас есть метрики, мы можем отправить их на сервер» - говорим мы<br>
— «Что же дальше?» - вопрошаете вы<br>
— «А давай построим график!» - отвечаем мы<br>
— «А что будем считать?» - уточняете вы
</blockquote>
<p>Как вы знаете, <dfn>медиана</dfn> – это серединное, а не среднее значение в выборке.
Если у нас имеются числа <i>1, 2, 2, 3, 8, 10, 20,</i> то медиана – <i>3</i>, а среднее –
<i>6,5</i>.
В общем случае медиана отлично показывает, сколько грузится средний пользователь.</p>
<p>В случае ускорения или замедления медиана, конечно, изменится. Но она не может
рассказать, сколько пользователей ускорилось, а сколько замедлилось.</p>
<p><abbr title="Application Performance Index">APDEX</abbr> – метрика, которая сразу говорит: хорошо или
плохо. Метрика
работает очень просто. Мы выбираем временной интервал <i>[0; t]</i>, такой, что если
время показа страницы попало в него, то пользователь счастлив. Берем еще один
интервал, <i>(t; 4t]</i> (в четыре раза больше первого), и считаем, что если страница
показана за это время, то пользователь в целом удовлетворен скоростью работы,
но уже не настолько счастлив. И применяем формулу:</p>
<p><mark>(кол-во счастливых пользователей + кол-во удовлетворенных / 2) / (кол-во всех).</mark></p>
<p>Получается значение от нуля до единицы, которое, видимо, лучше всего показывает,
хорошо или плохо работает почта.</p>
<h3>Как мы измеряем</h3>
<p>Сейчас модуль обновления сам логирует все свои стадии, и можно легко понять
причину замедления: медленнее стал отвечать сервер либо слишком долго
выполняется JavaScript. Выглядит это примерно так:</p>
<p><code>
this.timings['look-ma-im-start'] = Date.now();<br>
this.timings['look-ma-finish'] = Date.now();
</code></p>
<p>C помощью <code>Date.now()</code> мы получаем текущее время. Все тайминги собираются и при
отправке рассчитываются. На этапах разница между “end” и “start” не считается,
а все вычисления производятся в конце:</p>
<p><code>
var <var>totalTime</var> = this.timings['look-ma-finish'] - this.timings['look-ma-im-start'];
</code></p>
<p>И на сервер прилетают подобные записи:</p>
<p><samp>
<var>serverResponse</var>=50&domUpdate=60
</samp></p>
<h3>Как мы ускоряем</h3>
<p>Чтобы снизить время загрузки почты при выходе новых версий, мы уже делаем следующее:</p>
<ul>
<li>включаем gzip;</li>
<li>выставляем заголовки кэширования;</li>
<li>фризим <abbr title="Cascading Style Sheets">CSS</abbr>, <abbr title="Java Script">JS</abbr>,
шаблоны и картинки;</li>
<li>используем <abbr title="Content Delivery Network">CDN</abbr>;</li>
</ul>
<p>Мы подумали: <q>А что если хранить где-то старую версию файлов, а при выходе новой передавать только
diff между ней и той,
которая сохранена у пользователя?</q></p>
<p>В браузере же останется просто наложить патч на клиенте.</p>
<p>На самое деле эта идея не нова. Уже существуют стандарты для <abbr
title="HyperText Transfer Protocol">HTTP</abbr> — например,
<a href="https://tools.ietf.org/html/rfc3229" target="_blank"><abbr
title="Request for Comments">RFC</abbr> 3229 «Delta encoding in HTTP»</a>
и <a href="https://lists.w3.org/Archives/Public/ietf-http-wg/2008JulSep/att-0441/Shared_Dictionary_Compression_over_HTTP.pdf"
target="_blank">«Google <abbr title="Shared Dictionary Compression over HTTP">SDHC</abbr>»</a>,
— но по разным причинам они
не получили должного распространения в браузерах и на серверах.</p>
<p>Мы же решили сделать свой аналог на <abbr title="Java Script">JS</abbr>. Чтобы реализовать этот метод
обновления, начали искать реализации diff на <abbr title="Java Script">JS</abbr>. На популярных хостингах кода нашли
библиотеки:</p>
<ul>
<li>VCDiff</li>
<li>google-diff-patch-match</li>
</ul>
<p>Для окончательного выбора библиотеки нам нужно сравнить:</p>
<pre>
Библиотека | <abbr title="Internet Explorer">IE</abbr> 9 | Opera 12
---------- | ---- | --------
vcdiff | 8 | 5
google diff | 1363 | 76
</pre>
<p>После того как мы определились с библиотекой для диффа, нужно определиться с тем,
где и как хранить статику на клиенте.</p>
<p>Формат файла с патчами для проекта выглядит так:</p>
<pre>
[
{
"k": "jane.css",
"p": [patch],
"s": 4554
},
{
"k": "jane.css",
"p": [patch],
"s": 4554
}
]
</pre>
<p>То есть это обычный массив из объектов. Каждый объект — отдельный ресурс. У
каждого объекта есть три свойства. <var>k</var> — названия ключа в localStorage для этого
ресурса. <var>p</var> — патч для ресурса, который сгенерировал vcdiff. <var>s</var> — чексумма для
ресурса актуальной версии, чтобы потом можно было проверить правильность
наложения патча на клиенте. <strong>Чексумма вычисляется по алгоритму Флетчера.</strong></p>
<dl>
<dt>Алгоритм Бройдена — Флетчера — Гольдфарба — Шанно (<abbr
title="Broyden — Fletcher — Goldfarb — Shanno algorithm">BFGS</abbr>)</dt>
<dd>— итерационный метод численной оптимизации, предназначенный для
нахождения локального максимума/минимума нелинейного функционала
без ограничений.</dd>
</dl>
<p>Почему именно алгоритм Флетчера, а не другие популярные алгоритмы вроде:</p>
<ul>
<li><b>CRC16/32</b> - алгоритм нахождения контрольной суммы, предназначенный для проверки
целостности данных</li>
<li><b>md5</b> - 128-битный алгоритм хеширования. Предназначен для создания «отпечатков» или
дайджестов сообщения произвольной длины и последующей проверки их подлинности.</li>
</ul>
<p>Потому что он быстрый, компактный и легок в реализации.</p>
<h3>Итог</h3>
<p>Фактически мы экономим 80-90% трафика. Размер загружаемой статитки в байтах:</p>
<table border="1" cellpadding=5 cellspacing=0>
<thead>
<tr>
<th>Релиз</th>
<th>С патчем</th>
<th>Без патча</th>
</tr>
</thead>
<tbody>
<tr>
<td>7.7.20</td>
<td>397</td>
<td>174 549</td>
</tr>
<tr>
<td>7.7.21</td>
<td>383</td>
<td>53 995</td>
</tr>
<tr>
<td>7.7.22</td>
<td>483</td>
<td>3 995</td>
</tr>
</tbody>
</table>
</article>
<footer>
Автор: @doochik
С++ разработчик
<address>Электронная почта: (<a href="mailto:doochik@pravo.ru">doochik@pravo.ru</a>)</address>
Компания: <a href="https://pravo.ru/">Pravo.ru</a>
</footer>
</section>
<section>
<h3>
<b>Комментарии (3):</b>
</h3>
<article>
- Mogaika (<a href="mailto:mogaika@pravo.ru">mogaika@pravo.ru</a>) <time datetime="2014-11-30T17:05">30
ноября 2014 в 17:05</time>
А можете привести сравнение, на сколько быстрее грузится lite версия?
</article>
<hr>
<article>
- JIguse (<a href="mailto:mrawesome@pravo.ru">mrawesome@pravo.ru</a>) <time
datetime="2014-11-29T21:30">29 ноября 2014 в 21:30</time>
Спасибо за статью, познавательно. Здорово, что Pravo.ru делится некоторыми подробностями о внутренней
работе сервисов.
</article>
<hr>
<article>
- Brister (<a href="mailto:brist89@pravo.ru">brist89@pravo.ru</a>) <time datetime="2014-11-24T13:13">24
ноября 2014 в 13:13</time>
<blockquote>(кол-во счастливых пользователей + кол-во удовлетворенных / 2) / (кол-во всех). Получается
значение от нуля до единицы, которое, видимо, лучше всего показывает, хорошо или плохо работает
почта.
</blockquote>
<p>наверное все-таки от 0.5 до 1</p>
</article>
<hr>
<article>
- alexeimois (<a href="mailto:test@pravo.ru">test@pravo.ru</a>) <time datetime="2014-11-22T17:35">22
ноября 2014 в 17:35</time>
<p></p>
</article>
</section>
</main>
</body>
</html>