深入理解分布式系统:basic paxos
预备知识:paxos是最广泛使用的一种非拜占庭容错的分布式共识算法, 每个节点可能提出proposal, paxos使用提案来推动整体, 提案包括编号和值。
各个结点传递消息不断提出提案,最终整个系统接受同一提案, 即达成共识。在paxos种,集群超半数节点同意该提案,则此提案被chosen, 此提案的值作为后续提案值。
几种角色:1. 客户端:向系统发送请求并等待响应, write或者read
2. proposer:收到客户端请求,提出相关提案
3. voters(acceptors):接受或者拒绝提案
4. learner:不参与投票,客户端请求得到接受者同意,学习者得到提案值
1.实现流程:
basic paxos包含两个阶段,每个阶段包含两个部分(a和b), 对应两轮RPC消息传递, 每个阶段的a和b部分对应RPC的请求阶段和响应阶段。
1. 第一阶段:
prepare阶段(请求阶段)phase 1A:proposer收到客户端请求,选择最新的提案编号n, 向超过半数的voters广播,请求投票。
promise阶段 phase 1B: voters收到消息后判断:消息中的编号n大于之前接受的所有编号, 返回promise消息进行响应, 并且承诺不 接受任何小于n的提案。如果voters之前接受了提案,需要返回上一次的编号和值。 如果小于之前的编号,返回fail就可。
持久性问题:为了故障恢复, 存储最大编号,已接受编号, 已接受提案值。
2.第二阶段:
phase 2A:提议者收到过半数接受者promise响应, 向多数派的voters发起accept请求, 带上提案编号和值。
phase 2B:acceptors收到accept请求, 如果没接收到大于n的提案, 则接受,保存提案。
角色结构体定义:
type Proposer struct {
//服务器id
id int
//提议者已知的最大轮次
round int
//提案编号
number int
//接受者id列表
acceptors []int
}
type Acceptors struct {
lis net.listener //启动RPC和端口
id int //服务器id
minProposal int
acceptedNumber int
acceptedValue interface{}
learners []int
}
type Learners struct {
lis net.listener
id int
acceptedMsg map[int]MsgArgs
}
定义消息:
type MsgArgs struct {
Number int
Value interface{}
From int
To int
}
type MsgReply struct {
Ok bool
Number int
Value interface{}
}
func call (src string, name string , args interface{}, reply interface{}) bool {
c, err := rpc.Dial("tcp", srv)
if err != nil {
return false
}
defer c.Close()
err = c.Call(name, args, reply)
if err == nil {
return true
}
return false
}
到这儿我们的准备工作基本就绪,下篇文章会具体实现算法逻辑,包括各个角色的行为。