博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
etcd raft如何实现leadership transfer
阅读量:5078 次
发布时间:2019-06-12

本文共 3113 字,大约阅读时间需要 10 分钟。

leadership transfer可以把raft group中的leader身份转给其中一个follower。这个功能可以用来做负载均衡,比如可以把leader放在性能更好的机器或者离客户端更近的机器上。

对于一个大规模分布式系统来说,负载均衡非常重要。然而raft本身在选主方面必须要求新主包含所有的意境committed的log,从这点上看,在选主阶段,不能加入自定义的选主逻辑。而paxos协议不太一样,paxos对选主没有要求,任何一个成员都可以成为主,选主协议可以自己实现。paxos leader当选后,从其他成员把commit的log拉过来即可。所以为了这个feature,raft作者提出了一个方案作为raft的扩展。

大概原理就是保证transferee(transfer的目标follower)拥有和原leader有一样新的日志,期间需要停写,然后给transferee发送一个特殊的消息,让这个follower可以马上进行选主,而不用等到election timeout,正常情况下,这个follower的term最大,当选,原来的leader变为备。

还是一样看看etcd实现的raft library怎么做,省略无关代码

首先应用通过如下函数来启动leader transfer,其中lead是当前的leader,transferee是目标leader,在任意一个成员上调用即可。

func (n *node) TransferLeadership(ctx context.Context, lead, transferee uint64) {    select {    // manually set 'from' and 'to', so that leader can voluntarily transfers its leadership    case n.recvc <- pb.Message{Type: pb.MsgTransferLeader, From: transferee, To: lead}:    case <-n.done:    case <-ctx.Done():    }}

跑raft的goroutine从recvc中拿出message,首先做各种各样的检查,比如是否已经有transfer leader正在进行中,如果正在进行,目标是谁,然后做相应的处理。如果没有,则调用一下代码:

r.leadTransferee = leadTransfereeif pr.Match == r.raftLog.lastIndex() {            r.sendTimeoutNow(leadTransferee)            r.logger.Infof("%x sends MsgTimeoutNow to %x immediately as %x already has up-to-date log", r.id, leadTransferee, leadTransferee)} else {            r.sendAppend(leadTransferee)}

首先将目标leader保存在leadTransferee中,标示着有transfer正在进行,后续如果有请求propose进来,会检查:

if r.leadTransferee != None {            r.logger.Debugf("%x [term %d] transfer leadership to %x is in progress; dropping proposal", r.id, r.Term, r.leadTransferee)            return}

这里相当于停写。

回到上面:

  • 如果transferee和leader的log一样新,则给transferee发送MsgTimeoutNow类型的消息,告诉transferee可以立即选主,不需要等到election timeout。transferee端:
r.campaign(campaignTransfer)

raft为了防止出现网络分区的情况下,candidate频繁增加term从而导致term爆炸,在选主的时候新增加了一个PreVote阶段,通过了这个阶段才会真正开始Vote,这里,由于transferee明确知道是transfer,就没有必要采用这种两阶段的选主,所以传入的参数是campaignTransfer

  • 如果leader发现transferee的日志落后,则给transferee append日志,leader在收到响应MsgAppResp后,会检查:
// Transfer leadership is in progress.if m.From == r.leadTransferee && pr.Match == r.raftLog.lastIndex() {    r.logger.Infof("%x sent MsgTimeoutNow to %x after received MsgAppResp", r.id, m.From)    r.sendTimeoutNow(m.From)}

如果发现transferee已经日志最新,则同样,给transferee发送MsgTimeoutNow

最后,看看etcd如何调用:

func (s *EtcdServer) transferLeadership(ctx context.Context, lead, transferee uint64) error {    now := time.Now()    interval := time.Duration(s.Cfg.TickMs) * time.Millisecond    plog.Infof("%s starts leadership transfer from %s to %s", s.ID(), types.ID(lead), types.ID(transferee))    s.r.TransferLeadership(ctx, lead, transferee)    for s.Lead() != transferee {        select {        case <-ctx.Done(): // time out            return ErrTimeoutLeaderTransfer        case <-time.After(interval):        }    }    // TODO: drain all requests, or drop all messages to the old leader    plog.Infof("%s finished leadership transfer from %s to %s (took %v)", s.ID(), types.ID(lead), types.ID(transferee), time.Since(now))    return nil}

调用TransferLeadership后,每隔一段时间检查是否transfer成功,要么超时,直接返回。

转载于:https://www.cnblogs.com/foxmailed/p/7173137.html

你可能感兴趣的文章
一步步学习微软InfoPath2010和SP2010--第七章节--从SP列表和业务数据连接接收数据(4)--外部项目选取器和业务数据连接...
查看>>
oracle 报错ORA-12514: TNS:listener does not currently know of service requested in connec
查看>>
基于grunt构建的前端集成开发环境
查看>>
利用循环播放dataurl的视频来防止锁屏:NoSleep.js
查看>>
python3 生成器与迭代器
查看>>
java编写提升性能的代码
查看>>
Abstract Factory Pattern
查看>>
list 容器 排序函数.xml
查看>>
《Genesis-3D开源游戏引擎完整实例教程-跑酷游戏篇03:暂停游戏》
查看>>
CPU,寄存器,一缓二缓.... RAM ROM 外部存储器等简介
查看>>
windows下编译FreeSwitch
查看>>
git .gitignore 文件不起作用
查看>>
Alan Turing的纪录片观后感
查看>>
c#自定义控件中的事件处理
查看>>
django Models 常用的字段和参数
查看>>
IOS--沙盒机制
查看>>
使用 JointCode.Shuttle 访问任意 AppDomain 的服务
查看>>
sqlite的坑
查看>>
digitalocean --- How To Install Apache Tomcat 8 on Ubuntu 16.04
查看>>
【题解】[P4178 Tree]
查看>>