使用redis实现流量控制时如何防止竞争?

时间:2013-11-01 17:59:48

标签: python redis redis-py

如果有太多用户同时登录(意味着间隔时间不到7秒),我们的服务器就会变得暴躁。一旦用户登录,就没有问题(同时登录一两个也不是问题,但是当10-20尝试整个服务器进入死亡螺旋叹息

我正在尝试编写一个可以保留用户的页面(显示动画倒计时等)并让它们相隔7秒钟。算法很简单

  1. 获取上次登录时的时间戳(t)
  2. 如果过去t+7启动登录并将now()存储为新时间戳
  3. 如果将来t+7,请将其存储为新时间戳,等待t+7,然后开始登录。
  4. 直接的python / redis实现将是:

    import time, redis
    SLOT_LENGTH = 7  # seconds
    
    now = time.time()
    
    r = redis.StrictRedis()
    
    # lines below contain race condition..
    last_start = float(r.get('FLOWCONTROL') or '0.0')  # 0.0 == time-before-time
    my_start = last_start + SLOT_LENGTH
    r.set('FLOWCONTROL', max(my_start, now))  
    
    wait_period = max(0, my_start - now)
    time.sleep(wait_period)
    
    # .. login
    

    这里的竞争条件很明显,许多进程可以同时处于my_start =行。如何使用redis解决这个问题?

    我已经尝试了redis-py pipeline功能,但当然在r.get()调用之前不能获得实际值......

1 个答案:

答案 0 :(得分:2)

我会记录答案,以防其他人发现这个......

r = redis.StrictRedis()
with r.pipeline() as p:
    while 1:
        try:
            p.watch('FLOWCONTROL')  # --> immediate mode
            last_slot = float(p.get('FLOWCONTROL') or '0.0')
            p.multi()  # --> back to buffered mode
            my_slot = last_slot + SLOT_LENGTH
            p.set('FLOWCONTROL', max(my_slot, now))
            p.execute()  # raises WatchError if anyone changed TCTR-FLOWCONTROL
            break  # break out of while loop
        except WatchError:
            pass  # someone else got there before us, retry.

比原来的三条线复杂一点......