Hyperledger-Fabric源码分析(Gossip-StateInfoSnapshot)

前面我们说过了StateInfo消息, 这个主要是自发热给别人,有点细水长流的意思。下面我们要讲的是Gossip里面怎么去主动拉取成员的StateInfo列表。

struct

type StateInfoPullRequest struct {
    // channel_MAC is an authentication code that proves
    // that the peer that sent this message knows
    // the name of the channel.
    Channel_MAC          []byte   
}

type StateInfoSnapshot struct {
    Elements             []*Envelope 
}

type Envelope struct {
    Payload              []byte         
    Signature            []byte         
    SecretEnvelope       *SecretEnvelope
}

type SecretEnvelope struct {
    Payload              []byte 
    Signature            []byte   
}

  • 这里要注意的是,这两个消息是成对的,StateInfoPullRequest是获取的请求,StateInfoSnapshot是返回的结果。

GossipMessage_StateInfoPullReq

初始化

func (gc *gossipChannel) createStateInfoRequest() (*proto.SignedGossipMessage, error) {
   return (&proto.GossipMessage{
      Tag:   proto.GossipMessage_CHAN_OR_ORG,
      Nonce: 0,
      Content: &proto.GossipMessage_StateInfoPullReq{
         StateInfoPullReq: &proto.StateInfoPullRequest{
            Channel_MAC: GenerateMAC(gc.pkiID, gc.chainID),
         },
      },
   }).NoopSign()
}
  • 这个请求没什么好讲的,基本上就是生成MAC

发起点

func NewGossipChannel(pkiID common.PKIidType, org api.OrgIdentityType, mcs api.MessageCryptoService,
    chainID common.ChainID, adapter Adapter, joinMsg api.JoinChannelMessage) GossipChannel {
    ...

    // Periodically publish state info
    go gc.periodicalInvocation(gc.publishStateInfo, gc.stateInfoPublishScheduler.C)
    // Periodically request state info
    go gc.periodicalInvocation(gc.requestStateInfo, gc.stateInfoRequestScheduler.C)
    ...

    go gc.membershipTracker.trackMembershipChanges()
    return gc
}
  • 又到了这里,gc初始化的时候就会自循环发起StateInfoPullReq,真是不甘寂寞。边吃边拉。
func (gc *gossipChannel) requestStateInfo() {
   req, err := gc.createStateInfoRequest()
   if err != nil {
      gc.logger.Warningf("Failed creating SignedGossipMessage: %+v", errors.WithStack(err))
      return
   }
   endpoints := filter.SelectPeers(gc.GetConf().PullPeerNum, gc.GetMembership(), gc.IsMemberInChan)
   gc.Send(req, endpoints...)
}
  • 这里不深入了,组装消息前面已经说了
  • 随机取PullPeerNum个节点,Send。这里注意Gossip中有两种发送方式,一种是Send,一种是Gossip。有区别,虽然都是通过底层comm来send,不过emitter会做一层buff来缓冲,对于不是那么急的,可以累计多个消息,一次发出去。
  • 显然这里的场景是要立即,马上发送出去。

接受点

func (gc *gossipChannel) HandleMessage(msg proto.ReceivedMessage) {
   ...

   if m.IsStateInfoPullRequestMsg() {
      msg.Respond(gc.createStateInfoSnapshot(orgID))
      return
   }

   ...
}
  • 消息校验的部分,之前讲过了,这里不再赘述
  • 可以看到收到request后,会createStateInfoSnapshot
  • 下面进到GossipMessage_StateSnapshot的部分

GossipMessage_StateSnapshot

初始化

