Обходимо обмеження браузера на кількість з’єднань
Утилітарний вибір
Під час створення будь-яких додатків всім розробникам доводиться робити те, що називається «утилітарним» вибором (utilitarian choice). Якщо дещо химерно перефразувати Jeremy Bentham, то «утилітарним» можна назвати той підхід, «в результаті якого ми отримуємо найбільшу кількість добра для найбільшої кількості [людей]». Багато разів продуктивністю жертвували для невеликої кількості користувачів, щоб, в результаті, середня продуктивність для всіх користувачів у сукупності була б вищою.
Браузери були створені в ту епоху, коли безліч користувачів користувалися комутованим доступом з невисокою пропускною здатністю каналу, тому тоді було важливо обмежити користувачів невеликою кількістю одночасних з'єднань. Накладні витрати на перемикання між безліччю з'єднань при комутованому доступі створювали великі складності для обробки та завантаження кожного окремого запиту. До того ж у ті часи веб- і проксі-сервера були недостатньо потужними, щоб підтримувати безліч з'єднань, тому таке жорстке обмеження кількості одночасних з'єднань у браузера істотно знижувало ризик падіння мережевої інфраструктури в цілому.
Щоб знайти відповідний баланс, IE та Firefox, за замовчуванням, обмежують користувачів всього шістьма одночасними з'єднаннями або двома з'єднаннями на хост для протоколу HTTP 1.1. HTTP 1.0 трохи відрізняється в цьому плані, але це вже зовсім інша історія, тому що всі вигоди від постійних з'єднань доступні тільки якщо ви будете використовувати HTTP 1.1 (або вже робите це).
Часи змінюються
Природно, у світі всі ці утилітарні рішення мають особливість старіти, коли минає той час, коли вони були актуальні. Сьогодні у більшостікористувачів широкосмугового доступу в Інтернет, тому найбільш вузьким місцем є вже не клієнтська сторона (природно, клієнтська сторона була, є і буде найбільш вузьким місцем у продуктивності ваших веб-додатків, просто потрібно розуміти, як саме її можна оптимізувати в кожному конкретному випадку). Зазвичай затримки при отриманні окремих об'єктів значно більше, ніж час на встановлення нового з'єднання та надсилання запиту. Збільшуючи кількість одночасних з'єднань, ми можемо розпаралелити це місце і набагато швидше пробитися через безліч об'єктів, які знаходяться в списку очікуваних до завантаження, що призведе, в результаті, до збільшення швидкості завантаження, що відчувається, у користувача «до швидкості блискавки» — якщо скористатися гіперболою з кліпу metacafe.
На жаль, якщо сподіватися, що користувачі самі змінюватимуть налаштування свого браузера, це буде не найкращою стратегією оптимізації. Так що ж робити розробнику, щоб досягти того ж таки ефекту зі свого боку?
«Ріжемо» з'єднання
Як практичний приклад можна розглянути цей веб-додаток, який Buddy і я розробили для нашої майстерні на Web Builder 2.0. Невеликі зображення альбомів, за замовчуванням, завантажуються з батьківського хоста, тому вони також змушені використовувати ті ж два доступні з'єднання. Нижче наведено приблизну діаграму завантаження об'єктів на сторінці, отриману за допомогою Gomez' — зовнішнього сервісу для перевірки продуктивності.
1. Завантаження при двох з'єднаннях. На посилання доступна діаграма в повний розмір.
На цьому графіку добре видно, що для musicstore.ajaxperformance.com відкрито лише 2 з'єднання (хочу помітити, що дана діаграма є модельною і справедлива тільки для IE, у всіх іншихбраузерах, за замовчуванням, відкриваються великі з'єднання): C0 і C2. Ми використовуємо протокол HTTP 1.1, тому нам не потрібно відкривати окреме з'єднання для кожної картинки, але ми, як і раніше, втрачаємо багато часу на обслуговування індивідуальних запитів до об'єктів. Час на встановлення з'єднання (час до отримання першого байта, синя смужка на діаграмі) явно домінує над часом завантаження даних, яке не таке велике (червона смужка на діаграмі).
Краще, більше, швидше
Щоб покращити продуктивність ми створили записи CNAME в DNS-таблиці для images1.ajaxperformance.com , images2.ajaxperformance.com і images3.ajaxperformance.com , кожна з яких вказує назад на основний хост. Цей невеликий шматочок коду на Rails у шаблоні, що відповідає за відображення альбому, розподіляє картинки по різних хостах:
Варто звернути увагу, що при зміні порядку картинок хости, з яких вони повинні завантажуватись, також змінюватися, тому користувачеві доведеться завантажувати ті ж картинки ще раз (якщо у нього працювало кешування). При проектуванні додатків, що масштабуються, які будуть розподіляти об'єкти по різних хостах, потрібно використовувати хеш-функцію, яка встановить однозначну відповідність між назвою зображення і хостом, з якого воно має завантажуватися.
Результати можна спостерігати на оновленій версії. При першому завантаженні продуктивність буде значно кращою. Як можна бачити з наведеного нижче графіка (і знову, картинка на весь розмір доступна за посиланням), зараз ми використовуємо вже 6 з'єднань для завантаження наших картинок.
Рисунок 2. Завантаження при шести з'єднаннях.
Висновок
А що в цьому хорошого, запитаєте ви? Досить багато, якщо брати до уваги відносно простізміни у вихідному коді. Нижче наведено порівняння загального часу завантаження сторінки, яке вимірювалося протягом 24 годин з 8 точок доступу по всій країні.
Рисунок 3. Денний графік часу завантаження
Середній час завантаження при використанні 2 з'єднань становив 7,919 секунди. Середній час при використанні 6 з'єднань лише 4,629 секунд. Час завантаження сторінки зменшивсябільше ніж на 40%. І ця техніка буде працювати у всіх випадках, коли у вас є великий пул запитів до об'єктів, які розташовані на одному сервері.