node.js 使用redis做分布式限流

原理分析:
node.js 如果用进程间通信ipc 做分布式限流,部署起来会比较麻烦,要考虑到进程间通信,状态维护等,redis性能足够,做起来相对简单

具体使用方法

    redis.setnx 如果当前键存在,则啥也不做就返回0 不存在设置这个键 返回1
    redis.getset 设置新值得同时返回旧值

利用redis.getset 来判断当前的请求是否已经被抢占
伪代码如下

    let value = await redis.setnx(redisKey,`[timestamps]`);
    // 这里第一次请求
    if (value) {
        // 直接请求成功
        return next()
    }
    // 如果之前已经有请求 拿到历史请求数距
    let history = await redis.get(redisKey);
        history = JSON.parse(history);
        let lastHitTime = history[history.length - 1];
        // 如果当前时间大于上次设置时间,
        let setTime = new Date().getTime();
        if (setTime < lastHitTime) {
          return res.status(429).send();
        }
        history.push(setTime);
        if (history.length > params.rateSpeed * 2) {
          // 只保留100次请求的信息
          history = history.slice(history.length - params.rateSpeed * 2);
        }
        let oldHistory = await redis.getset(redisKey, JSON.stringify(history));
        oldHistory = JSON.parse(oldHistory);
        if (oldHistory[oldHistory.length - 1] > setTime) {
          // 这里表示有其他进程 设置了时间
          // 这个列表维护起来略复杂,直接block 这个请求
          // 这里设置的时间不超过20ms 如果出现了抢占直接block好了
          // 这里由于block 太多了,导致很多请求被丢弃了。。
          return res.status(429).send();
        }
        let hitHistory = oldHistory.filter(val => parseInt(val / 1000) === currentSecond);
        if (hitHistory.length >= params.rateSpeed) {
          return res.status(429).send();
        }







不过这里的设置会导致极为大量的请求进来,有一部分数据会被额外的丢弃,