Что есть сейчас:
public static Page GetPage(Uri url)
Что нужно:
Ограничить число запросов в рамках определенных "контекстов".
В целом, с одной стороны хочется чтобы когда я делаю условный godObject.Download()
, а тот делает какие то свои проверки внутри, плюс скачивание, плюс ещё чего-нибудь, чтобы внутри не возникала куча запросов параллельных, с другой - чтобы когда я делаю simpleClass.DownloadAllLinks()
он мог обработать параллельно пачку ссылок, если на разных ресурсах находятся.
Видится это примерно как свойство Request
, которое в рамках вебсервера есть практически всегда, т.к. обработка запроса и есть весь жизненный цикл. У меня цикл приложения в любом случае какой то тоже есть, например нажатие кнопки пользователем (но оно не будет к сожалению контекстом для скачивания страничек). Но сложить всё это в какую то логическую схему пока не могу.
Переделывать метод на нестатический не хочется, потому что он уже много где используется и явно получить там например экземпляр "контекста" будет нелегко.
Основная идея мне пришла давно и в целом, её реализуемость подтвердило это сообщение
К сожалению, в netcore пока нет CallContext
, но enSO подсказало выход, который при тестировании вел себя аналогично и никаких проблем не выявил.
В итоге, как выглядит код:
using (ThrottleService.SetThrottler(new Throttler(15)))
{
godObject.Download();
}
ThrottleService
просто ставит в CallContext
экземпляр троттлера, а GetPage
внутри просто обращается к CallContext
и проверяет, как там у него с ограничениями.
Отлично сработало с создаваемыми подтасками, внутри троттлера тупо SemaphoreSlim
.
Из того, что ещё можно сделать - можно вкладывать троттлеры друг в друга, чтобы работали конструкции вида (созданные в разных методах):
using (ThrottleService.SetThrottler(new Throttler(15)))
using (ThrottleService.SetThrottler(new Throttler(10)))
Это решение неплохо заходит с точки зрения прикладной логики - если ты знаешь, что твой код может генерировать много запросов, укажи какое то ограничение.
Тут же кроется и минус - если ты ограничение указал в конкретном месте, то выше по стеку можно только ещё больше ограничить условия, передать только своё ограничение уже не получится. Над этим можно подумать и изобрести костыль, но в целом тут скорее прав A K и стоит подумать над рефакторингом. Пока, в целях ускорения решения задачи я скорее скомбинирую код - добавлю и решение с CallContext
и добавлю возможность явно передавать троттлер, для случаев когда он и так под рукой имеется.
Сталкивался с аналогичными задачами в рамках "качаем сайты" или "делаем запросы к СМЭВ" и в общем-то не понимаю сути вопроса. Вам ничего не мешает внутри вашего метода расположить любую логику "не более трёх запроов в секунду на такой-то домен и не более пяти на другой" и ставить в очередь запросы, которые пока невозможно обработать по ограничениям.
Ведь вы и сами понимаете положа руку на сердце, что пошли по кривой тропке, где ваш объект всё больше становится god object - и понимаете, что переделка сигнатуры метода уже дастся немалой кровью — но пока не готовы вернутся на правильную дорогу. Что ж, идите дальше — просто дальше цена будет лишь больше.
По мне вам просто нужно решиться на нормальный рефакторинг. Видел много людей, которые долго собираются с мыслью пойти к стоматологу, а потом удивляются, чего же это они раньше не пошли. Тут что-то похожее. Вам пока не поздно — отрефакторьте по-уму, самому же потом проще будет, особенно когда будете добавлять отдельные потоки/обработчики, каждый из которых будет свою прокси иметь, свои очереди и полиси.
Мне видится это как полноценный объект, который умеет принимать ссылку в очередь и умеет работать с политиками очереди. Ну и пашет себе где-то в отдельном от UI потоке.
Виртуальный выделенный сервер (VDS) становится отличным выбором
Есть тут люди которые помогут разобраться с созданием кастомных элементов для WPF C#?
У меня есть таблица городов около 25 миллионов записей (весит 2гб) и если в SQL запрос выполняется моментально по поиску, то Entity Framework начинает...
Добавил стандартное окно браузера от Cefsharp, пытаюсь авторизоваться на сайте и получить ответ при помощи IRequestHandler, но в итоге из за OnBeforeResourceLoad...