etcd作为一个分布式的键值存储,其中leader选举是非常重要的一个功能。在etcd中,可以使用租约机制来实现leader选举。
以下是基本的流程:
- 创建租约
leaseResp, err := cli.Grant(context.Background(), 10) // 租约10秒
if err != nil {
log.Fatal(err)
}
- 将键和租约绑定
_, err = cli.Put(context.Background(), "key", "value", clientv3.WithLease(leaseResp.ID))
if err != nil {
log.Fatal(err)
}
- 监听租约变化
keepAliveChan, err := cli.KeepAlive(context.Background(), leaseResp.ID)
if err != nil {
log.Fatal(err)
}
for {
select {
case <- keepAliveChan:
// 租约续期成功,不需要做任何处理
case <-time.After(time.Second * 15):
// 没有收到续期成功通知,说明失去了leader地位,重新选举
log.Println("Lost leadership")
goto elect // 跳转到leader选举步骤
}
}
- 实现leader选举
elect:
electionKey := "my-election"
// 创建一个竞争用的键值对,并将其与上面创建的租赁ID关联。
kv := clientv3.NewKV(cli)
_, err = kv.Put(context.Background(), electionKey, "", clientv3.WithLease(leaseResp.ID))
if err != nil {
log.Fatalf("election: failed to create key: %v", err)
}
// 首先,获取当前最小的键值对。
getResp, err := kv.Get(context.Background(), electionKey, clientv3.WithPrefix())
if err != nil {
log.Fatalf("election: failed to get key(s): %v", err)
}
minID := getMinID(getResp.Kvs)
// 接下来,创建一个Watch客户端,并监视前面获取到的最小键值对的变化。
watcher := clientv3.NewWatcher(cli)
watchChan := watcher.Watch(context.Background(), electionKey, clientv3.WithRev(minID), clientv3.WithPrefix())
for {
select {
case event := <-watchChan:
for _, ev := range event.Events {
switch ev.Type {
case mvccpb.DELETE:
// 如果最小的键被删除,则重新计算最小键并再次加入竞争。
if string(ev.Kv.Key) == electionKey+"/"+string(minID) {
log.Println("re-electing")
goto elect
}
}
}
case <-time.After(time.Second * 15):
// 没有收到续期成功通知,说明失去了leader地位,重新选举
log.Println("Lost leadership")
goto elect // 跳转到leader选举步骤
}
}
在以上示例中,我们通过租约机制来实现了leader选举。具体流程是,在启动应用程序时为自己分配一个租约,并将其与指定的键关联。当其他节点试图更新该键时(例如删除),可以使用监视器来检测到。如果检测到键已被删除,则重新进行选举。
注意,在实际使用中,需要考虑更多的情况,例如网络分区、节点崩溃等。




