聊聊Raft协议
source link: http://vearne.cc/archives/39494
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
版权声明 本站原创文章 由 萌叔 发表
转载请注明 萌叔 | http://vearne.cc
1. 参考资料
The original project authors have created new raft implementations now used in etcd and InfluxDB.
goraft/raftd
的作者参与了etcd
项目的实现,所以goraft/raftd
是有参考价值的 。另外goraft/raftd
的实现不完整,没有实现Log
擦除等功能,因此不能用于生产环境。
2. Node的简单介绍
2.1 node的三种状态(state)
图1 有限状态自动机
- Leader
- Candidate
- Follower
2.2 各种存储
2.2.1 Write-Ahead Log(WAL)
存储:文件
4f
raft:join">{"name":"2832bfa","connectionString":"http://localhost:4001"}
e
raft:nop 4f
raft:join">{"name":"3320b68","connectionString":"http://localhost:4002"}
4f
raft:join">{"name":"7bd5bdc","connectionString":"http://localhost:4003"}
e
raft:nop 29
write"{"key":"foo","value":"bar"}
29
write"{"key":"aaa","value":"bbb"}
29
write"{"key":"bbb","value":"ccc"}
29
write"{"key":"ddd","value":"eee"}
e
raft:nop e
raft:nop 29
write"{"key":"foo","value":"bar"}
29
write"{"key":"foo","value":"bar"}
2.2.2 状态信息(状态信息)
commitIndex、peer等
存储:内存&文件
type server struct {
*eventDispatcher
name string
path string
// Leader、Follower 或者 Candidate
state string
transporter Transporter
context interface{}
// 代表它所感知的全局的Term情况
currentTerm uint64
votedFor string
log *Log
// 代表它所感知的全局的leader情况
leader string
peers map[string]*Peer
}
type Log struct {
ApplyFunc func(*LogEntry, Command) (interface{}, error)
file *os.File
path string
entries []*LogEntry
commitIndex uint64
mutex sync.RWMutex
startIndex uint64 // the index before the first entry in the Log entries
startTerm uint64
initialized bool
}
goraft
的实现是写完Log.entries, 接着就写WAL
2.2.3 内存数据库
存储:内存
// The key-value database.
type DB struct {
data map[string]string
mutex sync.RWMutex
}
2.3 节点之间的通讯&重要的名词解释
Leader –> Follower
{
"Term": 17,
"PrevLogIndex": 26,
"PrevLogTerm": 17,
"CommitIndex": 26,
"LeaderName": "2832bfa",
"Entries": [{
"Index": 27,
"Term": 17,
"CommandName": "write",
"Command": "eyJrZXkiOiJhYWEiLCJ2YWx1ZSI6ImJiYiJ9Cg=="
}]
}
名词解释
Term
CommitIndex
LogEntry
3. Leader Election(选举过程)
注意:不需要集群中节点的数量是奇数
可以是4、8个,都没关系
3.1 什么时候开始选举?
3.2 投票相关–如何处理VoteRequest
看参考资料1.4 的演示动画
3.2.1 一个Term期间,最多只能投出1票
// VoteRequest中的Term,必须大于本地的currentTerm
if req.Term < s.Term() {
s.debugln("server.rv.deny.vote: cause stale term")
return newRequestVoteResponse(s.currentTerm, false), false
}
// If the term of the request peer is larger than this node, update the term
// If the term is equal and we've already voted for a different candidate then
// don't vote for this candidate.
// VoteRequest中的Term,如果大于本地的currentTerm,则更新本地的currentTerm
if req.Term > s.Term() {
s.updateCurrentTerm(req.Term, "")
} else if s.votedFor != "" && s.votedFor != req.CandidateName {
s.debugln("server.deny.vote: cause duplicate vote: ", req.CandidateName,
" already vote for ", s.votedFor)
return newRequestVoteResponse(s.currentTerm, false), false
}
3.2.2 获得投票需要满足的条件
raft协议中这样的要求
candidate’s log is at least as up-to-date as receiver’s log then vote
解释起来,就是必须同时满足以下2个条件,才会给candidate投票
- candidate.LastLogTerm >= receiver.LastLogTerm
- candidate.LastLogIndex >= receiver.LastLogIndex
注意: Log中有擦除情况出现,所以条件1是必须的
3.3 当选&当选后的一系列动作
- 获得
majority
投票的候选人当选为Leader
- 通过心跳压制其它
Candidate
, 迫使其它Candidate
转变为Follower
- 写入
NOPCommand
3.4 一种极端场景
- 注意1:
Node B
和Node D
每个节点收到2张选票,
所以这一轮投票没有选出Leader
。Node B
和Node D
的状态仍为candidate
。一段时间后(goraft/raftd
的实现为electionTimeout至2倍electionTimeout之间的随机值),他们会发起下一轮投票。详情见图1 - 注意2 每轮投票
Term
均需要加1
4. Log Replication(数据写入)
Step0. Client
发出WriteCommand
Step1. 先写Leader
的Log
Step2. 在通过AppendRequest,写Follower
的Log
Step3. 执行Leader
的Commit(写内存数据库)
Step4. 执行Follower
的Commit
Step5. 给Client
返回结果
- 注意1: 写入动作,只能由
Leader
来发起, 在goraft/raftd
的实现里,Follower
会直接拒绝执行WriteCommand
- 注意2: Step5 不需要等到Step4完全完成(收到Follower的response) 就可以开始执行。
只要client等到leader完成Commit动作。即使后续leader发生变更或部分节点崩溃,raft协议可以保证,client所提交的改动依然有效。
5. 数据读取&watch
5.1 默认consul有3种一致性模型
- default
- consistent
- stale
默认情况下,consul server(follower)不提供数据查询,仅转发请求给consul server(leader)
consistency
其中consistent模式是强一致性的,其它两种模式都不能保证强一致性。用stale模式可以提高吞吐能力,当然数据短时间内可能会有不一致问题
5.2 default
和consistent
模式的区别
5.3 如何使用stale模型
curl -v 'http://dev1:8500/v1/health/service/es?dc=dc1&passing=1&stale
5.4 watch是怎么回事?
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK