Сегодня большинство приложений управляется API. Независимо от того, создаете ли вы прогноз погоды, финансовый тикер, оповещение о спортивных результатах или переводите на местный язык, вам потребуется подключить сторонние API для доступа к необходимым данным.
API обычно измеряются, а потребление ограничивается ограничением скорости. API с ограничением скорости определяет скорость API. Обычно это выражается в количестве запросов, сделанных за определенный период времени; например, скорость API котировок цен на акции API составляет 100 в секунду, что означает, что вы можете делать только 100 вызовов в секунду.
Механизм стимуляции должен справляться с разрывом между скоростью запросов приложений и скоростью принятия API.
Настройка сценария
Предположим, мы хотим получить прогноз погоды для всех 19000
(приблизительно) почтовых индексов (PIN) в Индии. Один запрос может привести только к предсказанию одного почтового индекса, а максимальная частота вызовов API составляет 50 per second
.
ОЧЕВИДНО, мы не можем выполнить 19000
запросов одновременно для получения данных. Нам нужно создать механизм для запросов в темпе.
В этом посте показано создание Pacer (на машинописном языке), который принимает все запросы на пин-коды и обращается к API с соответствующей скоростью. И браузер, и узел будут совместимы с кодом.
Понимание механизма стимуляции
Если нам нужно выполнить 19000
запроса (1 запрос за один вызов) за один вызов, мы храним запросы в памяти и берем 50 из них, выполняем их в следующую секунду, берем следующие 50 и так далее. По сути, он ограничивает запросы, не отклоняя их по максимальному лимиту.
Создание интерфейса
Логика стимуляции вполне может содержаться внутри класса. Лучше всего разделить логику вызова API и логику стимуляции. Затем темп может применяться в различных контекстах.
Интерфейс кардиостимулятора
class Pacer
принимает запросы и обрабатывает их в заданном темпе. Запрос — это самоисполняемый код для его выполнения, и Pacer решает, когда запускать этот код.
interface Request<T> { (): Promise<T>; } class Pacer<T> { private ratePerSecond: number; constructor(ratePerSecond: number) { this.ratePerSecond = ratePerSecond; } pace(request: Request<T>): Promise<T> { // implementation pending return request(); } }
Использование кардиостимулятора
Чтобы использовать Pacer
, мы должны создать его экземпляр с требуемой скоростью. После этого можно использовать метод pace
для добавления запросов.
let pacer = new Pacer<Axios.Response>(50); let resultPromises = []; for(let pinCode of pinCodes) { resultPromises.push(pacer.pace(() => $axios.get(`https://api.weather.org/weather/${pinCode}`)); } let responses = await Promise.all(results);
Внедрение темпа
Функционально правильной базовой реализацией метода темпа может быть:
При использовании приведенной выше реализации метод pace
будет выполнять запрос сразу после получения, аналогично коду без стимуляции. Таким образом, мы должны пойти дальше.
Микропакеты
Запросы должны быть объединены в пакеты и выполняться один раз в секунду для надлежащей реализации. Этот метод называется микропакетной обработкой: Меньшая партия, частое выполнение. Нам нужна очередь и исполнитель для контролируемого выполнения запросов.
Очередь
Запрос будет поставлен в очередь, как только будет получен. Нам также нужно вернуть обещание результата запроса. Но мы не можем выполнить обещание без выполнения запроса.
Прокси-обещание
Будет возвращено Proxy Promise, которое зафиксирует результат запроса при выполнении. Нам нужно сохранить его разрешение и отклонить ссылки вместе с запросом.
Исполнение
Надежным выбором дизайна является сохранение выполнения запроса в отдельной функции, которая выполняет несколько запросов в очереди. Теперь нам нужно работать над тем, чтобы запускать его каждую секунду.
setTimeout()
является разумной конструкцией для использования, чем setInterval
(), так как с setTimeout
мы можем контролировать следующее выполнение.
Логика планирования
- Когда запрос принят, он может быть обработан немедленно или с задержкой в несколько миллисекунд, если выполнение не запланировано.
- На стороне выполнения после запуска запросов мы проверяем размер очереди для планирования следующего выполнения с задержкой
1s
.
Реализация работает, если запросы выполняются в течение одной секунды; вряд ли это так в реальном мире. Чтобы сделать его более эффективным, мы должны отслеживать количество обрабатываемых запросов и соответственно корректировать темп.
Полный код доступен по адресу:
Что дальше?
Это практическая реализация. Другой вариант — включить механизм повторной попытки или отказа. Я еще не рассматривал возможность реализации этой функции; Буду признателен за ваши отзывы по улучшению.