漏斗限流
class Funnel(object): def __init__(self, capacity, leaking_rate): self.capacity = capacity # 漏斗容量 self.leaking_rate = leaking_rate # 流水速率 self.left_quota = capacity # 漏斗剩余空间 self.leaking_ts = time.time() # 上一次漏水时间 def make_space(self): now_ts = time.time() # 距离上一次漏水的时间 delta_ts = now_ts - self.leaking_ts # 可以腾出的空间 delta_quota = delta_ts * self.leaking_rate # 如果空间不足则下次再说 if delta_quota < 1: return self.left_quota += delta_quota # 增加剩余空间 self.leaking_ts = now_ts # 记录漏水时间 # 剩余空间不得高于容量 if self.left_quota > self.capacity: self.left_quota = self.capacity def watering(self, quota): self.make_space() # 判断剩余空间是否足够 if self.left_quota >= quota: self.left_quota -= quota return True return False
funnels = {} # 所有的漏斗
def is_action_allowed(user_id, action_key, capacity, leaking_rate): key = '%s:%s' % (user_id, action_key) funnel = funnels.get(key) if not funnel: funnel = Funnel(capacity, leaking_rate) funnels[key] = funnel return funnel.watering(1)
for i in range(20): print(is_action_allowed("tabll6", "replay", 15, 5)) # 容量 15, 流水速率 5/s
结果: True True True True True True True True True True True True True True True False False False False False
Redis-Cell
r.execute_command('CL.THROTTLE', "tabll6:get", 15, 10, 20, 1) # key / 15漏斗容量 / 10 option 10个操作 / 20 seconds 每20秒 / need 1 quota
结果: ------------------------------------------- ResponseErrorTraceback (most recent call last) <ipython-input-100-03ac17675581> in <module> ----> 1 r.execute_command('CL.THROTTLE', "tabll6:get", 15, 10, 20, 1) # key / 15漏斗容量 / 10 option 10个操作 / 20 seconds 每20秒 / need 1 quota ~/.local/lib/python3.8/site-packages/redis/client.py in execute_command(self, *args, **options) 899 try: 900 conn.send_command(*args) --> 901 return self.parse_response(conn, command_name, **options) 902 except (ConnectionError, TimeoutError) as e: 903 conn.disconnect() ~/.local/lib/python3.8/site-packages/redis/client.py in parse_response(self, connection, command_name, **options) 913 "Parses a response from the Redis server" 914 try: --> 915 response = connection.read_response() 916 except ResponseError: 917 if EMPTY_RESPONSE in options: ~/.local/lib/python3.8/site-packages/redis/connection.py in read_response(self) 754 755 if isinstance(response, ResponseError): --> 756 raise response 757 return response 758 ResponseError: unknown command `CL.THROTTLE`, with args beginning with: `tabll6:get`, `15`, `10`, `20`, `1`,
- (integer) 0 # 0 表示允许,1 表示拒绝
- (integer) 15 # 漏斗容量 capacity
- (integer) 14 # 漏斗剩余空间 left_quota
- (integer) -1 # 如果拒绝了,需要多长时间后再试(漏斗有空间了,单位秒)
- (integer) 2 # 多长时间后,漏斗完全空出来(left_quota==capacity,单位秒)
相关文档:https://github.com/brandur/redis-cell
GeoHash
r.geoadd('company', 116.48105, 39.996794, 'tabll')
结果:1
r.geoadd('company', 116.48205, 39.996894, 'tabll2') r.geoadd('company', 116.514203, 39.905409, 'tabll3') r.geoadd('company', 116.489033, 39.007669, 'tabll4') r.geoadd('company', 116.123456, 39.123456, 'tabll5')
结果:1
r.geodist('company', 'tabll', 'tabll3', 'km') # 计算两者之间的距离
结果:10.5501
r.geopos('company', 'tabll') # 获取经纬度(会有些许误差)
结果:[(116.4810499548912, 39.9967934885826)]
r.geohash('company', 'tabll') # 获取 base32 编码的 hash 值 # http://geohash.org/wx4gd94yjn0 网页可以访问位置
结果:['wx4gd94yjn0']
r.georadiusbymember('company', 'tabll', 20, 'km') # 20 KM 内
结果:['tabll3', 'tabll', 'tabll2']
r.georadiusbymember("company", "tabll", 20, unit="km", withdist=True) # 20 KM 内
结果:[['tabll3', 10.5501], ['tabll', 0.0], ['tabll2', 0.0858]]
r.georadius("company", 116.48205, 39.996894, 100, unit="km", withdist=True) # 找出指定位置 100 KM 范围内的数据
结果:[['tabll3', 10.5383], ['tabll', 0.0859], ['tabll2', 0.0002]]
georadiusbymember
函数参数:
georadiusbymember(self, name, member, radius, unit=None, withdist=False, withcoord=False, withhash=False, count=None, sort=None, store=None, store_dist=None)