在库中使用瓶颈 rate-limit API 请求
Using Bottleneck to rate-limit API requests in a library
我正在用 TypeScript 编写 API 包装器。我希望代码是异步的,以便最大限度地满足相关 API 的速率限制。 API 希望以每秒 1 次的最大速率提交请求。
我打算实现一个实例化一次的 API 包装器,并允许使用 object 到达不同的端点。例如,在更大的 API 中有一个 post
和 pool
端点。我想像 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 或从多个位置访问它有关,但我不确定。
首先,这里是Model
object的代码,表示进入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));
我已经设法根据需要正确绑定上下文。
我正在用 TypeScript 编写 API 包装器。我希望代码是异步的,以便最大限度地满足相关 API 的速率限制。 API 希望以每秒 1 次的最大速率提交请求。
我打算实现一个实例化一次的 API 包装器,并允许使用 object 到达不同的端点。例如,在更大的 API 中有一个 post
和 pool
端点。我想像 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 或从多个位置访问它有关,但我不确定。
首先,这里是Model
object的代码,表示进入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));
我已经设法根据需要正确绑定上下文。