#c_sharp #архитектура
Что есть сейчас: public static Page GetPage(Uri url) Что нужно: Ограничить число запросов в рамках определенных "контекстов". Это может быть host в параметре url (и это самый просто вариант, можно просто записывать текущие хосты и ограничивать) Может быть "сайт", который хостит разные вещи на разных инстансах облачного провайдера и по хосту их уже не отличить. Может быть сборка запрашивающая метод (т.е. сборке например нельзя этим методом получать больше чем 3 разных страницы в один момент времени) Может быть метод (метод не должен например параллельно делать большую пачку запросов), хотя возможно входит в предыдущий вариант, не продумывал пока детально. В целом, с одной стороны хочется чтобы когда я делаю условный godObject.Download(), а тот делает какие то свои проверки внутри, плюс скачивание, плюс ещё чего-нибудь, чтобы внутри не возникала куча запросов параллельных, с другой - чтобы когда я делаю simpleClass.DownloadAllLinks() он мог обработать параллельно пачку ссылок, если на разных ресурсах находятся. Видится это примерно как свойство Request, которое в рамках вебсервера есть практически всегда, т.к. обработка запроса и есть весь жизненный цикл. У меня цикл приложения в любом случае какой то тоже есть, например нажатие кнопки пользователем (но оно не будет к сожалению контекстом для скачивания страничек). Но сложить всё это в какую то логическую схему пока не могу. Переделывать метод на нестатический не хочется, потому что он уже много где используется и явно получить там например экземпляр "контекста" будет нелегко.
Ответы
Ответ 1
Основная идея мне пришла давно и в целом, её реализуемость подтвердило это сообщение К сожалению, в 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 и добавлю возможность явно передавать троттлер, для случаев когда он и так под рукой имеется.Ответ 2
Сталкивался с аналогичными задачами в рамках "качаем сайты" или "делаем запросы к СМЭВ" и в общем-то не понимаю сути вопроса. Вам ничего не мешает внутри вашего метода расположить любую логику "не более трёх запроов в секунду на такой-то домен и не более пяти на другой" и ставить в очередь запросы, которые пока невозможно обработать по ограничениям. Ведь вы и сами понимаете положа руку на сердце, что пошли по кривой тропке, где ваш объект всё больше становится god object - и понимаете, что переделка сигнатуры метода уже дастся немалой кровью — но пока не готовы вернутся на правильную дорогу. Что ж, идите дальше — просто дальше цена будет лишь больше. По мне вам просто нужно решиться на нормальный рефакторинг. Видел много людей, которые долго собираются с мыслью пойти к стоматологу, а потом удивляются, чего же это они раньше не пошли. Тут что-то похожее. Вам пока не поздно — отрефакторьте по-уму, самому же потом проще будет, особенно когда будете добавлять отдельные потоки/обработчики, каждый из которых будет свою прокси иметь, свои очереди и полиси. Мне видится это как полноценный объект, который умеет принимать ссылку в очередь и умеет работать с политиками очереди. Ну и пашет себе где-то в отдельном от UI потоке.
Комментариев нет:
Отправить комментарий