raft 有关竞选超时 vs 心跳超时的疑惑

raft协议这两个超时时间是比较重要的,看着有些疑惑,

  1. 集群刚启动的时候,心跳会超时,这时候follower是直接成为candidator么?还是还要在心跳超时的基础上等待一个竞选超时才变成 candidator?
  2. 成为candidator并且发送投票请求给follower,这个candidator这时候本身等待投票是有个超时的,这个超时也是竞选超时么?
  3. 心跳超时和竞选超时哪个大?心跳超时一般设置为多少?
  4. 集群已经正常运转leader刚挂掉的时候,我们是依靠心跳超时还是竞选超时来触发选举流程?

 

1.心跳超时后,Follower 等待自身竞选超时后成为 Candidate 身份并发起选举。raft 使用了一个叫随机选举超时(randomize election timeout)的方式,使每个 server 的超时时间不一样,这样就避免了多个 Candidate 同时发起选举的问题。
2.选举也有时限,规定时间内没有获取到足够多的票数,则当前 Leader 选举竞选失败;但这个时限不是 election timeout ,election timeout 是指心跳超时后发起竞选的时限。
3.心跳超时可以自己配置,看你自己的网络规模和拓扑;竞选超时是随机的,大概在几十到几百毫秒;通常情况下,心跳超时比竞选超时长得多,心跳是秒级,竞选是毫秒级。
4.前面已经回答了,Follower 检测到心跳超时后,等待自身竞选超时后发起选举,也就是两个时限都超了。

