Наши партнеры

UnixForum





Библиотека сайта rus-linux.net

Высокопроизводительный сетевой стек Chrome

Глава 1 из книги "Производительность приложений с открытым исходным кодом".

Оригинал: High Performance Networking in Chrome
Автор: Ilya Grigorik
Перевод: А.Панин

Жизнь вашей сессии в рамках браузера

Обладая знаниями, полученными в ходе рассмотрения архитектуры сетевого стека Chrome с расстояния в 10000 футов, мы можем подробнее рассмотреть типы заметных пользователю оптимизаций, осуществленных в браузере. А сейчас давайте представим, что мы только что создали новый профиль Chrome и готовы начать наш день.

Оптимизация поведения браузера после холодной загрузки

При первой загрузке ваше браузер не обладает большим объемом информации о ваших любимых сайтах или методах просмотра страниц. Однако, многие из нас после холодной загрузки браузера выполняют одни и те же процедуры, переходя к просмотру входящих сообщений электронной почты с помощью веб-интерфейса, на любимый новостной сайт, сайт социальной сети, внутресетевой портал, и.т.д. Сами сайты могут отличаться, но сходство самих этих сессий позволяет предиктору Chrome повысить производительность вашего браузера после холодного старта.

Chrome запоминает десять имен узлов, которые пользователь посетит с наибольшей вероятностью после запуска браузера - следует помнить о том, что эти узлы не являются десятью наиболее популярными узлами, а являются десятью специфичными узлами, на которые осуществляется переход после запуска браузера. По мере загрузки Chrome может начать с помощью сервера DNS предварительное разрешение имен узлов, на которые вероятнее всего будет осуществлен переход. Если вас заинтересовала эта возможность, вы можете изучить свой список имен узлов для разрешения при запуске, открыв новую вкладку и перейдя по адресу chrome://dns. В верхней части страницы вы обнаружите список из десяти узлов, являющихся наиболее вероятными кандидатами для перехода после запуска браузера с вашим профилем.

Имена узлов, разрешаемые при запуске с помощью сервера DNS
Рисунок 1.5 - Имена узлов, разрешаемые при запуске с помощью сервера DNS

Снимок экрана, представленный на Рисунке 1.5, является примером для моего профиля Chrome. Как я обычно начинаю свою работу с браузером? Чаще всего я перехожу на страницу Google Docs при работе над такой статьей, как эта. Поэтому большое количество наблюдаемых нами имен узлов компании Google в данном списке не является удивительным.

Оптимизация взаимодействий со строкой ввода Omnibox

Одна из инноваций Chrome заключалась в использовании строки ввода Omnibox, которая в отличие от своих предшественниц имеет гораздо большие возможности в плане обработки исходных данных и позволяет не ограничиваться использованием исключительно строк URL для узлов назначения. Помимо сохранения строк URL для страниц, которые пользователь посетил в прошлом, она также предоставляет возможность полнотекстового поиска в вашей истории посещений страниц и обеспечивает тесную интеграцию с выбранной вами поисковой системой.

По мере ввода данных пользователем, строка ввода Omnibox автоматически предлагает действие, которое заключается либо в использовании строки URL, извлеченной из вашей истории посещения страниц, либо в осуществлении поискового запроса. Под капотом этой системы в зависимости от запроса, а также от вероятности использования в прошлом происходит установка приоритета для каждого из предлагаемых действий. Фактически, Chrome позволяет нам ознакомиться с этими данными, перейдя по адресу chrome://predictors.

Предварительное формирование строки URL средствами строки ввода Omnibox
Рисунок 1.6 - Предварительное формирование строки URL средствами строки ввода Omnibox

Chrome ведет историю введенных пользователем префиксов, действий, которые были предложены, а также частоты использования каждого из них. При рассмотрении моего профиля вы можете заметить, что в момент, когда я ввожу "g" в строку запроса Omnibox, существует 76-процентная вероятность того, что я хочу перейти в веб-интерфейс Gmail. Как только я добавляю "m" (получается строка "gm"), вероятность возрастает до 98% - фактически, она рассчитывается на основе информации о 412 записанных визитах, причем я не перешел в веб-интерфейс Gmail только единожды.

