Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NestJS 限流代码分析 #47

Open
ChuChencheng opened this issue Jul 27, 2022 · 0 comments
Open

NestJS 限流代码分析 #47

ChuChencheng opened this issue Jul 27, 2022 · 0 comments
Labels

Comments

@ChuChencheng
Copy link
Owner

背景

有关限流的知识可以参考 5 种限流算法,7 种限流方式,挡住突发流量?

代码地址

https://github.com/nestjs/throttler/blob/v3.0.0/src/throttler.guard.ts#L77

核心代码应该是这行开始了, Guard 中的 handleRequest

https://github.com/nestjs/throttler/blob/v3.0.0/src/throttler.service.ts#L17

以及 ThrottlerStorageService addRecord 方法

代码分析

ThrottlerGuard

const ttls = await this.storageService.getRecord(key);
const nearestExpiryTime = ttls.length > 0 ? Math.ceil((ttls[0] - Date.now()) / 1000) : 0;

// Throw an error when the user reached their limit.
if (ttls.length >= limit) {
  res.header('Retry-After', nearestExpiryTime);
  this.throwThrottlingException(context);
}

ttls.length 大于等于 limit (ttl 中允许的请求数量,如果 ttl 是 1秒 ,则 limit 就是 QPS 的意思),则会被限流。

那么, ttls 里面存放的东西就很关键

ThrottlerStorageService

深入 ThrottlerStorageService addRecord 方法,可以看到 ttls 数组存的是什么

const ttlMilliseconds = ttl * 1000;
if (!this.storage[key]) {
  this.storage[key] = [];
}

this.storage[key].push(Date.now() + ttlMilliseconds);

从代码中可以看出, ttls 存的是请求过期的时间点

也就是说,当一个请求过来,会往 ttls 数组中 push 一个时间点,代表到这个时间点之后,允许多放行一个新的请求。

还有多久可以放行请求

知道了 ttls 存放的是时间点,那么假设一位用户被限流了,他想知道多久后才会解除限流,就可以从 ttls 数组中得到这个信息,只需要取数组的第一项,减去现在的时间。

const nearestExpiryTime = ttls.length > 0 ? Math.ceil((ttls[0] - Date.now()) / 1000) : 0;

优缺点

NestJS 自带限流其实就是采用滑动日志的方式。

优点

优点就是限流比较精确,可以防止流量突刺

缺点

缺点也很明显,比较占用内存,假设 ttl 为 1 ,限制 QPS 为 1000 , ttls 最大就会有 1000 个元素

addRecord 中, ttls 数组是通过 setTimeout 的方式去清理的,那么 ttls 有多少个元素,就会有多少个 setTimeout 在排队

优化

本来是想着把 setTimeout 给优化掉,但是好像意义并不大,因为这种限流算法本来就是一种用空间妥协换取限流精确度的做法。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant