const result = await request('http://some.api');
counter--;
queue.shift()();
return result;
}
下面还有一个方便复用的版本。
async function limitWrapper(func, maxAllowedRequest) {
const queue = [];
const counter = 0;
return async function () {
if (counter > maxAllowedRequest) {
await new Promise((resolve, reject) => {
queue.push(resolve);
});
}
counter++;
const result = await func();
counter--;
queue.shift()();
return result;
}
}
针对路由进行限流
这种方式是写一个koa中间件,在中间件中进行限流:
async function limiter(ctx, next) => {
// 如果超过了最大并发数目
if (counter >= maxAllowedRequest) {
// 如果当前队列中已经过长
await new Promise((resolve, reject) => {
queue.push(resolve);
});
}
store.counter++;
await next();
store.counter--;
queue.shift()();
};
之后针对不同路由在router中使用这个中间件就好了:
router.use('/api', rateLimiter);比较
实现了针对接口进行限流,觉得逻辑有些乱,于是改用了针对路由进行限流,一切运行的很完美。
直到我又接了个需求,是要请求三次这个接口返回三次请求的结果数组。现在问题来了,我们不能直接调用接口,因为要限流。也不能直接调用请求接口的函数因为我们的限流是以路由为单位的。那怎么办呢?我们只有请求这个路由了,自己请求自己。。。
需要注意的地方
监听close事件,将请求从队列中移出
已经存储在队列中的请求,有可能遇到用户取消的情况。前面说过koa中即使请求取消,之后的中间件还是会运行,也就是还会执行需要限流的接口,造成浪费。
可以监听close事件来达到这个目的,每个请求我们需要用hash值来标记:
ctx.res.on('close', () => {
const index = queue.findIndex(item => item.hash === hash);
if (index > -1) {
queue.splice(index, 1);
}
});设置超时时间
为了防止用户等待过长时间,需要设置超时时间,这在koa中很容易实现:
const server = app.listen(config.port);
server.timeout = DEFAULT_TIMEOUT;当前队列已经过长
如果当前队列已经过长了,即使加入队列中也会超时。因此我们还需要处理队列过长的情况:
if (queue.length > maxAllowedRequest) {