Но как это относится к сетевому стеку? Желтые и зеленые цвета для вероятных кандидатов также являются важными сигналами для объекта ResourceDispatcher. В том случае, если используется вероятный кандидат (отмеченный желтым), Chrome может приступить к предварительному разрешению имени целевого узла с использованием сервера DNS. В том же случае, когда используется кандидат с высокой вероятностью (отмеченный зеленым), Chrome может дополнительно приступить к предварительному установлению соединения по протоколу TCP после того, как имя узла будет разрешено. Наконец, если после завершения двух описанных этапов пользователь все еще сомневается, Chrome может даже приступить к предварительному выводу всей страницы в скрытой вкладке.

В другом случае, когда нет удачных совпадений с введенным префиксом при использовании истории прошлых посещений страниц, Chrome может приступить к разрешению имени узла с использованием сервера DNS и предварительному установлению соединения по протоколу TCP с вашей поисковой системой, ожидая вероятного поискового запроса.

У среднестатистического пользователя уходят сотни миллисекунд на введение запроса и выбор нужного варианта из автоматически формируемого списка предложений. В фоновом режиме Chrome имеет возможность предварительного получения ресурса, установления предварительного подключения и в некоторых случаях даже предварительного вывода страницы, поэтому к тому моменту, пока пользователь будет готов нажать клавишу "enter", многие связанные с сетевой активностью задержки будут преодолены.

Оптимизация производительности кэша

Лучшим и самым быстрым запросом является тот запрос, который не был осуществлен. При любом разговоре о производительности игнорирование вопроса кэширования является ошибкой - вы же предоставляете заголовки Expires, ETag, Last-Modified и Cache-Control в ответах на запросы всех ресурсов с ваших веб-страниц? Если это не так, начните исправлять ситуацию. Мы подождем.

В Chrome имеются две различные реализации внутреннего кэша: одна реализация использует дисковое хранилище, а вторая позволяет хранить все данные в памяти. Реализация с хранением данных в памяти используется для работы в режиме просмотра страниц "инкогнито" и поддерживает возможность полного удаления данных в момент закрытия окна. Обе реализации предоставляют один и тот же внутренний интерфейс (disk_cahe::Backend и disk_cache::Entry), который в значительной степени упрощает архитектуру и, если вам это интересно, позволяет без лишних сложностей проводить эксперименты с вашими разрабатываемыми реализациями систем кэширования.

Внутренняя структура системы дискового кэша предусматривает использование набора собственных структур данных, все из которых хранятся в одной директории данных кэша для вашего профиля. Внутри этой директории находятся файлы индексирования, которые отображаются в память в момент запуска браузера, а также файлы данных, в которых хранятся сами данные вместе с заголовками HTTP и другой необходимой информацией.4 Наконец, для управления временем использования ресурсов дисковый кэш содержит кэш наиболее редко используемых ресурсов (Last Recently Used cache - LRU), в котором сохраняются такие данные рейтинга использования ресурсов, как частота доступа и время существования.

Если вас интересует текущее состояние кэша Chrome, вы можете открыть новую вкладку и перейти по адресу chrome://net-internals/#httpCache. В другом случае, если вы хотите увидеть актуальные метаданные HTTP и кэшированный ответ, вы также можете перейти по адресу chrome://cache на страницу, где будут перечислены все ресурсы, доступные на данный момент в кэше. На этой странице вы можете найти необходимый ресурс и нажать на строку URL для того, чтобы увидеть необработанные кэшированные заголовки и байтовое представление ответа.

Оптимизация взаимодействия с серверами DNS с помощью механизма предварительного разрешения имен узлов

