cornerstone中msg类型解析
1.概述
cornerstone中msg主要为resp_msg,req_msg类型。其中涉及到了oop中代码复用与封装、继承等优秀的设计思想,值得解析。
2.msg_base代码分析
class msg_base
{
private:
ulong term_;
msg_type type_;
int32 src_;
int32 dst_;
}
- 首先信息从一方发送到另一方,所以需要
src_
,dst_
- 在raft背景下,每条信息还需记录任期
term_
,同时为了支持不同消息,有消息类型type_
3. req_msg代码分析
class req_msg : public msg_base
{
private:
ulong last_log_term_;
ulong last_log_idx_;
ulong commit_idx_;
std::vector<ptr<log_entry>> log_entries_;
}
- 为了复用代码,public继承msg_base,消息自带
src_
,dst_
,term_
,type_
- raft里面信息都是单向流动,只能从leader到follower。为了同步leader与follower的entry,需要leader的
last_log_term_
与last_log_idx_
以及ulong commit_idx_
。 - 具体leader的entry放在
log_entries_
里面,如果不是为了同步entry可以为空
4. resp_msg代码分析
class resp_msg : public msg_base
{
private:
ulong next_idx_;
bool accepted_;
}
这里next_idx_的存在在append-entry这一个rpc通信里面很重要
在append-entry里面,follower对leader的rpc请求有两种情况
- follower接受leader的entry
- follower不接受leader的entry
如果是accept的情况,那么next_idx_其实是多余的,
但是如果不是accept,那么next_idx_就发挥作用了。
在raft论文里面,一个leader对每个follower都记录next_idx与match_idx。
next_idx:leader对follower应该match的idx的猜测(初始化为leader的log_store的最后idx + 1)
match_idx:leader与follower的log_store重合(即match)的idx的最大值,为实际值
举例来说:
leader:[1,2,3,4]
follower A:[1,2,3,7]
follower B:[1,3]
那么A的match_idx为2, B的match_idx为0
刚开始leader对A,B的对match_idx的猜测值next_idx都为4,即为leader的log_store的最后idx + 1。
在raft论文里给出调整next_idx的方法为next_idx每次减1,但是显然这是可以优化的。
因为在append-entry rpc中通过follower的resp,leader可以得知每个follower的log_store情况,不需要每次减1,可以一步到位。
比如对B来说,leader可以将B的next_idx缩减成B的log_store的最后idx + 1 = 2再继续逼近。
(对于A来说还是得每次减1,因为A的log_store中entry数目跟leader一样多。换句话说这种优化只针对follower的entry数目小于leader的情况。)
综上所述,在resp里面我们还需要next_idx_来帮助leader来纠正next_idx,使其更快逼近match_idx。
5.总结
- 1.善于设计base类,利用类的继承实现base类的拓展。
- 2.在rpc通信中,学会利用resp来传递follower的消息实现优化。