实现拜占庭容错的Raft算法-日志分发1

核心问题

不可信环境下Follower不相信Leader持有的已达成共识日志CommitIndex, Leader不相信Follower已将分发的日志保存

在基本Raft中, Follower根据Leader发来的追加请求中, 要追加日志的前一条日志是否是自己持有的最新日志, 决定是否接受日志追加请求, BRaft中这个判断保持不变

在基本Raft中, Leader根据追加日志的反馈确认某条日志是否已经被半数以上Follower接受, 如果已经接收则更新自己的CommitIndex, 在下次发送追加请求时Follower根据Leader的CommitIndex将自己持有的日志应用到状态机

BRaft中Follower不能直接相信Leader的CommitIndex, 因此需要在Leader完成追加后发送一条AppendEntriesCommit广播, 告知其他节点某日志已被我添加记录到map, 当一条日志确实已被多数节点添加, 根据map记录推进CommitIndex的更新, 这对于Leader来说也一样, 因为不能相信Follower确实添加了日志, 也就不能直接更新CommitIndex

修改LogEntry结构体, 增加每条记录的签名

type LogEntry struct {
	Term         int         // the log entry's term
	CommandValid bool        // if it should be applied
	Command      interface{} // 操作日志 the command should be applied to the state machine
	Signature    []byte
}

在AppendEntries中,Follower要校验Leader发来的日志是否与签名一致, 如果发现不一致说明被Leader篡改, 立即重新发起选举, 目前代码只校验了一条, 本版代码做了多条日志确认的优化, 否则无法通过PartB测试

if !verifySignature(GetBytes(args.Entries[0].Command), args.Entries[0].Signature) {
		rf.becomeCandidateLocked()
		return
	}

Follower收到追加日志之后并不信任Leader确实已经持有了自己已提交的日志, 因此发起一轮确认广播, 在所有节点之间再次确定Leader发送的日志是正确的, 需要两个新的结构体

type AppendEntriesCommitArgs struct {
	Term       int
	PeerId     int    // 确认消息发送者Id
	Signature  []byte // PeerId 的数字签名,用于防止拜占庭节点伪造确认消息
	EntryIndex int
	EntryTerm  int
	EntryHash  string
}

type AppendEntriesCommitReply struct {
	Term    int
	Success bool
}

为了让Follower能够自行统计Committed日志的数量, 新增一个raft_map文件, 在Raft结构体里增加一个map, 用于各个节点自行统计被提交的日志

package raft

import (
	"fmt"
	"sort"
)

type AppendEntriesCommitKey struct {
	Term  int
	Index int
	Hash  string
}
type CommitKeySlice []AppendEntriesCommitKey

//Raft
m map[AppendEntriesCommitKey]int
//Make
rf.m = make(map[AppendEntriesCommitKey]int)

在AppendEntries中增加broadcastAppendEntriesCommit(rf, args.PrevLogIndex, args.PrevLogTerm)

func broadcastAppendEntriesCommit(rf *Raft, index int, term int) {

	for i := range rf.peers {
		if i != rf.me && rf.role != Candidate {
			args := AppendEntriesCommitArgs{Term: rf.currentTerm, PeerId: rf.me, EntryIndex: index, EntryTerm: term}
			reply := AppendEntriesCommitReply{}
			go func(server int) {
				rf.sendAppendEntriesCommit(server, args, &reply)
			}(i)
		}
	}
}

func (rf *Raft) sendAppendEntriesCommit(server int, args AppendEntriesCommitArgs, reply *AppendEntriesCommitReply) bool {
	ok := rf.peers[server].Call("Raft.AppendEntriesCommit", args, reply)
	return ok
}

func (rf *Raft) AppendEntriesCommit(args AppendEntriesCommitArgs, reply *AppendEntriesCommitReply) {

	rf.mu.Lock()
	defer rf.mu.Unlock()

	key := AppendEntriesCommitKey{Term: args.EntryTerm, Index: args.EntryIndex}
	_, ok := rf.m[key]
	if ok == true {
		rf.m[key] += 1
	} else {
		rf.m[key] = 1
	}

	if rf.m[key] >= len(rf.peers)/2 && key.Index > rf.commitIndex {
		rf.commitIndex = key.Index
	}
	reply.Success = true
}


目前完成日志添加的操作, 接下来需要完成Leader对共识记录的更新, 各节点对共识记录的应用, AppendEntriesCommit增加签名验证
增加了一轮广播后On^2复杂度, 性能大幅下降, PartB原本的一些测试会无法通过
在这里插入图片描述

  • 9
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值