之前我们使用的定时任务都是只部署在了单台机器上,为了解决单点的问题,为了保证一个任务,只被一台机器履行,就需要斟酌锁的问题,因而就花时间研究了这个问题。到底怎样实现一个散布式锁呢?
锁的本质就是互斥,保证任什么时候候能有一个客户端持有同一个锁,如果斟酌使用redis来实现一个散布式锁,最简单的方案就是在实例里面创建一个键值,释放锁的时候,将键值删除。但是一个可靠完善的散布式锁需要斟酌的细节比较多,我们就来看看怎么写一个正确的散布式锁。
单机版散布式锁 SETNX
所以我们直接基于 redis 的 setNX (SET if Not eXists)命令,实现一个简单的锁。直接上伪码
锁的获得:
SET resource_name my_random_value NX PX 30000
锁的释放:
if redis.call(“get”,KEYS[1]) == ARGV[1] then
return redis.call(“del”,KEYS[1])
else
return 0
end
几个细节需要注意:
首先在获得锁的时候我们需要设置设置超时时间。设置超时时间是为了,避免客户端崩溃,或网络出现问题以后锁一直被持有。真个系统就死锁了。
使用 setNX 命令,保证查询和写入两个步骤是原子的
在锁释放的时候我们判断了KEYS[1]) == ARGV[1],在这里 KEYS[1]是从redis里面取出来的value,ARGV[1]是上文生成的my_random_value。之所以进行以上的判断,是为了保证锁被锁的持有者释放。我们假定不进行这一步校验:
- 客户端A获得锁,后发线程挂起了。时间大于锁的过期时间。
- 锁过期后,客户端B获得锁。
- 客户端A恢复以后,处理完相关事件,向redis发起 del命令。锁被释放
- 客户端C获得锁。这个时候一个系统中同时两个客户端持有锁。
造成这个问题的关键,在于客户端B持有的锁,被客户端A释放了。
锁的释放一定要使用lua脚本,保证操作的原子性。锁的释放包括了get,判断,del三个步骤。如果不能保证三个步骤的原子性,散布式锁就会有并提问题。
注意了以上细节,一个单redis节点的散布式锁就达成了。
在这个散布式锁中或者存在一个单点的redis。或许你会说,Redis是 master-slave的架构,产生故障的时候切换到slave就好,但是Redis的复制是异步的。
- 如果在客户端A在master上拿到了锁。
- 在master将数据同步到slave上之前,master宕机。
- 客户端B就从slave上又一次拿到了锁。
这样由于Master的宕机,造成了同时多人持有锁。如果你的系统可用接受短时时间内,有多人持有锁。这个简单的方案就可以解决问题。
但是如果解决这个问题。Redis的官方提供了一个Redlock的解决方案。
RedLock 的实现
为了解决,Redis单点的问题。Redis的作者提出了RedLock的解决方案。方案非常的奇妙和简洁。
RedLock的核心思想就是,同时使用多个Redis Master来冗余,且这些节点都是完全的独立的,也不需要对这些节点之间的数据进行同步。
假定我们有N个Redis节点,N应当是一个大于2的奇数。RedLock的实现步骤:
- 获得当前时间
- 使用上文提到的方法顺次获得N个节点的Redis锁。
- 如果获得到的锁的数量大于 (N/2+1)个,且获得的时间小于锁的有效时间(lock validity time)就认为获得到了一个有效的锁。锁自动释放时间就是最初的锁释放时间减去之前获得锁所消耗的时间。
- 如果获得锁的数量小于 (N/2+1),或在锁的有效时间(lock validity time)内没有获得到足够的说,就认为获得锁失败。这个时候需要向所有节点发送释放锁的消息。
对释放锁的实现就很简单了。想所有的Redis节点发起释放的操作,不管之前会不会获得锁成功。
同时需要注意几个细节:
重试获得锁的间隔时间应当是一个随机范围而非一个固定时间。这样可以避免,多客户端同时一起向Redis集群发送获得锁的操作,避免同时竞争。同时获得相同数量锁的情况。(虽然几率很低)
如果某master节点故障以后,回复的时间间隔应当大于锁的有效时间。
- 假定有A,B,C三个Redis节点。
- 客户端foo获得到了A、B两个锁。
- 这个时候B宕机,所有内存的数据丢失。
- B节点回复。
- 这个时候客户端bar重新获得锁,获得到B,C两个节点。
- 此时又有两个客户端获得到锁了。
所以如果恢复的时间将大于锁的有效时间,就能够避免以上情况产生。同时如果性能要求不高,乃至可以开启Redis的持久化选项。
总结
了解了Redis散布式的实现以后,其实觉得大多数的散布式系统其实原理很简单,但是为了保证散布式系统的可靠性需要注意很多的细节,琐碎异常。
RedLock算法实现的散布式锁就是简单高效,思路相当奇妙。
但是RedLock就一定安全么?我还会写一篇文章来讨论这个问题。敬请大家期待。
本篇文章到此结束,如果您有相关技术方面疑问可以联系我们技术人员远程解决,感谢大家支持本站!
本文来源:https://www.yuntue.com/post/234269.html | 云服务器网,转载请注明出处!