基于redis实现时间段内并发限制(滑动窗口)

  • 时间点一:[1, 2, 3], 4, 5, 6, 7, 8, 9
  • 时间点二:1, [2, 3, 4], 5, 6, 7, 8, 9
  • 时间点三:1, 2, [3, 4, 5], 6, 7, 8, 9
  • 时间点四:1, 2, 3, [4, 5, 6], 7, 8, 9
import json
import time

from redis import StrictRedis

redis_cli = StrictRedis()


class Demo:
    """按照资源类型限制,每种资源类型限制并发"""
    LIMIT = 10  # 阈值
    INTERVAL = 30  # 时间(秒)

    def __init__(self):
        self.redis = redis_cli
        ...

    def need_to_que(self, res_type: str):
        """
        判断资源是否已经超过设置阈值
        redis有序集合里面按照先后顺序存放资源放入的时间,大小为LIMIT的值
        """
        key = f"Limit:{res_type}"
        send_times = self.redis.zrange(key, 0, int(time.time() * 1000))
        while send_times:
            if int(send_times[0]) >= time.time() * 1000 - self.INTERVAL *  1000:
                break
            self.redis.zrem(key, send_times[0])
            send_times.pop(0)
        return send_times and len(send_times) >= self.LIMIT

    def set_flag(self, res_type):
        """
        任何被资源处理的时间戳放入池中
        """
        key = f"Limit:{res_type}"
        now = int(time.time() * 1000)
        self.redis.zadd(key, {now: now})

    def send_to_que(self, res_type: str, **data):
        """
        被限制的资源放入队列中,由消费者二次消费
        """
        key = f"Queue:{res_type}"
        self.redis.rpush(key, json.dumps(data))

    def action(self, res_type, data):
        """
        分步操作,所以需要加锁
        """
        key = f"Lock:{res_type}"
        with self.redis.lock(key, 3):
            if self.need_to_que(res_type):
                self.send_to_que(res_type, data=data)
                return "Restricted"
            self.set_flag(res_type)

        # 后面是业务逻辑
        return "Success"


if __name__ == '__main__':
    resource_type = "demo"
    redis_cli.delete(f"Limit:{resource_type}", f"Queue:{resource_type}")
    demo = Demo()
    demo.INTERVAL = 1
    demo.LIMIT = 5
    print(f"规则:{demo.INTERVAL}秒并发{demo.LIMIT}次")
    for i in range(20):
        demo.action(resource_type, f"Action {i}")
        time.sleep(0.1)
        pool = redis_cli.zrange(f"Limit:{resource_type}", 0, 100)
        queue = redis_cli.lrange(f"Queue:{resource_type}", 0, 100)
        print(f"当前池大小:{len(pool)},任务时间戳为:{pool}")
        print(f"当前被限制操作:{queue}")
        print("------------------------------------------")
    time.sleep(5)
    for i in range(20, 40):
        demo.action(resource_type, f"Action {i}")
        time.sleep(1)
        pool = redis_cli.zrange(f"Limit:{resource_type}", 0, 100)
        queue = redis_cli.lrange(f"Queue:{resource_type}", 0, 100)
        print(f"当前池大小:{len(pool)},任务时间戳为:{pool}")
        print(f"当前被限制操作:{queue}")
        print("------------------------------------------")

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值