使用Bottleneck对库中的API请求进行速率限制

问题描述

我正在用TypeScript编写API包装器。我希望代码是异步的,以便最大程度地满足相关API的速率限制。该API希望请求以最大1 /秒的速度提交。

我打算实现一个API封装,该封装被实例化一次,并允许使用对象到达不同的端点。例如,在较大的API中,有一个postpool端点。我想像post_object.post.submit_request(argument1,...)post_object.pool.submit_request(argument1,...)那样访问它们。

我创建了一个名为state_info的对象,该对象在各个对象之间传递,该对象中包含用户代理标头,登录信息(如果提供)以及瓶颈库中的速率限制对象。

我在测试时遇到的问题是我的程序似乎并没有真正限制请求的速度;无论我将“瓶颈”参数的限制更改为什么,每次请求都将在大约.600秒内发生。

我认为这与传递速率限制对象或从多个位置访问它有关,但是我不确定。

首先,这是Model对象的代码,代表对API的访问。

import axios,{ AxiosRequestConfig } from "axios";
import { StateInfo,Method } from "./interfaces";

export class Model {
  public stateInfo: StateInfo;

  constructor(stateInfo: StateInfo) {
    // Preserve rate limiter,user agent,etc.
    this.stateInfo = stateInfo;
  }

  //Updated to funcName = () => {} Syntax to bind "this" to this class context.
  private submit_request = (query_url: string,method: Method) => {
    if (this.stateInfo.username && this.stateInfo.api_key) {
      const axiosConfig: AxiosRequestConfig = {
        method: method,url: query_url,headers: { "User-Agent": this.stateInfo.userAgent },auth: {
          username: this.stateInfo.username,password: this.stateInfo.api_key,},};
      return axios(axiosConfig);
    }  else {
      const axiosConfig: AxiosRequestConfig = {
        method: "get",};

      return axios(axiosConfig);
    }
  };

  public submit_throttled_request = (url: string,method: Method) => {
    return this.stateInfo.rateLimiter.schedule(
      this.submit_request,url,method
    );
  };
}

然后,我从中调用该类的代码

import { Model } from "./models/model";
import Bottleneck from "bottleneck";

const limiter: Bottleneck = new Bottleneck({ mintime: 1000,maxconcurrent: 1 });

const stateInfo = {
  rateLimiter: limiter,userAgent: "[email protected] | API Dev",};

let modelObj: Model = new Model(stateInfo);

async function makeRequest() {
  try {
    let response = await modelObj.submit_throttled_request(
      "https://www.website.com/api","get"
    );
    console.log(response.data.id + "|" + Date.Now());
  } catch (err) {
    console.log(err);
  }
}

let start = new Date();
for (let i = 0; i < 20; i++) {
  makeRequest();
}

我的期望是,如果每秒只能提交一个请求,那么该操作将至少花费10秒。但是,无论我为mintime包括什么,我平均要得到一半。

解决方法

经过反复的摸索,我已经学会了自己的问题的答案。

事实证明,他们在"gotchas"bottleneck API reference部分中指出:

如果要将对象的方法作为作业传递,则可能需要对对象进行bind():

具有以下代码:

// instead of this:
limiter.schedule(object.doSomething);
// do this:
limiter.schedule(object.doSomething.bind(object));
// or,wrap it in an arrow function instead:
limiter.schedule(() => object.doSomething());

这是我遇到的问题。我在交递axios(axiosContext)时没有绑定范围,因此没有任何东西发送给瓶颈速率限制器。通过包装是这样的:this.state_info.rateLimiter.schedule(() => axios(axiosContext));我已经设法根据需要正确地绑定上下文。