2 апреля 2013 в 7:04

Об облаках Amazon и транспорте MPLS

В ходе разработки одного проекта на базе облачных услуг Amazon пришлось столкнуться с одной проблемой, описания которой в открытом доступе найти не удалось — значительные задержки при обращении к сервису Amazon RDS. Однако помочь разобраться с ней мне помогли знания в технологиях передачи данных, которые я получил в ходе работы в одном отраслевом НИИ.

Итак, сначала диагноз больного. Разрабатывается ИТ-система с клиентским приложением. Система представляет собой сервер, который размещается в облаке Amazon в виде сервиса EC2. Этот сервер взаимодействует с базой MySQL, которая стоит в том же регионе (US-west) на сервисе RDS (Relational Database Service). С сервером взаимодействует мобильное приложение, которое через сервер регистрируется, и загружает некоторые данные. В ходе работы этого приложения часто наблюдаются ошибки связи, на которые приложение выводит popup со словами Connection Error. При этом пользователи жалуются на медленную работу приложения. Такая ситуация возникает у пользователей и в США и в России.

Перенос всей системы на независимый хостинг (самый обычный, дешевый) привел к заметно более шустрой работе приложения, и отсутствию задержек Amazon RDS. Поиск по разным форумам ответа о причинах такого поведения не дал.

Для сбора временных характеристик процесса взаимодействия приложения с сервером и базой данных был написан скрипт, который делал запрос в базу данных MySQL 10 раз подряд. Чтобы исключить ситуацию кеширования запроса, в запрос была добавлена инструкция  SQL_NO_CACHE. При этом база данных не кеширует такой запрос SELECT. Запрос и измерение его продолжительности осуществляется следующей функцией, которая меряет продолжительность транзакции с учетом сетевой задержки.

function query_execute ($link)
{
     $time = microtime (true);
     $res = mysqli_query ($link, «SELECT SQL_NO_CACHE ui.* FROM `user_item_id` ui INNER JOIN `users` u ON u.`user_id`=ui.`user_id` WHERE u.`username`='any@user.com';»);
     return array («rows» => mysqli_num_rows ($res), «time» => sprintf («%4f», microtime (true) — $time));
}

Этот скрипт был загружен на российский хостинг, и запущен. Результат получился такой (скрипт — Россия, СУБД — Amazon RDS, Калифорния).

  • Start time to aws measure
    rows: 10, duration: 2.659313
    rows: 10, duration: 0.594934
    rows: 10, duration: 0.595982
    rows: 10, duration: 0.594558
    rows: 10, duration: 0.397052
    rows: 10, duration: 0.399988
    rows: 10, duration: 0.399615
    rows: 10, duration: 0.396856
    rows: 10, duration: 0.399138
    rows: 10, duration: 0.396113
    -----------
    Average duration: 0.6833549

Для сравнения — тоже самое, но с другой хостинговой площадки — во Франции (скрипт — Франция, СУБД — Amazon RDS, Калифорния).

  • Start time to aws measure
    rows: 14, duration: 1.980444
    rows: 14, duration: 0.472865
    rows: 14, duration: 0.318233
    rows: 14, duration: 0.417172
    rows: 14, duration: 0.342588
    rows: 14, duration: 0.303614
    rows: 14, duration: 0.908241
    rows: 14, duration: 1.809397
    rows: 14, duration: 0.497458
    rows: 14, duration: 0.316923
    -----------
    Average duration: 0.7366935

Такая ситуация чрезвычайно удивила, и заставила покопаться в справочной информации о кешировании SQL-запросов. Но все изыскания говорили одно — скрипт написан корректно. Ситуация сложилась интересная. Вроде все понятно, но непонятна самая малость: а) что происходит, и б) как хотя бы сформулировать запрос в саппорт?

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

  • Start time to aws measure
    rows: 10, duration: 0.024818
    rows: 10, duration: 0.009796
    rows: 10, duration: 0.006747
    rows: 10, duration: 0.005163
    rows: 10, duration: 0.007998
    rows: 10, duration: 0.006088
    rows: 10, duration: 0.009614
    rows: 10, duration: 0.007938
    rows: 10, duration: 0.008052
    rows: 10, duration: 0.007804
    -----------
    Average duration: 0.0094018

Задержки сильно снизились, но в целом картина прежняя — первый запрос выполняется в разы дольше последующих. И думаю, что именно это причина всех проблем с конечным приложением. Ведь приложение обменивается с сервером информацией «в один запрос», который как раз приходиться на эту самую долгую операцию. Соответственно, именно эта операция  и определяет все «тормознутость» приложения.

