zoukankan      html  css  js  c++  java
  • etcd raft如何实现leadership transfer

    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 = leadTransferee
    if 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成功,要么超时,直接返回。

  • 相关阅读:
    simhash算法:海量千万级的数据去重
    卸载pycharm再重新安装后,找不到第三方库
    一个完整的jmeter APP登录接口测试实例
    pycharm主题 变量颜色 自定义
    基于python xlsxwriter、xlrd 生成测试报告
    通过python xlsxwriter模块生成EXCEL柱状图、饼图
    jenkins环境搭建(Windows)
    'pip' 不是内部或外部命令,也不是可运行的程序 或批处理文件 — 处理办法
    python 模块学习——time模块
    Appium 使用android_uiautomator定位元素时报错: The requested resource could not be found, or a request was received using an HTTP method that is not supported by the mapped resource
  • 原文地址:https://www.cnblogs.com/foxmailed/p/7173137.html
Copyright © 2011-2022 走看看