关于gossip pull机制的相关操作
/*
PullEngine是一个执行pull的gossip对象; 维护一个内部状态关于item 通过字符串编号来识别;
协议如下
1、发送者发送一个携带特殊NONCE的Hello消息给其他远程对等节点peer
2、每个远程对等节点peer响应消息的摘要及对应的NONCE
3、发送者检验接收的NONCE的有效性,聚集这些摘要;创建一个request包含想从指定每个远程对等节点peer item的id 并发送每个request到对应的对等节点peer
4、若是每个对等点保存了item request和NONCE,那对应的对等节点peer将返回包含请求项的响应
远程对等节点peer 发送者
O <-------- Hello ------------------------- O (hello请求)
/|\ --------- Digest <[3,5,8, 10...], NONCE> --------> /|\ (摘要响应)
| <-------- Request <[3,8], NONCE> ----------------- | (验证摘要请求)
/ \ --------- Response <[item3, item8], NONCE>-------> / \ (请求响应)
*/
const (
defDigestWaitTime = time.Duration(1000) * time.Millisecond // 摘要等待周期
defRequestWaitTime = time.Duration(1500) * time.Millisecond // 请求等待周期
defResponseWaitTime = time.Duration(2000) * time.Millisecond // 响应等待周期
)
// 设置摘要等待时间
// SetDigestWaitTime sets the digest wait time
func SetDigestWaitTime(time time.Duration) {
viper.Set("peer.gossip.digestWaitTime", time)
}
// 设置请求等待时间
// SetRequestWaitTime sets the request wait time
func SetRequestWaitTime(time time.Duration) {
viper.Set("peer.gossip.requestWaitTime", time)
}
// 设置响应等待时间
// SetResponseWaitTime sets the response wait time
func SetResponseWaitTime(time time.Duration) {
viper.Set("peer.gossip.responseWaitTime", time)
}
// DigestFilter 摘要过滤策略器
// 根据消息的上下文将发送hello或请求的远程对等点,将应用摘要过滤策略器
// DigestFilter filters digests to be sent to a remote peer that
// sent a hello or a request, based on its messages's context
type DigestFilter func(context interface{}) func(digestItem string) bool
// PullAdapter: PullEngine的适配器接口
// 为了向远程的PullEngine实例发送消息,PullEngine是需要的
// 当相应的消息从远程对等节点peer达到时,PullEngine将按照预期调用OnHello、OnDigest、OnReq、OnRes
// 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 {
// pullEngine启动协议时需要的对等节点列表peers
// SelectPeers returns a slice of peers which the engine will initiate the protocol with
SelectPeers() []string
// 启动协议时发送hello消息并返回一个预期摘要信息以及NONCE
// 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)
// 发送一个摘要消息给远程的PullEngine,其中context参数指定目的PullEngine
// 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{})
// 发送item数组到dest指定远程PullEngine(request消息)
// SendReq sends an array of items to a certain remote PullEngine identified
// by a string
SendReq(dest string, items []string, nonce uint64)
// 发送item数组到context指定远程PullEngine(response消息)
// SendRes sends an array of items to a remote PullEngine identified by a context.
SendRes(items []string, context interface{}, nonce uint64)
}
// PullEnine是个在PullAdapter帮助下实际调用pull算法的组件
// PullEngine is the component that actually invokes the pull algorithm
// with the help of the PullAdapter
type PullEngine struct {
PullAdapter
stopFlag int32 // 是否可用
state *util.Set // 状态集
item2owners map[string][]string // items所有者集
peers2nonces map[string]uint64 // 对等节点peer的nonces集(peer:nonce)
nonces2peers map[uint64]string // (nonce:peer)
acceptingDigests int32 // 接收的摘要
acceptingResponses int32 // 接收的响应
lock sync.Mutex
outgoingNONCES *util.Set // 发出的nonces
incomingNONCES *util.Set // 接收的nonces
digFilter DigestFilter // 摘要过滤策略
digestWaitTime time.Duration // 摘要等待周期
requestWaitTime time.Duration // 请求等待周期
responseWaitTime time.Duration // 响应等待周期
}
// 创建一个带有确定休眠时间的PullEngine实例在pull启动时,当发送摘要和响应消息时能使用给定的过滤策略
// NewPullEngineWithFilter creates an instance of a PullEngine with a certain sleep time
// between pull initiations, and uses the given filters when sending digests and responses
func NewPullEngineWithFilter(participant PullAdapter, // Pull适配器
sleepTime time.Duration, // 休眠时长
df DigestFilter) *PullEngine { // 摘要过滤器
engine := &PullEngine{
PullAdapter: participant, // pull参与者
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(),
digFilter: df,
digestWaitTime: util.GetDurationOrDefault("peer.gossip.digestWaitTime", defDigestWaitTime),
requestWaitTime: util.GetDurationOrDefault("peer.gossip.requestWaitTime", defRequestWaitTime),
responseWaitTime: util.GetDurationOrDefault("peer.gossip.responseWaitTime", defResponseWaitTime),
}
go func() { // 开启goroutine执行pullEngine初始化
for !engine.toDie() { // pullEngine可用
time.Sleep(sleepTime) // 双重检查pullEngine是否可用
if engine.toDie() {
return
}
engine.initiatePull() // 初始化pullEngine
}
}()
return engine
}
// 新建PullEngine实例并指定初始化的休眠时长
// NewPullEngine creates an instance of a PullEngine with a certain sleep time
// between pull initiations
func NewPullEngine(participant PullAdapter, // Pull适配器
sleepTime time.Duration) *PullEngine { // 休眠时长
acceptAllFilter := func(_ interface{}) func(string) bool { // 过滤策略:接收所有的内容
return func(_ string) bool {
return true
}
}
return NewPullEngineWithFilter(participant, sleepTime, acceptAllFilter)
}
// PullEngine是否可用
func (engine *PullEngine) toDie() bool {
return atomic.LoadInt32(&(engine.stopFlag)) == int32(1)
}
// 接收响应内容
func (engine *PullEngine) acceptResponses() {
atomic.StoreInt32(&(engine.acceptingResponses), int32(1))
}
// 是否为响应内容
func (engine *PullEngine) isAcceptingResponses() bool {
return atomic.LoadInt32(&(engine.acceptingResponses)) == int32(1)
}
// 接收摘要
func (engine *PullEngine) acceptDigests() {
atomic.StoreInt32(&(engine.acceptingDigests), int32(1))
}
// 是否为摘要
func (engine *PullEngine) isAcceptingDigests() bool {
return atomic.LoadInt32(&(engine.acceptingDigests)) == int32(1)
}
// 忽略摘要
func (engine *PullEngine) ignoreDigests() {
atomic.StoreInt32(&(engine.acceptingDigests), int32(0))
}
// 停止PullEngine
// Stop stops the engine
func (engine *PullEngine) Stop() {
atomic.StoreInt32(&(engine.stopFlag), int32(1))
}
// 初始化PullEngine
// 发送hello消息 并完成返回摘要处理
func (engine *PullEngine) initiatePull() {
engine.lock.Lock()
defer engine.lock.Unlock()
// 接收摘要
engine.acceptDigests()
for _, peer := range engine.SelectPeers() { // 遍历远程对等节点peer
nonce := engine.newNONCE() // 新建NONCE
engine.outgoingNONCES.Add(nonce) // 对外输出NONCE
engine.nonces2peers[nonce] = peer // (nonce, peer)映射表
engine.peers2nonces[peer] = nonce // (peer, nonce)映射表
engine.Hello(peer, nonce) // 发送hello信息给各自远程对等节点peer
}
time.AfterFunc(engine.digestWaitTime, func() { // 等待摘要时长 处理返回的摘要
engine.processIncomingDigests()
})
}
// 处理摘要
func (engine *PullEngine) processIncomingDigests() {
engine.ignoreDigests() // 忽略摘要
engine.lock.Lock()
defer engine.lock.Unlock()
requestMapping := make(map[string][]string) // 构建请求映射表 (目的dest , items's ids)
for n, sources := range engine.item2owners { // item2owners:
// select a random source
source := sources[util.RandomInt(len(sources))]
if _, exists := requestMapping[source]; !exists {
requestMapping[source] = make([]string, 0)
}
// append the number to that source
requestMapping[source] = append(requestMapping[source], n)
}
engine.acceptResponses()
for dest, seqsToReq := range requestMapping { // 遍历对各个peer执行request消息
engine.SendReq(dest, seqsToReq, engine.peers2nonces[dest])
}
time.AfterFunc(engine.responseWaitTime, engine.endPull) // 等待响应时长 执行结束pull
}
// 结束pull
func (engine *PullEngine) endPull() {
engine.lock.Lock()
defer engine.lock.Unlock()
atomic.StoreInt32(&(engine.acceptingResponses), int32(0)) // acceptingResponses 置 为0
engine.outgoingNONCES.Clear() // 输出的nonce清空
engine.item2owners = make(map[string][]string) // 置空映射表
engine.peers2nonces = make(map[string]uint64)
engine.nonces2peers = make(map[uint64]string)
}
// 当摘要到达时通知PullEngine
// OnDigest notifies the engine that a digest has arrived
func (engine *PullEngine) OnDigest(digest []string, nonce uint64, context interface{}) {
if !engine.isAcceptingDigests() || !engine.outgoingNONCES.Exists(nonce) { // 摘要和nonce检查
return
}
engine.lock.Lock()
defer engine.lock.Unlock()
for _, n := range digest { // 遍历接收到的摘要
if engine.state.Exists(n) { // 状态集已存在则不做处理
continue
}
if _, exists := engine.item2owners[n]; !exists { // 不存在 新建内容便于后续添加新内容; 若存在则增加peer到item2owners映射表
engine.item2owners[n] = make([]string, 0)
}
// 存在则新增
engine.item2owners[n] = append(engine.item2owners[n], engine.nonces2peers[nonce])
}
}
// 将item添加到状态集中
// Add adds items to the state
func (engine *PullEngine) Add(seqs ...string) {
for _, seq := range seqs {
engine.state.Add(seq)
}
}
// 移除状态集中的item
// Remove removes items from the state
func (engine *PullEngine) Remove(seqs ...string) {
for _, seq := range seqs {
engine.state.Remove(seq)
}
}
// 当hello消息到达时通知PullEngine
// OnHello notifies the engine a hello has arrived
func (engine *PullEngine) OnHello(nonce uint64, context interface{}) {
engine.incomingNONCES.Add(nonce) // 新增nonce
time.AfterFunc(engine.requestWaitTime, func() { // 执行request等待时长后的操作
engine.incomingNONCES.Remove(nonce) // 移除nonce
})
a := engine.state.ToArray()
var digest []string
filter := engine.digFilter(context) // 获取PullEngine的摘要过滤器
for _, item := range a { // 遍历状态内容 通过filter的摘要则追加到本地digest数组中
dig := item.(string)
if !filter(dig) {
continue
}
digest = append(digest, dig)
}
if len(digest) == 0 {
return
}
engine.SendDigest(digest, nonce, context) // 发送摘要请求
}
// 当request消息到达时通知PullEngine
// OnReq notifies the engine a request has arrived
func (engine *PullEngine) OnReq(items []string, nonce uint64, context interface{}) {
if !engine.incomingNONCES.Exists(nonce) { // 不存在nonce 则忽略
return
}
engine.lock.Lock()
defer engine.lock.Unlock()
filter := engine.digFilter(context) // 获取过滤器
var items2Send []string
for _, item := range items { // 遍历item 满足过滤器 并且engine状态集已存在 便追加到本地item2Seed中
if engine.state.Exists(item) && filter(item) {
items2Send = append(items2Send, item)
}
}
if len(items2Send) == 0 {
return
}
go engine.SendRes(items2Send, context, nonce) // 执行发送response消息
}
// 当response消息到达 则通知PullEngine
// OnRes notifies the engine a response has arrived
func (engine *PullEngine) OnRes(items []string, nonce uint64) {
if !engine.outgoingNONCES.Exists(nonce) || !engine.isAcceptingResponses() { // nonce不存在engine的outgoingNONCES中或非响应消息 则忽略
return
}
engine.Add(items...) // 添加item
}
// 新建NONCE
func (engine *PullEngine) newNONCE() uint64 {
n := uint64(0) // 定义一个uint64的变量
for { // 循环执行 通过随机函数生产一个随机数 并且该随机数不存在outgoingNONCES中 则接受该NONCE
n = util.RandomUInt64()
if !engine.outgoingNONCES.Exists(n) {
return n
}
}
}