补充:

  • kindol: 

    感谢大佬解答,第3点心跳超时会比竞选超时长,那如果一段时间内集群持续没有操作,不会有append这种指令,由于心跳超时比竞选超时长,竞选超时没来得及刷新,这时候集群不就很容易发生竞选超时了吗?这种竞选超时是否反而是一种无用功?

      回复  7月31日

  • kindol: 

    另外关于第一点,心跳超时后follower还会等待竞选超时,这里是说心跳超时后,再开始竞选超时的计数,还是心跳超时、竞选超时是follower启动后就同时开始计数的?

      回复  7月31日

  • Lyde_Su: 

    @kindol 竞选超时并不是一段不停运行的代码,它只是一个参数,在心跳超时后系统启动一个计时器与竞选超时参数比较,超过这个参数就发起选举。

      回复  7月31日

  • Lyde_Su: 

    @kindol 心跳超时好理解,两次心跳之间间隔的时间,超过时限就叫超时;竞选超时是心跳超时后才起算的。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是RAFT算法的伪代码,包含超时选举和节点同步: ``` // 初始化节点 NODE_ID = unique_id() CURRENT_TERM = 0 VOTED_FOR = null LOG = [] NEXT_INDEX = {for each server: 1} MATCH_INDEX = {for each server: 0} // 启动节点 function start(): // 启动定时器 start_election_timeout() // 进入循环,监听事件 while true: message = receive_message() if message.type == "append_entries": handle_append_entries(message) else if message.type == "request_vote": handle_request_vote(message) else if message.type == "response": handle_response(message) // 检测是否超时,如果超时则发起选举 if election_timeout(): start_election() // 处理附加日志请求 function handle_append_entries(message): if message.term < CURRENT_TERM: send_response(false) return CURRENT_TERM = message.term VOTED_FOR = null stop_election_timeout() // 检测前一条日志是否匹配 if LOG[message.prev_log_index].term != message.prev_log_term: send_response(false) return // 添加新日志 LOG = LOG[:message.prev_log_index + 1] + message.entries + LOG[message.prev_log_index + 1:] // 更新提交索引 if message.leader_commit > COMMIT_INDEX: COMMIT_INDEX = min(message.leader_commit, len(LOG) - 1) send_response(true) // 处理投票请求 function handle_request_vote(message): if message.term < CURRENT_TERM: send_response(false) return last_log_index = len(LOG) - 1 last_log_term = LOG[last_log_index].term if message.last_log_term < last_log_term or (message.last_log_term == last_log_term and message.last_log_index < last_log_index): send_response(false) return if VOTED_FOR is null or VOTED_FOR == message.candidate_id: CURRENT_TERM = message.term VOTED_FOR = message.candidate_id stop_election_timeout() send_response(true) else: send_response(false) // 处理响应 function handle_response(message): if message.success: NEXT_INDEX[message.server_id] = message.next_index MATCH_INDEX[message.server_id] = message.next_index - 1 // 如果已经提交,更新提交索引 for i in range(COMMIT_INDEX + 1, len(LOG)): if LOG[i].term == CURRENT_TERM and sum(1 for j in MATCH_INDEX.values() if j >= i) > len(MATCH_INDEX) / 2: COMMIT_INDEX = i else: NEXT_INDEX[message.server_id] = message.next_index // 开始选举 function start_election(): CURRENT_TERM += 1 VOTED_FOR = NODE_ID votes = 1 stop_election_timeout() // 发送投票请求 for server_id in all_servers(): if server_id != NODE_ID: send_request_vote(server_id) // 等待投票结果 while votes <= len(all_servers()) / 2: message = receive_message() if message.type == "request_vote_response" and message.term == CURRENT_TERM and message.vote_granted: votes += 1 elif message.type == "append_entries" and message.term >= CURRENT_TERM: start_election() return // 选举成功,成为领导者 become_leader() // 成为领导者 function become_leader(): // 初始化 next_index 和 match_index for server_id in all_servers(): NEXT_INDEX[server_id] = len(LOG) + 1 MATCH_INDEX[server_id] = 0 // 开始发送心跳 while true: for server_id in all_servers(): if server_id != NODE_ID: // 发送附加日志请求 prev_log_index = NEXT_INDEX[server_id] - 1 prev_log_term = LOG[prev_log_index].term entries = LOG[NEXT_INDEX[server_id]:] send_append_entries(server_id, prev_log_index, prev_log_term, entries, COMMIT_INDEX) // 等待响应 if some_response_received(): // 如果已经提交,更新提交索引 for i in range(COMMIT_INDEX + 1, len(LOG)): if LOG[i].term == CURRENT_TERM and sum(1 for j in MATCH_INDEX.values() if j >= i) > len(MATCH_INDEX) / 2: COMMIT_INDEX = i // 如果有节点落后了,发送更小的日志条目 for server_id in all_servers(): if server_id != NODE_ID and NEXT_INDEX[server_id] <= len(LOG): send_append_entries(server_id, NEXT_INDEX[server_id] - 1, LOG[NEXT_INDEX[server_id] - 1].term, LOG[NEXT_INDEX[server_id]:], COMMIT_INDEX) // 等待一段时间后再次发送心跳 sleep(HEARTBEAT_INTERVAL) // 发送附加日志请求 function send_append_entries(server_id, prev_log_index, prev_log_term, entries, leader_commit): // 构造请求消息 message = { "type": "append_entries", "term": CURRENT_TERM, "leader_id": NODE_ID, "prev_log_index": prev_log_index, "prev_log_term": prev_log_term, "entries": entries, "leader_commit": leader_commit } // 发送请求消息 send_message(server_id, message) // 发送投票请求 function send_request_vote(server_id): // 构造请求消息 message = { "type": "request_vote", "term": CURRENT_TERM, "candidate_id": NODE_ID, "last_log_index": len(LOG) - 1, "last_log_term": LOG[len(LOG) - 1].term } // 发送请求消息 send_message(server_id, message) // 发送响应 function send_response(success, server_id, next_index): // 构造响应消息 message = { "type": "response", "term": CURRENT_TERM, "success": success, "server_id": NODE_ID, "next_index": next_index } // 发送响应消息 send_message(server_id, message) ``` 这段代码实现了RAFT算法的关键步骤,包括超时选举和节点同步。其中,超时选举是通过定时器和投票请求实现的,而节点同步是通过附加日志请求和响应实现的。具体来说,当节点超时时,它会发送投票请求并等待响应,如果收到超过半数的投票,则成为领导者;领导者会周期性地发送附加日志请求来同步节点,并等待响应。如果有节点落后了,领导者会发送更小的日志条目来加速同步。最终,如果有足够多的节点接受了某个日志条目,领导者就可以将其提交。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值