问题描述
我正在用TypeScript编写API包装器。我希望代码是异步的,以便最大程度地满足相关API的速率限制。该API希望请求以最大1 /秒的速度提交。
我打算实现一个API封装,该封装被实例化一次,并允许使用对象到达不同的端点。例如,在较大的API中,有一个post
和pool
端点。我想像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));
我已经设法根据需要正确地绑定上下文。