Комментарии 10
На мой взгляд, выглядит достаточно кустарно — надо встраивать использование примитива синхронизации в логику вызова и перебора, перемешивая инфраструктурный код с бизнес-логикой.
Вы не любите кошексамописные реализации интерфейсов? Да вы просто не умеете их готовить! ;-)
Зачем встраивать? Добавьте сервис для IDataObjectCollectionProvider с реализацией в виде этого вашего DataObjectCollectionProvider на семафоре в контейнер сервисов (он же - DI-контейнер) - и будет вам щастье: можете теперь этот интерфейс внедрять через DI, или вытаскивать из контейнера через Service Locator, и ничего перемешивать не надо. А реализация интерфейса такая - это для начала, потом поменяете ее, если надо будет, и никакой другой код даже трогать не придется (в идеале, конечно). Ну, а дополнительные параметры для настройки в эту вашу реализацию передать можно передать, используя Options pattern.
Только предварительно сервис для IDataObjectCollectionProvider еще надо в контейнер добавить (если вдруг его раньше никто не добавил), чтобы контейнер смог реализацию этого интерфейса в конструктор DataObjectCollectionProvider передать.
Жаль только это все не помогает, когда надо ограничивать несколько реплик. Пришлось token bucket запихивать в постгрес, и писать обертки, чтобы прозрачно встроиться в httpclient
Есть целый набор пакетов под названием DistibutedLock. С помощью них можно просто реализовать распределеный троттлинг через Redis, например.
А как клиенту узнать за какой период можно выполнить лимит запросов?
У вас в примере в 1 секунду можно выполнить 10 запросов, а что если на сервере изменят настройку и поставят за одну минуту можно выполнить 100 запросов.
Немного странный вопрос)
Обычно эту информацию предоставляет внешняя система в документации.
Например, https://dadata.ru/api/find-party/#restrictions
Если такую информацию зашивать в код, то при изменении со стороны сервера придется править код клиента, не лучшее решение. Часто изменения клиент узнает, когда начинает получать 429 код. Думал, раз вы в эту тему погрузились, то должны были знать, что лимит запросов, остаток запросов и через какое время выполнить следующий успешный запрос передаются в заголовках http.
Вообще, странно, что интернет сопротивляется и не выдаёт вам довольно подробную инструкцию от microsoft https://learn.microsoft.com/en-us/dotnet/core/extensions/http-ratelimiter
тут описан подход, который гораздо лучше ложится в любую архитектуру на dotnet, а именно лучше писать не обёртку над apiClient, а сделать обертку над handler, например HttpMessageHandler и встраивать его внутрь HttpClient например. Так пользователь вашего апи работает с обычной абстракцией, к которой привык, а не кастомной обёрткой
Как потреблять API с ограничением по RPS в .NET приложениях