Тестирование Amazon Sqs

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

  1. В документации написано, что Amazon старается сохранять порядок сообщений, насколько хорошо он сохраняется?
  2. Как быстро будет получено сообщение при использовании длинного опроса?
  3. Насколько пакетная обработка ускоряет процесс?


Постановка задачи

Наиболее поддерживаемая библиотека для AWS в erlang — erlcloud [1].

Чтобы инициализировать библиотеку, просто вызовите методы start и configure, как указано на github. Мои сообщения будут содержать набор случайных символов, сгенерированных следующей функцией:

  
  
  
  
  
  
  
  
  
  
  
  
   

random_string(0) -> []; random_string(Length) -> [random_char() | random_string(Length-1)].

random_char() -> random:uniform(95) + 31 .



Для измерения скорости мы будем использовать известную функцию, использующую timer:tc, но с некоторыми изменениями:

test_avg(M, F, A, R, N) when N > 0 -> {Ret, L} = test_loop(M, F, A, R, N, []), Length = length(L), Min = lists:min(L), Max = lists:max(L), Med = lists:nth(round((Length / 2)), lists:sort(L)), Avg = round(lists:foldl(fun(X, Sum) -> X + Sum end, 0, L) / Length), io:format("Range: ~b - ~b mics~n" "Median: ~b mics~n" "Average: ~b mics~n", [Min, Max, Med, Avg]), Ret. test_loop(_M, _F, _A, R, 0, List) -> {R, List}; test_loop(M, F, A, R, N, List) -> {T, Result} = timer:tc(M, F, [R|A]), test_loop(M, F, A, Result, N - 1, [T|List]).



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

сообщение.

Таким образом, функция отправки сообщения с номером будет выглядеть так:

send_random(N, Queue) -> erlcloud_sqs:send_message(Queue, [N + 1 | random_string(6000 + random:uniform(6000))]), N + 1 .



И ее звонок со сбором статистики:

test_avg(ЭMODULE, send_random, [QueueName], 31, 20)

