Сегодня большинство приложений управляется 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 мы можем контролировать следующее выполнение.

Логика планирования

  1. Когда запрос принят, он может быть обработан немедленно или с задержкой в ​​несколько миллисекунд, если выполнение не запланировано.
  2. На стороне выполнения после запуска запросов мы проверяем размер очереди для планирования следующего выполнения с задержкой 1s.

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

Полный код доступен по адресу:



Что дальше?

Это практическая реализация. Другой вариант — включить механизм повторной попытки или отказа. Я еще не рассматривал возможность реализации этой функции; Буду признателен за ваши отзывы по улучшению.