from redis import Redis import time import math import random redis_conn = Redis(host='localhost',port=6689,password="redis_user",db=0) hasinit = False def init_redis_key(key,times): redis_conn.hset(key, 'tokens', times) last_time = int(time.time()) # 记录令牌生成时间 redis_conn.hset(key, 'last_time', last_time) def can_pass_token_bucket(user, action, time_zone=60, times=30): """ :param user: 用户唯一标识 :param action: 用户访问的接口标识(即用户在客户端进行的动作) :param time_zone: 接口限制的时间段 :param time_zone: 限制的时间段内允许多少请求通过 """ # 请求来了就倒水,倒水速率有限制 key = '{}:{}'.format(user, action) global hasinit if not hasinit: init_redis_key(key,times) hasinit = True rate = times / time_zone # 令牌生成速度 capacity = times # 桶容量 tokens = redis_conn.hget(key, 'tokens') # 看桶中有多少令牌 last_time = redis_conn.hget(key, 'last_time') # 上次令牌生成时间 now = int(time.time()) if tokens: tokens = int(tokens) else: tokens = capacity if last_time: last_time = int(last_time) else: last_time = now print("tokens:",tokens) delta_tokens = (now - last_time) * rate # 经过一段时间后生成的令牌 if delta_tokens >= 1: tokens = tokens + math.ceil(delta_tokens) # 增加令牌 if tokens > capacity: tokens = capacity last_time = int(time.time()) # 记录令牌生成时间 # print("hset last_time2:",last_time) redis_conn.hset(key, 'last_time', last_time) if tokens >= 1: tokens = tokens - 1 # 请求进来了,令牌就减少1 print("hset tokens2:",tokens) redis_conn.hset(key, 'tokens', tokens) return True return False if __name__ == "__main__": i = 0 while i < 120: i = i + 1 if can_pass_token_bucket("77-88-99","get"): print(f"第{i}次成功") else: print(f"第{i}次失败") print("\n") # 通过sleep模拟不同时间段请求速率的不同,当请求过快,token充足,请求能通过,token不足以后,返回失败 # 当请求速率降下来之后,token的数量能上去,可以应对突发请求 token的数值大,突发请求成功数量就多 if i > 1 and i < 30: time.sleep(0.1) if i > 30 and i < 60: t = random.randint(2,6)/10 time.sleep(t) if i > 60 and i < 90: t = random.randint(30,41)/10 time.sleep(t) if i > 90 and i < 120: t = random.randint(2,6)/10 time.sleep(t)
令牌桶算法的python实现,人人都可以玩算法
最新推荐文章于 2024-04-28 13:59:21 发布