Поделиться через


Антишаблон шквала повторных попыток

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

Описание проблемы

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

В следующем примере демонстрируется сценарий, в котором клиент подключается к API на основе сервера. Если запрос не выполняется, клиент немедленно повторяет попытку и продолжает постоянно это делать. Часто такое поведение менее выражено, чем в этом примере, но принцип остается тем же.

public async Task<string> GetDataFromServer()
{
    while(true)
    {
        var result = await httpClient.GetAsync(string.Format("http://{0}:8080/api/...", hostName));
        if (result.IsSuccessStatusCode) break;
    }

    // ... Process result.
}

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

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

  • Ограничьте число повторных попыток и не выполняйте их в течение длительного периода времени. Хотя цикл while(true) кажется самым очевидным выбором, не следует выполнять повторные попытки в течение длительного периода времени, так как ситуация, из-за которой был инициирован запрос, уже могла измениться. Для большинства приложений достаточно выполнять повторную попытку в течение всего нескольких секунд или минут.
  • Делайте паузы между повторными попытками. Если служба стала недоступна, немедленная повторная попытка подключения вряд ли будет успешной. Постепенно увеличивайте время ожидания между попытками, например с применением стратегии экспоненциальной задержки.
  • Корректно обрабатывайте ошибки. Если служба не отвечает, возможно, имеет смысл отменить попытку и возвратить ошибку пользователю или вызывающему объекту компонента. Учитывайте такие сценарии сбоя при проектировании приложения.
  • Вы также можете использовать шаблон автоматического выключения, который создан специально для недопущения шквала повторных попыток.
  • Если сервер предоставляет заголовок ответа retry-after, ни в коем случае не пытайтесь выполнить повторную попытку, пока не истечет указанное время.
  • Используйте официальные пакеты SDK при обмене данными со службами Azure. Часто такие пакеты SDK включают встроенные политики повторных попыток и защиту от возникновения или поддержания шквалов повторных попыток. Если вы обмениваетесь данными со службой без пакета SDK или ее пакет SDK некорректно обрабатывает логику повторных попыток, вы можете воспользоваться библиотекой Polly (для .NET) или retry (для JavaScript), чтобы обеспечить надлежащую обработку логики повторных попыток без самостоятельного написания кода.
  • Если ваша среда обеспечивает соответствующую поддержку, воспользуйтесь сеткой службы (или другим уровнем абстракции) для отправки исходящих вызовов. Обычно эти средства, например Dapr, поддерживают политики повтора и автоматически следуют рекомендациям, таким как резервное копирование после повторных попыток. Такой подход избавит вас от необходимости писать код для повторных попыток самостоятельно.
  • При возможности выполняйте пакетную обработку запросов и используйте пулы запросов. Многие пакеты SDK выполняют пакетную обработку запросов и создание пулов подключений от вашего имени, что сокращает общее число попыток исходящих подключений от приложения. Но при этом вам все равно нужно соблюдать осторожность, чтобы не выполнять такие попытки слишком часто.

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

  • Добавьте уровень шлюза, чтобы иметь возможность прервать подключения во время инцидента. Это пример шаблона отсеков. Azure предоставляет множество разных служб шлюза для разных типов решений, в том числе Front Door, Шлюз приложений и Управление API.
  • Обеспечьте регулирование запросов на своем шлюзе, чтобы не допустить ситуаций, при которых ваши серверные компоненты не смогут работать из-за большого числа запросов.
  • Если вы используете регулирование, отправляйте заголовок retry-after, чтобы клиенты знали, когда можно повторить попытку подключения.

Рекомендации

  • Клиенты должны учитывать тип возвращаемой ошибки. Некоторые типы ошибок указывают не на сбой службы, а на то, что клиент отправил недопустимый запрос. Например, если клиентское приложение получает ответ об ошибке 400 Bad Request, отправка такого же запроса не решит проблему, так как сервер сообщил вам о его недопустимости.
  • Клиенты должны учитывать, что попытки повторного подключения имеет смысл выполнять только в течение ограниченного времени. Такое время определяется бизнес-требованиями и тем, можете ли вы относительно легко передать сведения об ошибке пользователю или вызывающему объекту. Для большинства приложений достаточно выполнять повторную попытку в течение всего нескольких секунд или минут.

Как определить проблему

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

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

Пример диагностики

В следующих разделах описывается подход к обнаружению потенциального шквала повторных попыток на стороне клиента и на стороне службы.

Обнаружение с помощью телеметрии клиента

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

Следующая диаграмма была получена с вкладки "Метрики" на портале Application Insights и отображает метрику Сбои зависимостей, разделенную по имени удаленной зависимости. Она иллюстрирует сценарий, в котором за короткое время было зарегистрировано большое число (более 21 000) неудачных попыток подключения к зависимости.

Снимок экрана: Application Insights с отображением 21 000 неудачных попыток подключения к одной зависимости в течение 30 минут

Обнаружение с помощью телеметрии сервера

Серверные приложения могут выявлять большое число подключения от одного клиента. В приведенном ниже примере служба Azure Front Door выступает в качестве шлюза для приложения и настроена для регистрации всех запросов к рабочей области Log Analytics.

Приведенный ниже запрос Kusto можно выполнить к Log Analytics. Он определяет клиентские IP-адреса, с которых за последний день было отправлено большое число запросов к приложению.

AzureDiagnostics
| where ResourceType == "FRONTDOORS" and Category == "FrontdoorAccessLog"
| where TimeGenerated > ago(1d)
| summarize count() by bin(TimeGenerated, 1h), clientIp_s
| order by count_ desc

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

Снимок экрана: Log Analytics с отображением 81 608 входящих подключений к службе Front Door с одного IP-адреса в течение часа