在库中使用瓶颈 rate-limit API 请求

Using Bottleneck to rate-limit API requests in a library

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

我打算实现一个实例化一次的 API 包装器,并允许使用 object 到达不同的端点。例如,在更大的 API 中有一个 postpool 端点。我想像 post_object.post.submit_request(argument1, ...)post_object.pool.submit_request(argument1, ...).

一样访问它们

我创建了一个名为 state_info 的 object,它在各种 object 之间传递,其中包含一个 user-agent header,登录如果提供了信息,以及瓶颈库中的 rate-limiter object。

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

我认为这与绕过 rate-limiter object 或从多个位置访问它有关,但我不确定。

首先,这里是Modelobject的代码,表示进入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",
        url: query_url,
        headers: { "User-Agent": this.stateInfo.userAgent },
      };

      return axios(axiosConfig);
    }
  };

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

然后,我调用它的代码 class:

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

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

const stateInfo = {
  rateLimiter: limiter,
  userAgent: "email@website.com | 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.

中包含什么,我的平均值是一半

我在 head-scratching.

之后找到了自己问题的答案

事实证明,在 "gotchas" section of the bottleneck API reference 他们注意到:

If you're passing an object's method as a job, you'll probably need to bind() the object:

使用以下代码:

// 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));我已经设法根据需要正确绑定上下文。