Consul分布式锁原理详解及实例
Posted 魏小言
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Consul分布式锁原理详解及实例相关的知识,希望对你有一定的参考价值。
Consul分布式锁原理详解及实例
现在大型系统为了保证服务高可用,都采用分布式架构提供服务。Consul是一款较zookeepr、etcd等年轻的纯Goland分布式服务架构。其包含多个功能模块服务发现、检查健康、K/V存储、多数据中心等,这里就其分布式锁进行介绍。
Consul分布式锁实现和其K/V存储的特性密切相关。K/V存储在业务中最常见的之一是Redis「基于K/V设计的存储、中间件等等很多,相对Redis是业务开发中接触更普遍的其中一个」,其SetNx可以出色的完成分布式锁的功能。Consul与Redis机制不同,Consul依赖K/V与Session的绑定关系,进而完成互斥锁定。
Session是什么呢/?在Consul中,session的概念和浏览器的session类似,标识本次操作owner,类似句柄。与Node/K-V关联,上游是一Node,下游多个K-V,相关系列函数包含renew、acquire、destroy、create…。
「源码:github.com/hashicorp/consul/api@v1.4.0/session.go」
K-V就是常规键值对,在Consul中,通过Api进行Get-Set。
「源码:github.com/hashicorp/consul/api@v1.4.0/kv.go」
Consul实现分布式锁,准确来讲,和K/V、Session各自功能没什么关联,主要应用的是两者之间的关系。
简单来说,根据Key绑定的Session判断是否可抢占。流程见下图:
其详细源码见:github.com/hashicorp/consul/api@v1.4.0/lock.go
其中含大量参数细节,着重注意:
// Lock 分布式锁数据结构
type Lock struct {
c *Client // 提供访问consul的API客户端
opts *LockOptions // 分布式锁的可选项
isHeld bool // 该锁当前是否已经被持有
sessionRenew chan struct{} // 通知锁持有者需要更新session
locksession string // 锁持有者的session
l sync.Mutex // 锁变量的互斥锁
}
// LockOptions 提供分布式锁的可选项参数
type LockOptions struct {
Key string // 锁的 Key,必填项,且必须有 KV 的写权限
Value []byte // 锁的内容,以下皆为选填项
Session string // 锁的session,用于判断锁是否被创建
SessionOpt *SessionEntry // 自定义创建session条目,用于创建session,避免惊群
SessionName string // 自定义锁的session名称,默认为 "Consul API Lock"
SessionTTL string // 自定义锁的TTL时间,默认为 "15s"
MonitorRetries int // 自定义监控的重试次数,避免脑裂问题
MonitorRetryTime time.Duration // 自定义监控的重试时长,避免脑裂问题
LockWaitTime time.Duration // 自定义锁的等待市场,避免死锁问题
LockTryOnce bool // 是否只重试一次,默认为false,则为无限重试
}
注意:lock delay需要注意一下。 在使用etcd和redis
redlock实现分布式锁的时候,一个节点释放锁,另一个节点可以立马拿到锁, 就算有延迟也只是网络上的调用开销。但consul的lock
delay策略不是这样的,一个节点释放锁了,另一个节点不能立马拿到锁。需要等待lock delay时间后才可以拿到锁「脑裂现象」。
Q&A
1、Consul分布式锁最终会落地以Api形式到Consul,那么Consul如何保证两个Http请求的原子性/?
Consul分布式强一致性算法Raft,及满足CAP的弱一致性方案Gossip协议。
2、Consul分布式强一致性与Session提供的分布式锁关系/? Session分布式锁基于Consul强一致特性
「要正确得实现了Paxos和Raft算法都不是一件特别容易的事,尤其是Paxos,Raft还好些。但如果想每一个服务都嵌入一个Paxos或者Raft,这对于维护将是一场灾难。Google的做法是使用Chubby作为一致性的基础服务提供者,在这之上提供分布式锁和Leader选举的服务(是不是有点PaaS的味道)。」
下附:
1、文档:
https://learn.hashicorp.com/tutorials/consul/application-leader-elections#overview
https://learn.hashicorp.com/tutorials/consul/application-leader-elections
2、编码demo
func DynamicCollectLog(arguments docopt.Opts) {
lockCollect, err := lock.ConsulLock()
if err != nil {
log.Infof("DynamicCollectLog ConsulLock Lock fail")
return
}
defer lockCollect.Unlock()
_ = CmdCollect(arguments)
sigCh := make(chan os.Signal, 1)
signal.Notify(sigCh, syscall.SIGTERM, syscall.SIGINT)
for {
select {
case sig, _ := <-sigCh:
fmt.Printf("DynamicCollectLog ConsulLock(%s), Unlock...\\n", sig)
log.Errorf("DynamicCollectLog ConsulLock(%s), Unlock...", sig)
err = lockCollect.Unlock()
if err != nil {
log.Infof("DynamicCollectLog ConsulUnlock fail")
}else{
log.Info("DynamicCollectLog ConsulLock Unlocked")
}
}
}
}
func ConsulLock() (lock *api.Lock, err error) {
client, err := api.NewClient(&api.Config{})
if err != nil {
log.Infof("ConsulLock failed to created client %v", err)
return nil, err
}
lockKey := "FlowAnalysis_Collect_Consul_Lock_key"
lock, err = client.LockOpts(&api.LockOptions{
Key: lockKey,
Value: []byte("sender 1"),
SessionTTL: "10s",
})
if err != nil {
log.Infof("ConsulLock failed to created lock %v", err)
return nil, err
}
_, err = lock.Lock(nil)
if err != nil {
log.Infof("ConsulLock failed to accquired lock")
return nil, err
}
return lock, nil
}
以上是关于Consul分布式锁原理详解及实例的主要内容,如果未能解决你的问题,请参考以下文章