Как стать автором
Обновить

Как потреблять API с ограничением по RPS в .NET приложениях

Уровень сложностиПростой
Время на прочтение11 мин
Количество просмотров8.7K
Всего голосов 45: ↑44 и ↓1+63
Комментарии10

Комментарии 10

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

Вы не любите кошексамописные реализации интерфейсов? Да вы просто не умеете их готовить! ;-)

Зачем встраивать? Добавьте сервис для IDataObjectCollectionProvider с реализацией в виде этого вашего DataObjectCollectionProvider на семафоре в контейнер сервисов (он же - DI-контейнер) - и будет вам щастье: можете теперь этот интерфейс внедрять через DI, или вытаскивать из контейнера через Service Locator, и ничего перемешивать не надо. А реализация интерфейса такая - это для начала, потом поменяете ее, если надо будет, и никакой другой код даже трогать не придется (в идеале, конечно). Ну, а дополнительные параметры для настройки в эту вашу реализацию передать можно передать, используя Options pattern.
Только предварительно сервис для IDataObjectCollectionProvider еще надо в контейнер добавить (если вдруг его раньше никто не добавил), чтобы контейнер смог реализацию этого интерфейса в конструктор DataObjectCollectionProvider передать.

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

Думаю автор просто неудачно слова подобрал.

Жаль только это все не помогает, когда надо ограничивать несколько реплик. Пришлось token bucket запихивать в постгрес, и писать обертки, чтобы прозрачно встроиться в httpclient

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

круто, спасибо. почему-то не нашел именно эту либу, когда надо было. смотрю, для постгреса там advisory lock. его и юзаем, но такая абстракция была бы полезной конечно

а redis, увы, плохо кластеризуется в мульти-цод среде, поэтому его не используем для синхронизаций вообще

А как клиенту узнать за какой период можно выполнить лимит запросов?

У вас в примере в 1 секунду можно выполнить 10 запросов, а что если на сервере изменят настройку и поставят за одну минуту можно выполнить 100 запросов.

Немного странный вопрос)

Обычно эту информацию предоставляет внешняя система в документации.
Например, https://dadata.ru/api/find-party/#restrictions

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

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

Там проблема решается, например, через Options Pattern, и есть кучу вариантов как это сделать: от конфига до OptionsMonitor

Так что код клиента править не придётся

Вообще, странно, что интернет сопротивляется и не выдаёт вам довольно подробную инструкцию от microsoft https://learn.microsoft.com/en-us/dotnet/core/extensions/http-ratelimiter
тут описан подход, который гораздо лучше ложится в любую архитектуру на dotnet, а именно лучше писать не обёртку над apiClient, а сделать обертку над handler, например HttpMessageHandler и встраивать его внутрь HttpClient например. Так пользователь вашего апи работает с обычной абстракцией, к которой привык, а не кастомной обёрткой

Зарегистрируйтесь на Хабре, чтобы оставить комментарий