cornerstone中msg型別解析

TomGeller發表於2024-11-20

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的訊息實現最佳化。

相關文章