Шаблон ограничения скорости

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

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

Контекст и проблема

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

Например, рассмотрим следующий неудачный процесс повторных попыток при возникновении ошибок при загрузке данных в Azure Cosmos DB:

  1. Приложение должно принять 10 000 записей в Azure Cosmos DB. Каждая запись требует 10 единиц запроса (RU) на загрузку, поэтому для выполнения задачи в общей сложности требуется 100 000 RU.

  2. Для экземпляра Azure Cosmos DB выделено 20 000 единиц запросов (RU) пропускной способности.

  3. Вы отправляете все 10 000 записей в Azure Cosmos DB. 2 000 записей успешно записываются, а 8 000 записей отклоняются.

  4. Вы отправляете оставшиеся 8000 записей в Azure Cosmos DB. 2000 записей успешно написаны, и 6000 записей отклоняются.

  5. Вы отправляете оставшиеся 6000 записей в Azure Cosmos DB. 2000 записей успешно написаны, и 4000 записей отклоняются.

  6. Вы отправляете оставшиеся 4000 записей в Azure Cosmos DB. 2000 записей успешно написаны, и 2000 записей отклоняются.

  7. Вы отправляете оставшиеся 2000 записей в Azure Cosmos DB. Все записи записываются успешно.

Задание приема успешно завершается, но только после отправки 30 000 записей в Azure Cosmos DB. Весь набор данных состоит только из 10 000 записей.

Существуют другие факторы, которые следует учитывать в этом примере:

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

  • Так как вы не знаете ограничения регулирования службы приема, у вас нет способа задать ожидания на время обработки данных. Ограничение скорости позволяет вычислить время, необходимое для приема.

Решение

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

Служба может ограничивать частоту запросов по различным метрикам в течение определённого периода времени, например:

  • Количество операций (например, 20 запросов в секунду).
  • Объем данных (например, 2 ГиБ в минуту).
  • Относительная стоимость операций (например, 20 000 RUs в секунду).

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

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

Чтобы избежать этого риска, рассмотрите возможность отправки своих записей в отказоустойчивую систему обмена сообщениями, которая может обрабатывать полную скорость приема данных. (Такие службы, как Центры событий Azure могут обрабатывать миллионы операций в секунду.) Затем можно использовать один или несколько процессоров заданий для чтения записей из системы обмена сообщениями с контролируемой скоростью, которая находится в пределах ограничений управляемой службы. Отправка записей в систему обмена сообщениями позволяет экономить внутреннюю память, поскольку дает возможность извлекать из очереди только те записи, которые могут быть обработаны за данный интервал времени.

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

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

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

Хотя это не обязательно, часто рекомендуется отправлять меньшее количество записей чаще для повышения пропускной способности. Таким образом, вместо того чтобы пытаться выпускать записи пакетами раз в секунду или раз в минуту, можно делать это более дробно, чтобы потребление ресурсов — памяти, процессора и сети — было более равномерным. Этот подход предотвращает потенциальные узкие места, вызванные внезапными всплесками запросов. Например, если служба разрешает 100 операций в секунду, реализация ограничения скорости может достигать равномерного распределения запросов, выпуская по 20 операций каждые 200 миллисекунд, как показано на следующем графике.

Диаграмма, показывающая ограниченный скоростью поток с течением времени.

Диаграмма называется «Поток с ограничением скорости». Он отображает количество операций на оси y от 0 до 25 с меткой времени на оси x от 0,0 до 4,0. От метки времени 0.0 до 0.2 число операций равно 0, указывающее, что операции не отправляются. В диапазоне от 0,2 до 0,4 линия резко поднимается до 20, что представляет ограничение скорости, достигающее настроенного потолка. На отметке 3.0 линия графика резко падает обратно до 0, что указывает на то, что работа завершена или что ограничитель скорости перестал выдавать записи. От 3.2 до 3.8 значение остается равным 0. Плоское плато между 0,4 и 3.0 является ключевым признаком диаграммы. Это показывает, что ограничение частоты запросов обеспечивает стабильную и предсказуемую пропускную способность, а не всплески и падения, связанные с проблемным подходом с повторными попытками при ошибках.

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

Например, если система с ограничением пропускной способности допускает до 500 запросов в секунду, можно создать 20 разделов по 25 запросов в секунду каждый. Если процесс требуется для выдачи 100 запросов, он может попросить распределенную систему взаимного исключения для четырех разделов. Система может выделить два раздела на 10 секунд. Затем процесс будет ограничивать скорость до 50 запросов в секунду, завершить задачу в два секунды, а затем освободить блокировку.