Чтобы проверить, что же все-таки мешает, база данных была мигрирована с Amazon RDS на отдельный хостинг (самый дешевый). И здесь ждал небольшой сюрприз — равную скорость выполнения первой и последующей транзакций (скрипт — Россия, СУБД — тот же хостинг в США).

  • Start time to linode measure
    rows: 10, duration: 0.018506
    rows: 10, duration: 0.017285
    rows: 10, duration: 0.011917
    rows: 10, duration: 0.011928
    rows: 10, duration: 0.027923
    rows: 10, duration: 0.011141
    rows: 10, duration: 0.072708
    rows: 10, duration: 0.011934
    rows: 10, duration: 0.007816
    rows: 10, duration: 0.008045
    -----------
    Average duration: 0.0199203

Немного отвлекусь — самая лучшая ситуация сложилась, когда на этот слабый, в общем-то хостинг, поставили еще и сам сервер (скрипт и СУБД — один сервер на территории США).

  • Start time to linode measure
    rows: 10, duration: 0.008159
    rows: 10, duration: 0.000344
    rows: 10, duration: 0.000317
    rows: 10, duration: 0.000309
    rows: 10, duration: 0.000269
    rows: 10, duration: 0.000282
    rows: 10, duration: 0.000260
    rows: 10, duration: 0.000263
    rows: 10, duration: 0.000303
    rows: 10, duration: 0.000297
    -----------
    Average duration: 0.0010803

Но вернемся к нашей ситуации. Для чистоты эксперимента сделали еще один тест — запустили скрипт с хостинговой площадки на Amazon RDS. Результат — опять долгая первая транзакция (скрипт — хостинг США, СУБД — Amazon RDS, Калифорния).

  • Start time to aws measure
    rows: 10, duration: 0.098134
    rows: 10, duration: 0.016168
    rows: 10, duration: 0.011697
    rows: 10, duration: 0.007868
    rows: 10, duration: 0.008148
    rows: 10, duration: 0.010468
    rows: 10, duration: 0.033403
    rows: 10, duration: 0.011947
    rows: 10, duration: 0.012217
    rows: 10, duration: 0.008185
    -----------
    Average duration: 0.0218235

Каких-то аналогов таким странностям ни мне, ни разработчикам найти не удалось. Однако тем временем, глядя на эти цифры, у меня стало закладываться смутное подозрение, что ситуация вызвана не настройками ИТ-систем, а особенностью передачи данных в транспортной сети IP, которая связывает дата-центры Amazon.

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

Итак, в телекоммуникационном мире в основе всех сетей связи лежит транспортный уровень, образованный протоколом IP. Для передачи пакетов IP через множество промежуточных маршрутизаторов используются, в общем случае, два метода, маршрутизация и коммутация.

Что такое маршрутизауия? Допустим, есть четыре сетеых узла. С первого на второй передан пакет IP. Как определить, куда его направить — на узел 3 или 4? Узел 2 смотрит у себя внутри таблицу маршрутизации, ищет адрес назначения (порт Ethernet), и передает пакет — на рисунке, на узел 4 (с адресом 48.136.23.03).

Принцип маршрутизации пакетов IP в сетях IP

Принцип маршрутизации пакетов IP в сетях IP

Минусы этого способа — низкая скорость маршрутизации пакетов по следующим причинам: а) нужно разбирать миллионы пакетов IP и извлекать из них адрес, б) нужно каждый пакет прогонять по базе данных (таблице маршрутизации), где стоит соответствие IP-адреса номеру сетевого интерфейса Ethernet.

В больших сетях, класса операторов Tier 1 это требует просто неимоверных ресурсов процессора и все равно каждый маршрутизатор вносит задержку.

Чтобы это избежать, была придумана технология MPLS (Multiprotocol Label Switching). В простонаречии — коммуатия пакетов. Как она работает? Допустим, началась передача первого пакета. На узле 1, поверх IP-адреса, к пакету добавили метку (label). Метка — это 4-х байтное целое число, которое легко и быстро обрабатывается процессорами. Такой пакет попал на узел 2. Узел 2 по таблице маршрутизации определяет описанным выше способом, что его надо отправить на узел 3. Одновременно на узле 2 создается таблица, в которой прописывается соответствие метки интерфейсу Eternet, смотрящему в сторону узла 4.

