分布式环境下,锁定全局唯一资源
请求处理串行化
实际表现互斥锁
基于Redis分布式锁
唯一线程串行处理
实现方式
Setnx命令指定的Key不存在时,为Key设置指定的值
SETNX KEY_NAME VALUE Expire_time
设置成功,返回1,设置失败返回0
存在的问题
锁时间不可控,无法续租期
单点问题
单实例存在进程一旦死掉,会彻底阻塞业务流程
主从方式,主从数据异步,会存在锁失效的问题
官方建议
Redis本身建议使用Redlock算法保证,类似RAFT,等同自己实现简单的一致性问题,细节繁琐,且容易出错
redis分布式锁本质是AP模型,主从同步时半同步,可能出错。对高一致性要求性不高的情况下可以使用
|
redis |
zookeeper |
etcd |
一致性算法 |
无 |
paxos->ZAB |
raft |
CAP |
AP | CP |
CP/AP |
高可用 |
主从 |
N+1可用(奇数个,投票选举时) |
N+1可用 |
接口类型 |
客户端 |
客户端 |
http/grpc |
实现 |
setNX |
createEphemeral |
restful API |
redis无法保证数据一致性
Zookeeper 对锁实现使用创建临时节点和watch机制,执行效率,扩展能力,社区活跃度等低于etcd
etcd
简单KV
强一致
高可用
数据高可靠,持久化
分布式锁整体方案
分布式客户端+etcd
Client TTL模式
Client A->etcd->(key,ttl,value,uuid)
Client B->etcd->(key,ttl,value,uuid)
ClientA拿锁成功,ClientB拿锁失败
A服务需要对etcd保持后台心跳线程
比如key的租期为10ms,后台心跳线程为3ms,心跳线程负责在拿到key以后,没3ms cas唯一凭证uuid
业务申请资源锁,调用时提供key,ttl
etcd生成uuid,作为当前锁的唯一凭证,将key,uuid,ttl写入etcd
检查etcd中key是否存在,如没有尝试写入key
拿锁后,心跳线程启动,心跳线程维持时间为ttl/3,key续租 comapre and swap uuid
etcd API
申请锁
curl http://127.0.0.1:2379/v2/keys/foo XPUT -d value=bar ttl=5 prevExist=false
CAS更新锁续租
curl http://127.0.0.1:2379/v2/keys/foo?prevValue=prev_uuid XPUT -d value=bar ttl=5 refresh=true
CAS删除锁
curl http://127.0.0.1:2379/v2/keys/foo?prevValue=prev_uuid -XDELETE
如果调用方正常结束,通过cas接口调用delete方法自动清理etcd中的key值
如果调用方异常终止,等待原有锁ttl过期后,锁资源释放
etcd分V2,V3两个版本
V3文档不够完全,提供gRPC接口
天然提供分布式锁功能,只需要申请锁,释放锁,不需要关注锁的租期问题