Одним из способов реализации этого шаблона является использование служба хранилища Azure. В этом сценарии вы создаете один BLOB-объект размером 0 байт для каждого логического раздела в контейнере. Затем ваши приложения могут получать эксклюзивные блокировки непосредственно для этих BLOB-объектов на короткий период времени (например, на 15 секунд). Для каждой аренды, по которой одобрена заявка, можно использовать объём ёмкости этого раздела. Затем приложение должно отслеживать срок аренды, чтобы, когда этот срок истечёт, оно могло прекратить использование предоставленной ему мощности. При реализации этого шаблона часто требуется, чтобы каждый процесс пытался арендовать случайную секцию, когда она нуждается в емкости.

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

Схема, на которой показано, как несколько процессов конкурируют за монопольную аренду разделов больших двоичных объектов в Хранилище BLOB-объектов Azure.

На схеме показаны четыре процесса, конкурирующие за исключительную аренду шести пронумерованных разделов BLOB-объектов, хранящихся в Хранилище BLOB-объектов. Четыре процесса помечены как A, B, C и D. Хранилище BLOB-объектов содержит шесть секций BLOB-объектов, нумеруемых от 0 до 5. От процесса A к разделу 0 указывает зелёная стрелка, обозначающая успешное получение аренды. Красная стрелка указывает от процесса A на раздел 1, обозначая неудачную попытку аренды, поскольку другой процесс уже удерживает этот раздел. От процесса D к разделу 1 указывает одна зеленая стрелка, что означает, что процесс D имеет аренду на этот раздел. Вот почему попытка процесса A на разделе 1 завершилась неудачей. Вторая зеленая стрелка ведет от процесса D к разделу 4, указывая на вторую аренду, которой владеет процесс D. С процессами B и C не связано ни одной стрелки, поскольку они не владеют арендой. Разделы 2, 3 и 5 также не сданы в аренду.

В качестве альтернативы служба хранилища Azure вы также можете реализовать эту систему управления арендой с помощью таких технологий, как ZooKeeper, etcd и Redis/Redsync.

Проблемы и рекомендации

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

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

  • Убедитесь, что повторные попытки согласованы с ограничением скорости. Бездумные или чрезмерно агрессивные повторные попытки могут увеличить нагрузку и вызвать штормы повторных запросов, поэтому передавайте сигналы обратного давления (например, HTTP 429 с Retry-After) и используйте ограниченное число повторных попыток с небольшими случайными задержками между попытками.

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

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

Когда следует использовать этот шаблон

Используйте этот шаблон, когда:

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

  • Вы хотите свести сетевой трафик к минимуму по сравнению с наивными стратегиями повторных попыток при ошибке.

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

Этот шаблон может быть не подходит, если:

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

  • Основное узкое место не является скоростью запросов, но вместо этого является параллелизмом или конфликтом ресурсов (например, насыщенность ЦП или длительные работы в полете). В этих случаях более подходящими являются элементы управления масштабированием или параллелизмом.

Проектирование рабочей нагрузки

Оцените, как использовать шаблон ограничения скорости в проектировании рабочей нагрузки для решения целей и принципов, описанных в основных принципах Azure Well-Architected Framework. В следующей таблице приведены рекомендации по использованию этого шаблона для целей каждого компонента.

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

- RE:07 Самосохранение

Если этот шаблон вводит компромиссы внутри столпа, рассмотрите их против целей других столпов.

Example

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

  1. Проверка
  2. Обогащение
  3. Вставка записи в базу данных

Все компоненты приложения (API, обработчик заданий А и обработчик заданий B) — это отдельные процессы, которые можно масштабировать независимо. Процессы не взаимодействуют друг с другом напрямую.

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