func (gc *gossipChannel) createStateInfoSnapshot(requestersOrg api.OrgIdentityType) *proto.GossipMessage {
   sameOrg := bytes.Equal(gc.selfOrg, requestersOrg)
   rawElements := gc.stateInfoMsgStore.Get()
   elements := []*proto.Envelope{}
   for _, rawEl := range rawElements {
      msg := rawEl.(*proto.SignedGossipMessage)
      orgOfCurrentMsg := gc.GetOrgOfPeer(msg.GetStateInfo().PkiId)
      // If we're in the same org as the requester, or the message belongs to a foreign org
      // don't do any filtering
      if sameOrg || !bytes.Equal(orgOfCurrentMsg, gc.selfOrg) {
         elements = append(elements, msg.Envelope)
         continue
      }
      // Else, the requester is in a different org, so disclose only StateInfo messages that their
      // corresponding AliveMessages have external endpoints
      if netMember := gc.Lookup(msg.GetStateInfo().PkiId); netMember == nil || netMember.Endpoint == "" {
         continue
      }
      elements = append(elements, msg.Envelope)
   }

   return &proto.GossipMessage{
      Channel: gc.chainID,
      Tag:     proto.GossipMessage_CHAN_OR_ORG,
      Nonce:   0,
      Content: &proto.GossipMessage_StateSnapshot{
         StateSnapshot: &proto.StateInfoSnapshot{
            Elements: elements,
         },
      },
   }
}
  • 首先判定同属一个Org
  • 获取stateInfoMsgStore保存的所有数据集,这里需要注意的是,这个store当初创建的时候是指定了过期策略的,也就是说,里面的状态会定时清理过期的无效状态。还记得StateInfo消息是有时间戳的么。
  • 当然了,如果请求人跟本地同属一个Org,消息全部推送过去没有话讲。
  • 如果不属于一个Org,难道就不推送么?这里处于有利于消息扩散的原则,当然也要尽可能的去做
    • 如果store里面有消息跟本地不是属于同一个组织,那么推送出去也没什么不好,反正自己也不知道是谁发来的,有人要就给他好了。
    • 如果store里面有消息跟本地属于同一个组织,那么问题来了,现在来了个陌生人来请求这个组织内的状态,显然不能给他,这里就跳过了。

发送

func (m *ReceivedMessageImpl) Respond(msg *proto.GossipMessage) {
   sMsg, err := msg.NoopSign()
   if err != nil {
      err = errors.WithStack(err)
      m.conn.logger.Errorf("Failed creating SignedGossipMessage: %+v", err)
      return
   }
   m.conn.send(sMsg, func(e error) {}, blockingSend)
}
  • 因为是Response,当然要点对点send。

接受点

func (gc *gossipChannel) HandleMessage(msg proto.ReceivedMessage) {
    ...

    if m.IsStateInfoSnapshot() {
        gc.handleStateInfSnapshot(m.GossipMessage, msg.GetConnectionInfo().ID)
        return
    }
    ...
}
  • 又来到这里,看来这个系列,这段代码会复制粘贴N次

  • 好了gc收到后,开始处理

处理

func (gc *gossipChannel) handleStateInfSnapshot(m *proto.GossipMessage, sender common.PKIidType) {
   chanName := string(gc.chainID)
   for _, envelope := range m.GetStateSnapshot().Elements {
      stateInf, err := envelope.ToGossipMessage()
      if err != nil {
         gc.logger.Warningf("Channel %s : StateInfo snapshot contains an invalid message: %+v", chanName, errors.WithStack(err))
         return
      }
      if !stateInf.IsStateInfoMsg() {
         gc.logger.Warning("Channel", chanName, ": Element of StateInfoSnapshot isn't a StateInfoMessage:",
            stateInf, "message sent from", sender)
         return
      }
      si := stateInf.GetStateInfo()
      orgID := gc.GetOrgOfPeer(si.PkiId)
      if orgID == nil {
         gc.logger.Debug("Channel", chanName, ": Couldn't find org identity of peer",
            string(si.PkiId), "message sent from", string(sender))
         return
      }

      if !gc.IsOrgInChannel(orgID) {
         gc.logger.Warning("Channel", chanName, ": Peer", stateInf.GetStateInfo().PkiId,
            "is not in an eligible org, can't process a stateInfo from it, sent from", sender)
         return
      }

      expectedMAC := GenerateMAC(si.PkiId, gc.chainID)
      if !bytes.Equal(si.Channel_MAC, expectedMAC) {
         gc.logger.Warning("Channel", chanName, ": StateInfo message", stateInf,
            ", has an invalid MAC. Expected", expectedMAC, ", got", si.Channel_MAC, ", sent from", sender)
         return
      }
      err = gc.ValidateStateInfoMessage(stateInf)
      if err != nil {
         gc.logger.Warningf("Channel %s: Failed validating state info message: %v sent from %v : %+v", chanName, stateInf, sender, errors.WithStack(err))
         return
      }

      if gc.Lookup(si.PkiId) == nil {
         // Skip StateInfo messages that belong to peers
         // that have been expired
         continue
      }

      gc.stateInfoMsgStore.Add(stateInf)
   }
}
  • 遍历收到的StateInfo消息集
  • 如果消息发起节点的组织不属于这个channel,丢弃。
  • 校验MAC
  • 校验消息,StateInfo篇已经讲过了。
  • 消息发起节点是否alive,如果本地都Lookup不到,也选择丢弃。
  • 下面就是久违的加到stateInfoMsgStore了。

总结

  • 可以看到,整个机制还是比较简单,唯一要注意是,StateInfo消息保存下来的必要条件是所属的Org要同属一个Channel。

你可能感兴趣的:(Hyperledger-Fabric源码分析(Gossip-StateInfoSnapshot))