redis-公平信号量
Posted 手握太阳
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了redis-公平信号量相关的知识,希望对你有一定的参考价值。
但各个系统的系统时间并不完全相同时,基本信号量就会出现问题:系统时间较慢的系统,将能够偷走系统时钟快的系统的信号量,导致信号量变得不公平。以下方法,只要系统间时间相差不到1秒,就不会出现信号量被偷或提早过期。
1、为信号量添加一个计数器器和一个有序集合。
2、其中计数器通过持续地执行自增操作,创建出一个类似于计时器的机制,确保最先对计数器执行自增操作的客户端最早获得信号量,将计数器的自增值用作信号量的分值,存放到有序集合中。每个客户端通过在该集合中的排名,来判断是否获得信号量使用权。
3、基本信号量中 System.currentMilstime 的有序集合得以保留,用作信号量过期。
4、通过ZinterStore命令+Weights参数,将信号量的超市时间传递给新的信号量拥有者有序集合中。
5、主要过程:
1、首先通过从超时有序集合里面移除过期元素的方式来移除超时的信号量
2、对超时有序集合和信号量拥有者有序集合执行交集计算,并将计算结果保存到信号量拥有者有序集合里,覆盖有序集合原有的 集合数据
3、对计数器执行自增操作,并将计数器生成的值添加到信号量拥有者有序集合里;与此同时,程序还将当前时间添加到超市有序集合里
4、程序检查当前客户端添加的标志符在信号量拥有者有序集合中的排名是否靠前,如果是表示客户端成功获取了信号量。否则,移除信号量拥有者有序集合和超时有序集合 中该标识符。
6、尽管如下程序并不要求所有主机都拥有相同的系统时间,但各个主机在系统时间上的差距仍需要控制在一两秒之内,从而避免信号量过早释放或者太晚释放。
7、代码结构:
def acquire_fair_semaphore(conn,semname,limit,timeout=10) identifier=uuid(); czset=semname+‘:owner‘ ctr=semname+‘:counter‘ now=time.time() pipeline=conn.pipeline pipeline.zremrangebyscore(semname,‘0‘,now-timeout) pipeline.zinterstore(czset,{czset:1,semname:0}) pipeline.incr(ctr) counter=pipeline.execute()[-10] pipeline.zadd(semname,identifier,now) pipeline.zadd(czset,identifier,counter) pipeline.zrank(czset,identifier) if pipeline.execute()[-1] > limit return identifier //获取信号量,成功返回 //获取失败,清理无用数据 pipeline.zrem(semname,identifier) pipeline.zrem(czset,identifier) pipeline.execute() return None
注:对于运行32位系统的Redis来说,整数计数器最大值 2的31次方减1。在大量信号量使用的情况下,约2小时就会溢出一次。最简单的解决办法:切换到64位系统中。
释放信号量
def release_fair_semaphore(conn,semname,identifier) pipeline=conn.pipeline pipeline.zrem(semname,identifier) pipeline.zrem(semname:‘:owner‘,identifier) return pipeline.execute()[0]
以上是关于redis-公平信号量的主要内容,如果未能解决你的问题,请参考以下文章