На схеме показаны два пользователя, отправляющие записи через общий API. Каждый тип записи направляется в отдельную очередь, обрабатывается выделенным обработчиком заданий и записывается в базу данных с ограниченной пропускной способностью с контролируемой скоростью, которая регулируется арендой разделов BLOB-объектов в служба хранилища Azure. Каждый пользователь отправляет пакет сообщений компоненту API. Один пользователь отправляет 10 000 сообщений, а другой отправляет 5000 сообщений. API направляет 10 000 сообщений в очередь A и 5000 сообщений в очередь B. Очередь А подключается к обработчику заданий A, а очередь B подключается к обработчику заданий B. Обработчик заданий А записывает в базу данных скоростью 300 записей в секунду. Обработчик заданий B записывает в базу данных 500 записей в секунду. Оба обработчика заданий подключаются к компоненту базы данных с пометкой «ограничение до 800 записей в секунду». Ниже двух обработчиков заданий в служба хранилища Azure находятся восемь разделов BLOB-объектов с метками от 0 до 7. В примечании указано, что каждый раздел соответствует 100 записям в секунду. Несколько стрелок соединяют обработчики заданий с разделами BLOB. Зеленые стрелки указывают от обработчика заданий A на разделы 0, 4 и 6, что означает, что у обработчика есть аренда на эти три раздела. Именно эти договоры аренды обеспечивают его скорость обработки в 300 записей в секунду. Зелёные стрелки указывают от обработчика заданий B на разделы 1, 2, 3, 5 и 7. Эти стрелки указывают на то, что этот процессор имеет аренду на пять секций. Эти механизмы аренды обеспечивают скорость 500 записей в секунду. Неудачные попытки получить аренду отображаются красными стрелками, которые пересекают разделы, уже занятые другим процессором. Эти стрелки обозначают разрешение коллизий. На схеме показано, что сумма всех удерживаемых секций в обоих процессорах равна 800 записей в секунду, что соответствует ограничению регулирования базы данных.

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

Эта схема включает следующий рабочий процесс:

  1. Пользователь отправляет 10 000 записей типа A в API.
  2. API ставит эти 10 000 записей в очередь A.
  3. Пользователь отправляет 5000 записей типа B в API.
  4. API ставит эти 5 000 записей в очередь B.
  5. Обработчик заданий A видит, что очередь A содержит записи, и пытается получить исключительную аренду BLOB-объекта 2.
  6. Обработчик заданий B видит, что очередь B содержит записи и пытается получить монопольную аренду на BLOB-объект 2.
  7. Обработчик заданий A не может получить аренду.
  8. Обработчик заданий B получает аренду для BLOB-объекта 2 на 15 секунд. Теперь он может ограничить скорость запросов к базе данных со скоростью 100 в секунду.
  9. Обработчик заданий B извлекает 100 записей из очереди B и записывает их.
  10. Проходит одна секунда.
  11. Обработчик заданий A видит, что в очереди A больше записей, и пытается получить исключительную аренду на BLOB-объект 6.
  12. Обработчик заданий B видит, что очередь B имеет больше записей и пытается получить монопольную аренду на BLOB-объект 3.
  13. Обработчик заданий A получает аренду для BLOB-объекта 6 на 15 секунд. Теперь он может ограничить скорость запросов к базе данных со скоростью 100 в секунду.
  14. Обработчик заданий B получает аренду для BLOB-объекта 3 сроком на 15 секунд. Теперь он может ограничить скорость запросов к базе данных со скоростью 200 в секунду. (У неё также есть право аренды на blob 2.)
  15. Обработчик заданий A извлекает 100 записей из очереди A и записывает их.
  16. Обработчик заданий B извлекает 200 записей из очереди B и записывает их.
  17. Проходит одна секунда.
  18. Обработчик заданий A видит, что очередь A содержит больше записей, и пытается получить исключительную аренду на BLOB-объект 0.
  19. Обработчик заданий B видит, что в очереди B больше записей, и пытается получить эксклюзивную аренду на объект blob 1.
  20. Обработчик заданий A получает право аренды для BLOB-объекта 0 на 15 секунд. Теперь он может ограничить скорость запросов к базе данных со скоростью 200 в секунду. (Она также имеет договор аренды на blob 6.)
  21. Обработчик заданий B получает право аренды на объект BLOB 1 на 15 секунд. Теперь он может ограничить скорость запросов к базе данных со скоростью 300 в секунду. (Она также владеет правами аренды на блоки 2 и 3.)
  22. Обработчик заданий A извлекает 200 записей из очереди A и записывает их.
  23. Обработчик заданий B извлекает 300 записей из очереди B и записывает их.
  24. И так далее.

Через 15 секунд одно или оба задания по-прежнему не будут завершены. По мере истечения сроков аренды обработчик также должен уменьшить количество запросов, которые он извлекает из очереди и записывает.

Реализации этого шаблона доступны на разных языках программирования:

Дальнейшие действия

При реализации этого шаблона также может быть актуально следующее руководство.

При реализации этого шаблона также могут быть важны следующие шаблоны и рекомендации.

  • Throttling. Шаблон ограничения частоты запросов обычно применяется в ответ на ограничение запросов со стороны сервиса.

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

  • Выравнивание нагрузки на основе очереди аналогично паттерну ограничения скорости, но отличается в нескольких ключевых аспектах:

    • Ограничение скорости не обязательно требует использования очередей для управления нагрузкой, но требуется использовать надежную службу обмена сообщениями. Например, шаблон ограничения скорости может использовать такие службы, как Apache Kafka или Центры событий.

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

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