Consul 分布式锁实现
Contents
分布式锁简介
什么是分布式锁
- 在单机环境中,为了防止多进程/多线程/多协程同时访问共享资源造成数据破坏,需要第三方提供一个互斥能力,这个时候可以内核或者是程序运行时(例如
GoRuntime
)。 - 在分布式系统中,由于有多台机器,此时内核锁或程序运行时提供的锁无法保证多台机器在访问共享资源时互斥,此时就需要一个分布式锁服务,不同的机器通过该服务获取一把锁,获取到锁的机器就可以排他性的访问共享资源,这样的服务我们统称为分布式锁服务,锁也就叫分布式锁。
细粒度分布式锁
使用场景
适用于分布式锁频繁访问的场景,例如通过分布式锁来判断库存是否超卖。
实现
基于异步复制的分布式系统,例如 mysql
,redis
等。
粗粒度分布式锁
使用场景
适用于访问锁频率比较低的场景,例如选主,服务发现。
实现
基于 paxos
协议的分布式一致性系统,例如 chubby
, zookeeper
,etcd
,consul
等。
Consul 中分布是锁的实现
Consul 分布式锁概述
Consul
是基于raft
(类paxos
) 的分布式一致性系统,consul
提供session
机制来构建分布式锁,借鉴了chubby
的实现,提供粗粒度的分布式锁服务。
基本概念
session
-
构成
node
:表示session
对应的consul
节点,创建session
时可以指定节点,默认是创建session
的那个节点。health checks
:一系列的健康检查。consul
提供两种健康检查的方式,一种是健康检查脚本,另一种是HTTP API Interval Request
的方式。健康检查可以通过consul
的RESTful API
创建。创建session
的时候可以不设置健康检查,节点宕机持有的锁不会被释放,但是consul
提供了强制删除session
的操作来处理这种情况。TTL
:session
存活时间,可以通过不断地刷新TTL
来保持session
,这也是一种健康检查的机制,但是相比于consul
的gossip failure detector
来说会增加consul server
的压力。lock-delay
:获得到锁的session
失效后,其他的节点并不会立马就获得锁,而是会等待一个lock-delay
时间才能获取锁,这是为了防止获得锁的节点由于正在执行的任务耗时较长或节点负载高耗尽 TTL 使得 session 失效的情况下会造成数据不一致问题。behavior
:session
失效后对锁的处理行为,默认是release
,即session
失效后,其获得的锁会被释放,另一种是delete
, 即session
失效后,其获取的锁会被删除。
-
session
失效场景- 节点从
consul
中下线。 - 注册的健康检查脚本下线。
- 健康检查检查到不健康的状态。
session
被销毁。TTL
过期。
- 节点从
KV
-
aquire
是一钟类
CAS
操作,只有在没有sessin
持有锁的情况下才会成功(持有锁的session
可以继续获取)。成功的情况下key
对应的内容会更新,LockIndex
会递增,锁对应的session id
会更新,表示锁由这个session
持有。如果已经持有锁的session
继续获取锁可以成功,key
的内容会更新,但是LockIndex
不会增加。 -
release
也是类
CAS
操作,通过无效的session
取释放锁会失败,释放锁的session
不一定是持有锁的session
, 因为consul
提供了强制删除sesesson
机制,让 session 强制失效来释放或删除锁。锁被释放后,LockIndex
不会变,但是对应的session id
会清空,ModiyIndex
增加。 -
sequencer
(
key
,LockIndex
,session
) 构成一个唯一的sequencer
,sequencer
可以用来验证请求是否属于当前的锁持有者。因为每次acquire
都会导致LockIndex
递增,即使在已持有锁的session
中重新获取锁,也能根据LockIndex
判断出是不同的请求。同样,如果session
失效,相应的LockIndex
将为空。
实现
加锁过程
加锁流程如下:
释放锁的过程
释放锁的过程比较简单,首先会判断是否当前是否持有锁,如果没有持有锁,则直接返回。如果持有锁直接调用release
来释放锁。
使用
Consul
提供的分布式锁的api
十分友好,如果不设置一些参数的化,如果不想设置lock-delay-time
这些参数的化,基本就可以直接用他提供的lock
,unlock
方法,他会自动帮你建立session
并保持会话。
我这边对他的使用做了一个配置参数的简单封装。代码放到github
上了 distributed-lock。