一个基于pull的gossip算法的实现。最终确保状态一致。
该协议如下:
1)A发起者发送Hello(B唯一标识,nonce)消息到B远程节点(多个)。
2)收Hello信息后发送SendDigest到A节点,其中包含nonce
3)A收到SendDigest,校验数据和nonce,把B作为待发送节点,并封装想要pull的数据SendReq到B节点
4)B收到SendReq发送SendRes到A节点,数据为SendReq不包含的数据
Other peer Initiator
O <——– Hello ————————- O
/|\ ——— Digest <[3,5,8, 10…], NONCE> ——–> /|\
| <——– Request <[3,8], NONCE> —————– |
/ \ ——— Response <[item3, item8], NONCE>——-> / \
一言不合就上源码
1. 这个是需要实现的接口。定义了上面所说的需要实现的4个接口
// PullAdapter is needed by the PullEngine in order to
// send messages to the remote PullEngine instances.
// The PullEngine expects to be invoked with
// OnHello, OnDigest, OnReq, OnRes when the respective message arrives
// from a remote PullEngine
type PullAdapter interface {
// SelectPeers returns a slice of peers which the engine will initiate the protocol with
SelectPeers() []string
// Hello sends a hello message to initiate the protocol
// and returns an NONCE that is expected to be returned
// in the digest message.
Hello(dest string, nonce uint64)
// SendDigest sends a digest to a remote PullEngine.
// The context parameter specifies the remote engine to send to.
SendDigest(digest []string, nonce uint64, context interface{
})
// SendReq sends an array of items to a certain remote PullEngine identified
// by a string
SendReq(dest string, items []string, nonce uint64)
// SendRes sends an array of items to a remote PullEngine identified by a context.
SendRes(items []string, context interface{
}, nonce uint64)
}
2. 这个是总调度类,初始化时需要传入第一步中的4个接口
stopFlag 结束标识如果为1时,就代表结束
state 一个数据集合
item2owners 需要pull的数据,key代表想要pull的数据,value代表那些节点有这些数据
peers2nonces 一个节点对应一个nonce,key节点唯一标识,value - nonce
nonces2peers 跟peers2nonces相反key,value
acceptingDigests 是否正在接收数据,Digest时锁住
acceptingResponses 是否正在接收数据,Resp时锁住
incomingNONCES 接收到的所有nonce
outgoingNONCES 发送出去的也就是自己生成的所有nonce
注意:processIncomingDigests就是随机挑选一个节点pull数据**
// NewPullEngine creates an instance of a PullEngine with a certain sleep time
// between pull initiations
func NewPullEngine(participant PullAdapter, sleepTime time.Duration) *PullEngine {
engine := &PullEngine{
PullAdapter: participant,
stopFlag: int32(0),
state: util.NewSet(),
item2owners: make(map[string][]string),
peers2nonces: make(map[string]uint64),
nonces2peers: make(map[uint64]string),
acceptingDigests: int32(0),
acceptingResponses: int32(0),
incomingNONCES: util.NewSet(),
outgoingNONCES: util.NewSet(),
}
go func() {
for !engine.toDie() {
time.Sleep(sleepTime)
if engine.toDie() {
return
}
engine.initiatePull()
}
}()
return engine
}
func (engine *PullEngine) initiatePull() {
engine.lock.Lock()
defer engine.lock.Unlock()
engine.accept