前言

业务上线之后,经常会出现一些来路不明的人在持续不断的请求业务接口,这些请求就算没有产生数据安全问题也会对业务服务器产生一些压力,那么如何拦截这些请求就成了一个不得不去面对的问题。
解决方案有很多种,其中之一就是:**根据 IP 限制访问接口的频率**

设计思路

以 10 秒钟之内请求次数不超过 20 次为例

  • 把请求的 ip 和接口路由 path 拼接起来作为 key,把请求的次数作为 value,存进 Redis,并设置过期时间为 10 秒

  • 第一次请求设置 value 为 1,以后每次请求 value + 1

  • 每一次请求都重置过期时间,并且判断 value 是否大于 10

  • 如果没过期,且 value 大于 10,则是应该被限制的请求

伪代码

以 eggjs/midway 中间件为例

const cacheName = `${ip}-${path}`; // key

const ttl = await redisService.ttl(cacheName); // key是否有剩余时间

if (ttl > 0) {
  const count = await redisService.read(cacheName); // key已经请求的次数
  if (count && +count < 20) {
    // 如果还没到20次,次数加1,重置过期时间,正确返回
    await redisService.write(cacheName, count + 1, ttl);
    await next();
  } else {
    // 次数大于阈值,拦截,返回429
    throw Error("too many requests", 429);
  }
} else {
  // ttl < 0, 重新写
  await redisService.write(cacheName, 1, 10);
  await next();
}