Принцип коммутации пакетов IP в сетях IP

Принцип коммутации пакетов IP в сетях IP

Далее, узел 1 отправляет второй пакет IP, и добавляет к нему ту же метку, что была установлена для первого пакета IP. Когда такой пакет поступит на узел 2, то узел уже не будет обрабатывать IP-адрес и искать его в таблице маршрутизации. Он сразу извлечет метку, и проверит — есть ли для нее выходной интерфейс.

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

А теперь главное...

Минус — первые пакеты передаются методом маршрутизации, выстраивая за собой «туннель» из меток, и только следующие пакеты уже передаются быстро. Именно эту картину мы и наблюдаем выше. Вряд ли у дешевого хостера стоит оборудование MPLS. Как результат — мы видим одинаковую продолжительность выполнения первого и последующего запросов. А вот в Amazon, похоже, как раз и проявляется работа протокола MPLS — посылка первого запроса занимает в 3-5 раз больше времени, чем последующих.

Думаю, именно в этом лежит причина неустойчивого и долгого взаимодействия приложения с базой данных. Хотя, если бы обмен с базой данных был более интенсивный, то скорее всего, через 3-4 запроса, Amazon обогнал бы дешевый хостинг. Парадоксально, что технология, призванная значительно ускорить передачу данных, в моем конкретном случае приводит к неустойчивой работе системы в целом.

Если принять эту версию за рабочую, возникает вопрос — а как же другие? Ведь работают же на Amazon крупные клиенты, социальные сети, и таких проблем не испытывают. Справедливый вопрос, точного ответа на который у меня нет. Однако есть некоторые сображения.

  1. Приложение, о котором я пишу, сейчас является версией 1.0. Скорее всего, а даже наверняка, у нас сейчас имеется неоптимальная структура запросов к базе данных. В будущем она будет оптимизирована, и за счет этого получим выигрыш в скорости. Но сейчас, похоже, одно наложилось на другое.
  2. Предполагать, что крупные соцсети базируются на Amazon, все-таки странно. Скорее всего, архитектура таких систем географически распределенная, и информация может асинхронно только стекаться в дата-центры Amazon. По крайней мере, трассировка из Москвы Facebook показывает конечной точкой ирландский сервер, Твиттер — какую-то свою площадку, Pinterest — на сети Telia и так далее.
  3. Между крупными узлами социальных сетей, скорее всего, предустановлены статические маршруты.

В любом случае, другого объяснения сложившейся ситуации у меня нет.

А решение — есть. Три недели назад система была мигрирована на канадские выделенные серверы хостинг-провайдера OVH.  На этом проблема решилась: все заработало заметно быстрее и Connection Error больше никто не видел.

 

Поделиться заметкой
Опубликовать в Google Plus
Опубликовать в LiveJournal
Опубликовать в Мой Мир
Опубликовать в Одноклассники

Комментариев: 5

  • Андрей
    2 апреля 2013 в 18:41

    Парадоксально, что технология, призванная значительно ускорить передачу данных, в моем конкретном случае приводит к неустойчивой работе системы в целом.

    Почему парадоксально, все очень логично. У всего есть минимум две стороны, положительная и отрицательная.

    Ответить

    Stan_1

    Только не все с этой точкой зрения согласны. :) habrahabr.ru/post/175023/

    Ответить

  • Андрей
    3 апреля 2013 в 11:15

    Хм, проблема такая, что однозначного ответа без знания конфигурации Amazon хостинга дать не удастся. Могу только сказать:

    1. Построение SQL серверов в среде виртуализации очень тонкий процесс. И очень большой риск нарваться на тормоза при использовании хостингового решения в тяжелых задачах.

    2. При использовании VmWare (я не знаю на чем сидят Amazon) я видел что-то подобное на простом пинге. То есть, пускаешь пинг, а он тормозит на первом пакете, а потом начинает нормально работать.

    3. И, на мой взгляд, правильно сделали, что перешли на использование железного сервера. Его можно настроить под себя и знать структуру, все его узкие места.

    Ответить

    Stan_1

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

    Ответить

    Андрей

    Так он и работает, только для типичного случая. Не типичные случаи, о которых ты пишешь (facebook, twitter etc.) и в который попадаете вы, они делают по отдельному договору, как кастомное решение. В этом я почему-то уверен.

    Ответить

Ваш комментарий:

Поля, помеченные символом * обязательны для заполнения.

CAPTCHA image