Date: 2020-05-31
За несколько недель до 14 февраля системе Dodo IS немного поплохело под нагрузкой. Одной из причин стало то, что в backend’ах мобильного приложения и сайта не совсем корректно работали политики поверх HttpClient’а (Retry, Circuit Breaker, Timeout). В этой статье я хочу поделиться с вами потенциальными проблемами, которые могут возникнуть при неправильном использовании таких политик.
Для начала краткая вводная: о каких «политиках поверх HttpClient’а» мы говорим, и зачем они нужны?
Допустим, сервис А запрашивает какие-то данные у сервиса B путём обычного http-запроса. К сожалению, сеть — штука ненадёжная, а сервера могут выходить из строя. Мы не можем гарантировать, что наш запрос успешно дойдёт и будет обработан на стороне сервиса B.
В ответ сервис B может ответить ошибкой или сервис B может быть недоступен, тогда наш запрос может и вовсе потеряться на просторах сети. Чтобы как-то повысить надёжность наших запросов, придуманы различные так называемые политики.
Такие политики работают как обёртка над стандартным HttpClient’ом. Каждая из политик перехватывает запрос, проверяет ответ от сервера и выполняет какие-то операции.
В мире .NET эта проблема в некотором смысле решена. Есть прекрасная библиотека Polly, которая предоставляет уже готовые политики. Достаточно выбрать те, которые вам нужны, обернуть свой HttpClient, и дело в шляпе. Но, как мы знаем, дьявол кроется в деталях.
Если вы хорошо знаете эту библиотеку, прочитали всю документацию к ней и во всём разобрались, то можете закончить читать статью прямо сейчас, ничего нового вы не узнаете. Хотя нет, оставайтесь, сможете посмеяться над нашими ошибками.
В чём же тут проблема? Берём клиент, обвешиваем нужными политиками, которые декорируют исходный HttpClient, и всё работает. Ниже приведён пример, как сейчас собирается HttpClient с помощью IHttpClientBuilder’а и какие политики к нему применяются.
clientBuilder .AddRetryPolicy(settings.RetrySettings) .AddCircuitBreakerPolicy(settings.CircuitBreakerSettings) .AddTimeoutPolicy(settings.TimeoutPerTry);
Здесь ровно три строчки кода с применением политик. Ниже мы рассмотрим, как можно ошибиться в каждой из них, вдобавок ещё и порядок напутать. Итого 4 ошибки.
Начнём с повторных запросов. Тут вроде бы всё достаточно просто: если код ответа от сервера находится в списке кодов, которые нужно «ретраить» (разрешите мне ввести в нашу терминологию такое классное русское слово), то мы выполняем запрос ещё раз. В самой библиотеке Polly есть описание синтаксиса и работы такой политики.
В чём же тут можно ошибиться? На самом деле, когда мы имеем дело с распределёнными системами, то ошибиться можно абсолютно во всем. Рассмотрим несколько моментов, которые могут потенциально вызвать проблему.