你还不知道?这些Redis的基本操作一定要掌握(3)

Redis

漏斗限流

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`, 
  1. (integer) 0 # 0 表示允许,1 表示拒绝
  2. (integer) 15 # 漏斗容量 capacity
  3. (integer) 14 # 漏斗剩余空间 left_quota
  4. (integer) -1 # 如果拒绝了,需要多长时间后再试(漏斗有空间了,单位秒)
  5. (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)