在etcd-raft模塊中,客戶端發往集羣中的寫請求是通過MsgProp消息表示的。Raft集羣中只有Leader節點能夠響應客戶端的寫入請求。
客戶端請求的寫操作,會通過調用Node接口的Propose函數請求到raft模塊中,當Follower接收到MsgProp類型的消息會轉發到Leader節點,所以MsgProp消息的主要處理是在raft.stepLeader()方法中實現。主要流程如下:
1.判斷MsgProp是否攜帶Entry記錄,如果未攜帶則輸出異常日誌並終止程序
2.檢測當前節點是否被移除集羣,如果當前節點以Leader狀態被移除集羣,則不再處理MsgProp消息
3.判斷當前是否正在進行Leader節點轉移,如果正在轉移則返回錯誤
4.遍歷MsgProp消息攜帶的全部Entry,如果pendingConfIndex大於已提交的位置,則該Entry的類型設置爲EntryNormal,否則更新pendingConfIndex值
5.將上述Entries記錄追加到當前節點的raftLog中
6.向集羣中的其他節點發送MspApp消息複製日誌
func stepLeader(r *raft, m pb.Message) error {
switch m.Type {
//其他消息省略.....
case pb.MsgProp:
if len(m.Entries) == 0 { //判斷MsgProp是否攜帶Entry記錄,如果未攜帶則輸出異常日誌並終止程序
r.logger.Panicf("%x stepped empty MsgProp", r.id)
}
if _, ok := r.prs[r.id]; !ok { //檢測當前節點是否被移除集羣,如果當前節點以Leader狀態被移除集羣,則不再處理MsgProp消息
// If we are not currently a member of the range (i.e. this node
// was removed from the configuration while serving as leader),
// drop any new proposals.
return ErrProposalDropped
}
if r.leadTransferee != None { //判斷當前是否正在進行Leader節點轉移
r.logger.Debugf("%x [term %d] transfer leadership to %x is in progress; dropping proposal", r.id, r.Term, r.leadTransferee)
return ErrProposalDropped
}
for i, e := range m.Entries { //遍歷MsgProp消息攜帶的全部Entry
if e.Type == pb.EntryConfChange {
if r.pendingConfIndex > r.raftLog.applied {
r.logger.Infof("propose conf %s ignored since pending unapplied configuration [index %d, applied %d]",
e.String(), r.pendingConfIndex, r.raftLog.applied)
m.Entries[i] = pb.Entry{Type: pb.EntryNormal}
} else {
r.pendingConfIndex = r.raftLog.lastIndex() + uint64(i) + 1
}
}
}
r.appendEntry(m.Entries...) //將上述Entry記錄追加到當前節點的raftLog中
r.bcastAppend() //向集羣中的其他節點發送MspApp消息複製日誌
return nil
}
}