здесь 31 - номер первого сообщения, это число выбрано не случайно, дело в том, что эрланг не очень хорошо различает последовательности чисел и строки и в сообщении это будет символ номер 31, можно передавать и меньшие числа до SQS, но в этом случае получаются небольшие непрерывные диапазоны (#x9 | #xA | #xD | [#x20 до #xD7FF] | [#xE000 до #xFFFD] | [#x10000 до #x10FFFF], подробнее [2 ]), и если вы выйдете за пределы диапазона, вы получите исключение.

Таким образом, функция send_random формирует и отправляет сообщение в очередь с именем Queue, в начале которой стоит число, определяющее ее номер, функция возвращает номер следующего числа, которое затем используется следующим поколением функция.

Функция test_avg принимает QueueName, которое становится вторым аргументом функции send_random, первый аргумент — это количество и количество повторений.

Функция, которая будет получать сообщения и проверять их порядок, будет выглядеть так:

checkorder(N, []) -> N; checkorder(N, [H | T]) -> [{body, [M | _]}|_] = H, K = if M > N -> M; true -> io:format("Wrong ~b less than ~b~n", [M, N]), N end, checkorder(K, T).

receive_checkorder(LastN, Queue) -> [{messages, List} | _] = erlcloud_sqs:receive_message(Queue), remove_list(Queue, List), checkorder(LastN, List).



Удаление сообщений:

remove_msg(_, []) -> wrong; remove_msg(Q, [{receipt_handle, Handle} | _]) -> erlcloud_sqs:delete_message(Q, Handle); remove_msg(Q, [_ | T]) -> remove_msg(Q, T).

remove_list(_, []) -> ok; remove_list(Q, [H | T]) -> remove_msg(Q, H), remove_list(Q, T).



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

Перетасовка сообщений

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

К сожалению, хороших критериев найти не удалось и было решено отображать максимальное и среднее расхождение с правильным положением.

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

Чтобы вычислить такую разницу, достаточно изменить всего одну функцию проверки порядка сообщений:

checkorder(N, []) -> N; checkorder({N, Cnt, Sum, Max}, [H | T]) -> [{body, [M | _]}|_] = H, {N1, Cnt1, Sum1, Max1} = if M < N -> {N, Cnt + 1, Sum + N - M, if Max < N - M -> N - M; true -> Max end }; true -> {M, Cnt, Sum, Max} end, checkorder({N1, Cnt1, Sum1, Max1}, T).



Вызов функции run series будет выглядеть так:

{_, Cnt, Sum, Max} = test_avg(ЭMODULE, receive_checkorder, [QueueName], {0, 0, 0, 0}, Size)

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

Самое интересное для меня здесь максимальное смещение, остальные характеристики можно назвать спорными и они могут быть не очень хорошо рассчитаны (например, если один элемент считывается раньше, то будут учитываться все элементы, которые должны идти до него в данном случае перестановка).

К результатам:

Размер (шт.) 20 50 100 150 200 250 300 400 500 600 700 800 900 1000
Максимальное смещение (шт.) 11 32 66 93 65 139 184 155 251 241 218 249 359 227
Среднее смещение (шт.) 5.3 10.5 23.9 43 25.6 45.9 48.4 65.6 74.2 74.2 78.3 72.3 110.8 82.8
Первая строка — количество сообщений в очереди, вторая — максимальное смещение, третья — среднее смещение.

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

То же самое в виде графика:

Тестирование Amazon SQS



Длинный опрос

Как я уже писал, Amazon SQS не поддерживает подписки, для этого можно использовать Amazon SNS, но если требуются быстрые очереди с несколькими процессорами, это не подходит, чтобы не тянуть метод получения сообщений, Amazon реализовал Long Опрос, который позволяет зависать, ожидая сообщения до двадцати секунд, а так как SQS тарифицируется по количеству вызванных методов, то это должно значительно снизить затраты на очередь, но вот в чем проблема: для небольшого количества сообщений( согласно официальной документации), очередь может ничего не вернуть.

Такое поведение критично для очередей, в которых нужно быстро реагировать на событие и, вообще говоря, если это происходит часто, то Long Polling не имеет особого смысла, поскольку становится эквивалентным периодическому опросу с временем ответа SQS. Для проверки создадим два процесса, один из которых будет отправлять сообщения в случайное время, а второй будет постоянно находиться в Long Polling, при этом моменты отправки и получения сообщений будут сохраняться для последующего сравнения.

Чтобы включить этот режим, в параметрах очереди установите Время ожидания приема сообщения = 20 секунд.

send_sleep(L, Queue) -> timer:sleep(random:uniform(10000)), Call = erlang:now(), erlcloud_sqs:send_message(Queue, random_string(6000 + random:uniform(6000))), [Call | L].



эта функция переходит в режим сна на случайное количество миллисекунд, после чего запоминает момент и отправляет сообщение

remember_moment(L, []) -> L; remember_moment(L, [_ | _]) -> [erlang:now() | L].

receive_polling(L, Queue) -> [{messages, List} | _] = erlcloud_sqs:receive_message(Queue), remove_list(Queue, List), remember_moment(L, List).



эти две функции позволяют получать сообщения и запоминать моменты, в которые это произошло.

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

При этом не учитывается, что сообщения могут быть смешанными; в общем, это просто еще больше увеличит время реакции.

Давайте посмотрим, что произошло:

Интервал сна 10000 7500 5000 2500
Минимальное время (сек) 0.27 0.28 0.27 0.66
Максимальное время (сек) 10.25 7.8 5.36 5.53
Среднее время (сек) 1.87 1.87 1.84 1.88
первая строка — это значение, установленное как максимальная задержка процесса отправки.

То есть: 10 секунд, 7,5 секунд. Остальные строки — минимальное, максимальное и среднее время ожидания получения сообщения.

То же самое в виде графика:

Тестирование Amazon SQS

Среднее время во всех случаях было одинаковым; можно сказать, что между отправкой таких одиночных сообщений и их получением в среднем проходит две секунды.

Довольно долго.

В этом тесте выборка была достаточно маленькой, 20 сообщений, поэтому минимальные-максимальные значения — это скорее вопрос удачи, чем какая-то зависимость.



Пакетная отправка

Для начала проверим, насколько важен эффект «прогрева» очереди при отправке сообщений:
Количество записей 20 50 100 150 200 250 300 400 500 600 700 800 900 1000
Минимальное время (сек) 0.1 0.1 0.1 0.09 0.09 0.09 0.09 0.1 0.09 0.1 0.1 0.09 0.09 0.09
Максимальное время (сек) 0.19 0.37 0.41 0.41 0.37 0.38 0.37 0.43 0.39 0.66 0.74 0.48 0.53 0.77
Среднее время (сек) 0.12 0.12 0.12 0.12 0.12 0.12 0.12 0.12 0.12 0.12 0.12 0.12 0.12 0.12
То же в виде графика:

Тестирование Amazon SQS

можно сказать, что никакого прогрева не наблюдается, то есть очередь ведет себя примерно одинаково для этих объемов данных, только максимум почему-то увеличивается, а среднее и минимальное остаются на своих местах.

То же самое для чтения с удалением

Количество записей 20 50 100 150 200 250 300 400 500 600 700 800 900 1000
Минимальное время (сек) 0.001 0.14 0 0.135 0 0.135 0 0 0 0 0 0 0 0
Максимальное время (сек) 0.72 0.47 0.65 0.65 0.69 0.51 0.75 0.75 0.76 0.73 0.82 0.79 0.74 0.91
Среднее время (сек) 0.23 0.21 0.21 0.21 0.21 0.21 0.21 0.21 0.21 0.2 0.2 0.2 0.2 0.21


Тестирование Amazon SQS

Насыщения здесь тоже нет, среднее значение в районе 200мс.

Иногда чтение было мгновенным (быстрее 1мс), но это означает, что сообщение не было получено, по документации SQS-серверы это умеют, нужно просто запросить сообщение еще раз.

Перейдем непосредственно к блочному и многопоточному тестированию.

К сожалению, библиотека erlcloud не содержит функций для пакетной отправки сообщений, но такие функции несложно реализовать на основе существующих; в функции отправки сообщения нужно изменить запрос на следующий:

Doc = sqs_xml_request(Config, QueueName, "SendMessageBatch", encode_message_list(Messages, 1)),

и добавьте функцию генерации запроса:

encode_message_list([], _) -> []; encode_message_list([H | T], N) -> MesssageId = string:concat("SendMessageBatchRequestEntry.", integer_to_list(N)), [{string:concat(MesssageId, ".

Id"), integer_to_list(N)}, {string:concat(MesssageId, ".

MessageBody"), H} | encode_message_list(T, N + 1)].



в библиотеке вам также следует исправить версию API, например, на 2011-10-01, иначе Amazon в ответ на ваши запросы вернет Bad request. Функции тестирования аналогичны тем, которые используются в других тестах:

gen_messages(0) -> []; gen_messages(N) -> [random_string(5000 + random:uniform(1000)) | gen_messages(N - 1)].

send_batch(N, Queue) -> erlang:display(erlcloud_sqs:send_message_batch(Queue, gen_messages(10))), N + 1 .



Здесь нам просто нужно было изменить длину сообщений, чтобы весь пакет умещался в 64кб, иначе будет сгенерировано исключение.

Были получены следующие данные записи:

Количество потоков 0 1 2 4 5 10 20 50 100
Максимальная задержка (сек) 0.452 0.761 0.858 1.464 1.698 3.14 5.272 11.793 20.215
Средняя задержка (сек) 0.118 0.48 0.436 0.652 0.784 1.524 3.178 9.1 19.889
Время отправки сообщения (сек) 0.118 0.048 0.022 0.017 0.016 0.016 0.017 0.019 0.02
здесь 0 означает чтение по одному в 1 потоке, затем 1 чтение по 10 в 1 потоке, 10 в 2 потоках, 10 в 4 потоках и так далее Для чтения:
Количество потоков 0 1 2 4 5 10 20 50 100
Максимальная задержка (сек) 0.762 2.998 2.511 2.4 2.606 2.751 4.944 11.653 18.517
Средняя задержка (сек) 0.205 1.256 1.528 1.566 1.532 1.87 3.377 7.823 17.786
Время отправки сообщения (сек) 0.205 0.126 0.077 0.04 0.031 0.02 0.019 0.017 0.019
график, показывающий пропускную способность чтения и записи (сообщений в секунду):

Тестирование Amazon SQS

Синий цвет пишет, красный читает. Из этих данных можно сделать вывод, что максимальная пропускная способность достигается при записи около 10 потоков, а при чтении — около 50; при дальнейшем увеличении количества потоков количество сообщений, отправляемых в единицу времени, не увеличивается.



Выводы

Получается, что Amazon SQS существенно меняет порядок сообщений, имеет не очень хорошее время ответа и пропускную способность, и противостоять этому можно только надежностью и небольшой (в случае небольшого количества сообщений) комиссией.

То есть, если вам не критична скорость, не важно, что сообщения путаются и вы не хотите администрировать или нанимать администратора сервера очередей — это ваш выбор.



Ссылки

  1. Erlcloud на github github.com/gleber/erlcloud
  2. www.w3.org/TR/REC-xml/#charsets
Теги: #aws #erlang #очереди сообщений #Erlang/OTP #Amazon Web Services
Вместе с данным постом часто просматривают:

Автор Статьи


Зарегистрирован: 2019-12-10 15:07:06
Баллов опыта: 0
Всего постов на сайте: 0
Всего комментарий на сайте: 0
Dima Manisha

Dima Manisha

Эксперт Wmlog. Профессиональный веб-мастер, SEO-специалист, дизайнер, маркетолог и интернет-предприниматель.