ETCD分布式锁实现选主机制(Golang实现)
ETCD分布式锁实现选主机制(Golang)
为什么要写这篇文章
做架构的时候,涉及到系统的一个功能,有一个服务必须在指定的节点执行,并且需要有个节点来做任务分发,想了半天,那就搞个主节点做这事呗,所以就有了这篇文章的诞生,我把踩的坑和收获记录下来,方便未来查看和各位兄弟们参考。
选主机制是什么
举个例子,分布式系统内,好几台机器,总得分个三六九等,发号施令的时候总得有个带头大哥站出来,告诉其他小弟我们今天要干嘛干嘛之类的,这个大哥就是master节点,master节点一般都是做信息处理分发,或者重要服务运行之类的。所以,选主机制就是,选一个master出来,这个master可用,并且可以顺利发消息给其他小弟,其他小弟也认为你是master,就可以了。
ETCD的分布式锁是什么
首先认为一点,它是唯一的,全局的,一个key值
为什么一定要强调这个唯一和全局呢,因为分布式锁就是指定只能让一个客户端访问这个key值,其他的没法访问,这样才能保证它的唯一性。
再一个,认为分布式锁是一个限时的,会过期的的key值
你创建了一个key,要保证访问它的客户端时刻online,类似一个“心跳”的机制,如果持有锁的客户端崩溃了,那么key值在过期后会被删除,其他的客户端也可以继续抢key,继续接力,实现高可用。
选主机制怎么设计
其实主要的逻辑前面都说清楚了,我在这里叙述下我该怎么做。
我们假设有三个节点,node1,node2,node3
- 三个节点都去创建一个全局的唯一key /dev/lock
- 谁先创建成功谁就是master主节点
- 其他节点持续待命继续获取,主节点继续续租key值(key值会过期)
- 持有key的节点down机,key值过期被删,其他节点创key成功,继续接力。
ETCD分布式锁简单实现
看一下ETCD的golang代码,还是给出了如何去实现一个分布式锁,这个比较简单,我先写一个简单的Demo说下几个接口的功能
- 创建锁
kv = clientv3.NewKV(client)
txn = kv.Txn(context.TODO())
txn.If(clientv3.Compare(clientv3.CreateRevision("/dev/lock"),"=",0)).Then(
clientv3.OpPut("/dev/lock","占用",clientv3.WithLease(leaseId))).Else(clientv3.OpGet("/dev/lock"))
txnResponse,err = txn.Commit()
if err !=nil{
fmt.Println(err)
return
}
- 判断是否抢到锁
if txnResponse.Succeeded {
fmt.Println("抢到锁了")
} else {
fmt.Println("没抢到锁",txnResponse.Responses[0].GetResponseRange().Kvs[0].Value)
}
- 续租逻辑
for {
select {
case leaseKeepAliveResponse = <-leaseKeepAliveChan:
if leaseKeepAliveResponse != nil{
fmt.Println("续租成功,leaseID :",leaseKeepAliveResponse.ID)
}else {
fmt.Println("续租失败")
}
}
time.Sleep(time.Second*1)
}
我的实现逻辑
首先我的逻辑就是,大家一起抢,谁抢到谁就一直续,要是不续了就另外的老哥上,能者居之嘛!我上一下我的实现代码
package main
import (
"fmt"
"context"
"time"
//"reflect"
"go.etcd.io/etcd/clientv3"
)
var (
lease clientv3.Lease
ctx context.Context
cancelFunc context.CancelFunc
leaseId clientv3.LeaseID
leaseGrantResponse *clientv3.LeaseGrantResponse
leaseKeepAliveChan <-chan *clientv3.LeaseKeepAliveResponse
leaseKeepAliveResponse *clientv3.LeaseKeepAliveResponse
txn clientv3.Txn
txnResponse *clientv3.TxnResponse
kv clientv3.KV
)
type ETCD struct {
client *clientv3.Client
cfg clientv3.Config
err error
}
// 创建ETCD连接服务
func New(endpoints ...string) (*ETCD, error) {
cfg := clientv3.Config{
Endpoints: endpoints,
DialTimeout: time.Second * 5,
}
client, err := clientv3.New(cfg)
if err != nil {
fmt.Println("连接ETCD失败")
return nil, err
}
etcd := &ETCD{
cfg: cfg,
client: client,
}
fmt.Println("连接ETCD成功")
return etcd, nil
}
// 抢锁逻辑
func (etcd *ETCD) Newleases_lock(ip string) (error) {
lease := clientv3.NewLease(etcd.client)
leaseGrantResponse, err := lease.Grant(context.TODO(), 5)
if err != nil {
fmt.Println(err)
return err
}
leaseId := leaseGrantResponse.ID
ctx, cancelFunc := context.WithCancel(context.TODO())
defer cancelFunc()
defer lease.Revoke(context.TODO(), leaseId)
leaseKeepAliveChan, err := lease.KeepAlive(ctx, leaseId)
if err != nil {
fmt.Println(err)
return err
}
// 初始化锁
kv := clientv3.NewKV(etcd.client)
txn := kv.Txn(context.TODO())
txn.If(clientv3.Compare(clientv3.CreateRevision("/dev/lock"), "=", 0)).Then(
clientv3.OpPut("/dev/lock", ip, clientv3.WithLease(leaseId))).Else(
clientv3.OpGet("/dev/lock"))
txnResponse, err := txn.Commit()
if err != nil {
fmt.Println(err)
return err
}
// 判断是否抢锁成功
if txnResponse.Succeeded {
fmt.Println("抢到锁了")
fmt.Println("选定主节点", ip)
// 续租节点
for {
select {
case leaseKeepAliveResponse = <-leaseKeepAliveChan:
if leaseKeepAliveResponse != nil {
fmt.Println("续租成功,leaseID :", leaseKeepAliveResponse.ID)
} else {
fmt.Println("续租失败")
}
}
}
} else {
// 继续回头去抢,不停请求
fmt.Println("没抢到锁", txnResponse.Responses[0].GetResponseRange().Kvs[0].Value)
fmt.Println("继续抢")
time.Sleep(time.Second * 1)
}
return nil
}
func main(){
// 连接ETCD
etcd, err := New("xxxxxxxx:2379")
if err != nil {
fmt.Println(err)
}
// 设定无限循环
for {
etcd.Newleases_lock("node1")
}
}
总结
相关代码写入到github当中,其中的地址是
https://github.com/Alexanderklau/Go_poject/tree/master/Go-Etcd/lock_work
实现这个功能废了不少功夫,好久没写go了,自己太菜了,如果有老哥发现问题请联系我,好改正。
ETCD分布式锁实现选主机制(Golang实现)的更多相关文章
- 聊聊Zookeeper应用场景、架构设计、选主机制
Zookeeper作为一个分布式协调系统提供了一项基本服务:分布式锁服务,分布式锁是分布式协调技术实现的核心内容.像配置管理.任务分发.组服务.分布式消息队列.分布式通知/协调等,这些应用实际上都是基 ...
- etcd分布式锁及事务
前言 分布式锁是控制分布式系统之间同步访问共享资源的一种方式.在分布式系统中,常常需要协调他们的动作.如果不同的系统或是同一个系统的不同主机之间共享了一个或一组资源,那么访问这些资源的时候,往往需要互 ...
- kafka分区选主机制
Kafka Partition Leader选主机制 https://blog.csdn.net/qq_27384769/article/details/80115392 kafka leader选举 ...
- 分布式ID生成器及redis,etcd分布式锁
分布式id生成器 有时我们需要能够生成类似MySQL自增ID这样不断增大,同时又不会重复的id.以支持业务中的高并发场景.比较典型的,电商促销时,短时间内会有大量的订单涌入到系统,比如每秒10w+.明 ...
- Etcd 使用场景:通过分布式锁思路实现自动选主
分布式锁?选主? 分布式锁可以保证当有多台实例同时竞争一把锁时,只有一个人会成功,其他的都是失败.诸如共享资源修改.幂等.频控等场景都可以通过分布式锁来实现. 还有一种场景,也可以通过分布式锁来实现, ...
- golang基于etcd实现分布式锁(转)
下面描述使用 Etcd 实现分布式锁的业务流程,假设对某个共享资源设置的锁名为:/lock/mylock 步骤 1: 准备 客户端连接 Etcd,以 /lock/mylock 为前缀创建全局唯一的 k ...
- etcd实现分布式锁
转载自:etcd实现分布式锁 当并发的访问共享资源的时候,如果没有加锁的话,无法保证共享资源安全性和正确性.这个时候就需要用到锁 1.需要具备的特性 需要保证互斥访问(分布式环境需要保证不同节点.不同 ...
- zookeeper curator选主(Leader)
在分布式系统设计中,选主是一个常见的场景.选主是一个这样的过程,通过选主,主节点被选择出来控制其他节点或者是分配任务. 选主算法要满足的几个特征: 1)各个节点均衡的获得成为主节点的权利,一旦主节点被 ...
- Redis实现分布式锁(设计模式应用实战)
笔者看过网络上各种各样使用redis实现分布式锁的代码,要么错误,要么片段化,没有一个完整的例子,借这个周末给大家总结一下redis实现分布式锁的两种机制 自旋锁和排他锁 鉴于实现锁的方式不同,那么这 ...
随机推荐
- C/C++ 递归
递归 当一个函数调用它自己来定义时称它为递归函数.(什么叫它自己调用它自己呢?) 1.1.引出递归 从一个简单的问题考虑递归,求0,1,2, 3,4,5......n的和. 首先定义一个求和公式:su ...
- 在JSP中<%= >,<%! %>,<% %>所代表的含义
<%! %>:是jsp中的声明标签,通常声明全局变量,常量,方法等. <% %>:<% java代码 %>,其中可以包含局部变量,java语句等. <%= % ...
- 方法签名_spring aop_around
//注解签名 方法签名 Signature signature = pjp.getSignature(); MethodSignature methodSignature= (MethodSignat ...
- Red Hat Enterprise Linux查看系统版本命令
# arch 返回结果为i686表示32位系统,x86_64表示64位系统. # uname -a # uname -r # lsb_release -a # cat /proc/version # ...
- htm、html、shtml区别。(web服务器配置ssi)
转载源:http://www.divcss5.com/html/h59.shtml 首先htm.html.shtml都是静态网页的后缀,三者也可以说都是只是扩展名不同,其他一样,都是静态的网页.Htm ...
- 内存缓存的使用Demo
使用缓存的目的是为了使用的应用程序能更快速的响应用户输入,是程序高效的运行.有时候我们需要将远程web服务器获取的数据缓存起来,减少对同一个url多次请求. 内存缓存我们可以使用sdk中的NSURLC ...
- Hibernate:基于HQL实现数据查询
HQL: hibernate query language(hibernate特有的查询语言) hql是基于对象的查询语言,其语法与sql类似,但是他和sql的区别在于sql是面向表和字段的查询,而 ...
- Win10 + Ubuntu 安装教程(痛苦踩坑)
今天搞了一天,痛苦万分,本文的教程基本适用大部分情况,现在记录下需要主义的几点: 一.制作ubuntu usb安装盘的时候,格式要选saw的,千万不要用usb-HDD+的 二.安装完后使用EasyBC ...
- evpp tcpclient
重点函数讲解①:消息回调函数——void evpp::TCPClient::SetMessageCallback(const evpp::MessageCallback& cb) 注:设置消息 ...
- wepy相关
1.根据官方文档: $ npm install @wepy/cli -g # 全局安装 WePY CLI 工具 $ wepy init standard myproj # 使用 standard 模板 ...