Мы уже несколько раз упоминали о механизме предварительного разрешения имен узлов, поэтому перед рассмотрением его реализации, давайте рассмотрим случаи, в которых он может быть активирован, а также причины его активации:
  • Система разбора документа Blink, работающая в рамках процесса вывода страницы, может предоставить список имен узлов для всех ссылок на текущей странице, в отношении которых Chrome может принять решение о необходимости заблаговременного разрешения.
  • Процесс вывода страницы может сообщить о перемещении указателя мыши над ссылкой или "неполном нажатии кнопки", как о событии, являющимся ранним сигналом о намерении пользователя перейти на новую страницу.
  • Строка ввода Omnibox может отправить запрос разрешения имени узла, основываясь на предположения о высокой вероятности перехода по адресу.
  • Предиктор может отправить запрос разрешения имени узла, основываясь на данных о переходах на страницу в прошлом и данных запроса ресурса.
  • Владелец страницы может явно указать Chrome на узлы, предварительное разрешение имен которых должно быть выполнено.

Во всех случаях предварительное разрешение имен узлов с использованием сервера DNS рассматривается как подсказка. Chrome не гарантирует, что предварительное разрешение имен узлов будет выполнено, а вместо этого использует каждый сигнал в комбинации со своим предиктором для оценки этой подсказки и принятия решения о векторе последующих действий. В "худшем случае", когда Chrome не удается осуществить предварительное разрешение имени узла вовремя, пользователю придется ожидать завершения видимого процесса разрешения имени узла с использованием сервера DNS с последующим установлением соединения по протоколу TCP и финальным этапом самого получения ресурса. Однако, в такой ситуации предиктор может сохранить необходимую информацию и принимать последующие решения в соответствии с ней - таким образом браузер будет выполнять действия быстрее и разумнее по мере использования вами.

Одной из оптимизаций, о которой мы ранее не упоминали, является возможность Chrome изучать топологию каждого из сайтов и впоследствии использовать полученную информацию для ускорения повторных их открытий. В частности, вспомним о том, что среднестатистическая страница состоит из 88 ресурсов, которые доставляются с более чем 15 удаленных узлов. В каждый момент перехода на страницу Chrome может записывать имена узлов для популярных ресурсов на странице и, в ходе последующего визита, он может осуществить предварительное разрешение этих имен с использованием сервера DNS и даже предварительное соединение по протоколу TCP для некоторых из них.

Для ознакомления с сохраненными Chrome именами узлов для подресурсов перейдите по адресу chrome://dns и выполните поиск любого популярного имени удаленного узла для вашего профиля. В представленном выше примере вы можете увидеть шесть имен узлов для подресурсов, которые были сохранены Chrome при посещении Google+ наряду со статистикой о количестве случаев, когда было осуществлено предварительно разрешение имен узлов с использованием сервера DNS или предварительное соединение по протоколу TCP, а также с данными об ожидаемом количестве запросов, которые могут быть осуществлены для каждого из них. Эти внутренние механизмы учета предоставляют предиктору Chrome возможность выполнять свои оптимизации.

В дополнение ко всем внутренним сигналам владелец сайта также имеет возможность внедрять дополнительный код разметки в свои страницы для сообщения браузеру о том, что он должен осуществить предварительное разрешение имени узла с использованием сервера DNS:
 <link rel="dns-prefetch" href="//host_name_to_prefetch.com">

Почему просто не положиться на автоматизированные механизмы браузера? В некоторых случаях вам может понадобиться предварительно разрешить имя узла, который не обозначен где-либо на странице. Перенаправление является классическим примером: ссылка может вести на похожую на узел службу анализа и сбора статистики, которая впоследствии перенаправит пользователя на действительный целевой ресурс. Сам по себе Chrome не может предугадать данное поведение, но вы можете помочь ему, самостоятельно предоставив подсказку и попросив браузер заблаговременно разрешить имя действительного целевого узла.

Как же все это технически реализовано? Ответ на этот вопрос, как и описание всех других рассмотренных нами оптимизаций, зависит от версии Chrome, так как команда разработчиков постоянно экспериментирует с новыми и более совершенными способами повышения производительности. Однако, если говорить обобщенно, инфраструктура для работы с серверами DNS в рамках Chrome выла представлена двумя основными реализациями. Исторически Chrome полагался на платформо-независимый системный вызов getaddrinfo() и делегировал ответственность за разрешение имен узлов операционной системе. Тем не менее, этот подход в процессе развития Chrome был заменен на использование собственной реализации асинхронной системы для разрешения имен узлов с использованием сервера DNS.

