11
мая
2008

Обход лимита одновременных соединений в браузерах

Статья написана еще в декабре 2006 года, но, не смотря на это, показалась мне интересной и актуальной. Тем не менее, некоторые цифры в тексте могут отличаться от актуальных для настоящего времени значений.

Автор: Ryan Breen, ajaxperformance.com
Оригинал: «Circumventing browser connection limits for fun and profit»
Перевод: Александр Мусаев, paradigm.ru

Недавно на Digg появился видеоролик с Metacafe, где демонстрировался способ увеличения скорости открытия страниц в браузе. Рассмотрим подробнее то, как происходит управление сетевыми соединениями, чтобы понять причину такого эффекта.

В процессе принятия решений, разработчики программного обеспечения зачастую вынуждены руководствоваться «утилитарными» принципами. Перефразируя Джереми Бентама, таковым является подход, при котором наилучший результат достигается для наибольшего количества конечных пользователей (в оригинале, согласно классической формулировке Бентама, моральным является то, что «приносит наибольшее счастье наибольшему количеству людей,» — прим. переводчика). Потеря производетельности, которая отразится на работе определенной группы пользователей, может быть обоснована выйгрышем для более широкой группы.

Все распространенные сейчас браузеры изначально разрабатывались еще в то время, когда подавляющее большинство пользователей Сети работало через низкоскоростные dial-up подключения. Это было основной причиной необходимости ограничивать количество одновременных соединений. Постоянное чередование большого количества сетевых соединений может приводить к существенной потере скорости пересылки данных для каждого из них. Кроме того, веб-серверы и прокси в тот период так же не отличались высокой производительностью. Поэтому лимитирование пула соединений в браузере было дополнительно обосновано снижением нагрузки на сетевую инфраструктуру.

IE и Firefox по-умолчанию допускают 6 параллельных соединений по протоколу HTTP 1.1. При этом на каждых хост может приходиться не более 2 одновременных соединений. С HTTP 1.0 немного другая история, но мы не будем рассматривать его отдельно, т.к. преимущества постоянных соединений в сделали версию 1.1 значительно более предпочтительной.

Конечно, в настоящее время утилитарный способ принятия решений давно себя изжил. Большинство пользователей имеют доступ в сеть по широкополосным каналам, и пропускная способность с клиентской стороны уже перестала быть узким местом. Чаще всего основной причиной задержек при загрузки отдельных объектов становится процесс установки соединения и пересылка запросов. Увеличение количества одновременных соединений может сократить общее время обработки списка загружаемых объектов.

К сожалению, полагаться на то, что пользователи сами станут оптимизировать настройки браузеров, бессмысленно. Но для повышения производительности веб-разработчики могут прибегать к альтернативным решениям.

Большинство сайтов использует один единственный хост, что ограничивает количество соединений до двух. Распределение контента между несколькими хостами может стать эффективной стратегий для распараллеливания загрузки. Добиться этого довольно несложно, т.к. браузеры обращают внимание только на доменное имя, а не на IP. Поэтому для images1.yoursite.com и images2.yoursite.com будет выделено уже не 2, а 4 отдельных соединения.

Для примера рассмотрим веб-приложение, в котором генерируются страницы с большим количеством графики. По-умолчанию картинки загружаются с одного и того же хоста, и количество одновременных соединений ограничено двумя. Ниже приведена «каскадная» схема, иллюстрирующая последовательность загрузки файлов.

На графике видно, что для хоста musicstore.ajaxperformance.com открыто только два сетевых соединения — C0 и С2. Мы используем HTTP 1.1, поэтому необходимость каждый раз открывать новые соединения отсутствует. Тем не менее, большая часть времени уходит на обработку запросов, а не передачу данных (синяя часть полоски начинается от первого переданного байта запроса, а красная показывает передаваемый контент).

Для улучшения производительности, мы добавили три CNAME записи в DNS для доменных имен images1.ajaxperformance.com, images2.ajaxperformance.com, и images3.ajaxperformance.com (CNAME — canonical name record, т.е. каноническая запись имени или, проще говоря, псевдоним, — прим. переводчика). Каждый из них указывает на один и тот же основной хост. Приведенный ниже фрагмент кода на Ruby последовательно распределяет картинки по разным доменам:

img_url = "/images/album_art/#{album.id}.jpg"
if @perf_multiplex_images
  idx = (album.id % 3) + 1
  img_url = "http://images#{idx}.ajaxperformance.com/images/album_art/#{album.id}.jpg"
end

Результат можно увидеть в обновленной версии приложения. Скорость первой загрузки должна существенно улучшиться, что подтверждает обновленный график загрузки:

Чего мы добились в итоге? Существенного прироста производительности, по средствам относительно небольших изменений в программе. Далее приведены сравнительные графики скорости загрузки старой и новой версии страницы в течении 24 часов, из разных географических точек:

Среднее время загрузки при использовании двух соединений — 7.919 с. А при использовании шести — 4.629 с. Разница составляет более 40%. Техника будет работать везде, где необходима одновременная загрузка большого количества объектов с одного и того же сервера.

Описанный метод широко применяется в Ajax-приложениях. Например, в Google Maps для распараллеливания загружаемых фрагментов карты используются четыре домена — mt0.google.com .. mt3.google.com. Тот же подход задействован в Virtual Earth.

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

Комментарии к заметке «Обход лимита одновременных соединений в браузерах»

# Gluek: (11 мая, 2008 @ 14:39)

Вот только как быть с кэшированием таких объектов? По идее надо всегда на один отдельный объект «выделять» свой CNAME, чтобы браузер его закэшировал и больше не запрашивал. Можно привязать к имени (например картинки, имя которых начинается с «a» — загружать с img1, с «b» — с img2) или использовать схему посложнее — с подсчетом хэшей (по имени файла или url).

# admin: (11 мая, 2008 @ 15:47)

Gluek: Согласен, динамический выбор CNAME от балды – может быть неэффективен. Но если привязываться только к именам картинок или хэшу может получиться так, что объекты распределятся по хостам неравномерно и какой-то из них все равно будет перегружен. Оптимальный вариант стоит подбирать относительно конкретных условий (когда и как генерируются ссылки на объекты). Можно распределять по первой букве, можно по дате добавления (unixtime % N) или, например, с помощью глобального циклического счетчика, который будет инкрементироваться при каждой новой обработанной ссылке (i++ % N).

# JavaScript фреймворки теперь можно хостить на Google: (28 мая, 2008 @ 16:04)

[...] загрузки страниц увеличивается, благодаря переносу части запросов на другой хост и поддержке [...]

# Bloggy » Blog Archive » JavaScript фреймворки теперь можно хостить на Google: (28 мая, 2008 @ 16:20)

[...] загрузки страниц увеличивается, благодаря переносу части запросов на другой хост и поддержке [...]

# Михаил Наумов: (6 июня, 2008 @ 16:26)

Интересная статья, спасибо, буду знать.

# sunnybear: (19 октября, 2008 @ 10:03)

Прикольно, еревод этой же статьи появился и на webo.in, однако, на 8 дней позже :)
http://webo.in/articles/habrahabr/39-out-of-connections-limits/

# sunnybear: (19 октября, 2008 @ 10:12)

Прикольно, перевод этой же статьи появился и на webo.in, однако, на 8 дней позже :)
http://webo.in/articles/habrahabr/39-out-of-connections-limits/

Написать комментарий

Можно использовать следующие HTML теги: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> .