Начальная реализация, которая строилась на основе механизмов операционной системы, имела свои преимущества: меньший объем и простота кода, а также возможность использования системного кэша DNS. Однако, getaddrinfo() также является блокирующим системным вызовом, что подразумевает необходимость Chrome в создании и обслуживании отдельного рабочего пула потоков, который позволит осуществлять множество разрешений имен серверов в параллельном режиме. Этот несвязанный пул имел ограничение в шесть рабочих потоков, эмпирически вычисленное на основе наименьшего общего знаменателя параметров аппаратного обеспечения - оказалось, что большее количество параллельных запросов может перегрузить маршрутизаторы некоторых пользователей.

Для осуществления предварительного разрешения имен узлов с помощью пула рабочих потоков Chrome просто осуществляет вызов getaddrinfo(), который блокирует рабочий поток до момента готовности ответа, после чего возвращаемый результат просто отклоняется и начинается выполнение следующего запроса предварительного разрешения имени узла. Результат добавляется в кэш DNS операционной системы, которая будет немедленно возвращать ответ на этот же запрос в будущем при использовании вызова getaddrinfo() для действительного разрешения имен узлов. Это решение является простым, эффективным и на практике работает достаточно надежно.

Это неплохое, эффективное, но не достаточно удачное решение. Вызов getaddrinfo() скрывает от Chrome большой объем такой полезной информации, как метки времени жизни (TTL) для каждой записи, а также состояние самого кэша DNS. Для улучшения производительности команда разработчиков Chrome приняла решение о реализации собственной, кроссплатформенной асинхронной системы разрешения имен узлов с использованием серверов DNS.

Включение асинхронной системы разрешения имен узлов с использованием серверов DNS
Рисунок 1.7 - Включение асинхронной системы разрешения имен узлов с использованием серверов DNS

Делегирование задач по разрешению имен узлов с использованием серверов DNS новой системе асинхронного разрешения имен позволяет провести множество новых оптимизаций:
  • появляется возможность лучшего контроля за таймерами повторной передачи и параллельного исполнения множества запросов
  • появляется возможность доступа к информации о времени жизни записей (TTL), что позволяет Chrome заблаговременно обновлять записи для популярных ресурсов
  • улучшается поведение реализаций с поддержкой двух стеков IP (IPv4 и IPv6)
  • появляется возможность использования механизмов обработки ошибок множества серверов с использованием параметра времени обращения пакетов (RTT) и других сигналов в качестве входных данных

Описанные выше, а также дополнительные возможности явились идеями для непрерывных экспериментов и улучшений Chrome. Они привели нас к постановке очевидного вопроса: как мы можем узнать и измерить результат реализации этих идей? Все очень просто, ведь Chrome ведет наблюдение за сетевой активностью и собирает подробную статистику о производительности сетевого стека, а также строит гистограммы на основе собранных данных для каждого отдельного профиля. Для ознакомления с собранными данными взаимодействия с серверами DNS следует открыть новую вкладку и перейти по адресу chrome://histograms/DNS (обратитесь к Риснуку 1.8).

Гистограммы предварительного получения данных от серверов DNS
Рисунок 1.8 - Гистограммы предварительного получения данных от серверов DNS

Приведенная выше гистограмма отражает распределение задержек для предварительных запросов разрешения имен с использованием серверов DNS: примерно 50% (по данным из крайнего правого столбца) запросов предварительного разрешения имен узлов завершилось в течение 20 мс (по данным из крайнего левого столбца). Следует учесть, что эти данные собраны во время активности последней сессии просмотра страниц (9869 измерений) и специфичны для пользователя. В том случае, если пользователь принял решение о предоставлении статистики использования Chrome разработчикам, эти данные в обобщенной форме будут периодически анонимно передаваться команде разработчиков, у участников которой впоследствии появится возможность оценить результаты своих экспериментов и предпринять соответствующие действия.


Продолжение статьи: Оптимизация управления соединениями по протоколу TCP с помощью механизма предварительного соединения.