Fabric 1.0原始碼分析(16)gossip(流言演算法) #GossipServer(Gossip服務端)
# Fabric 1.0原始碼筆記 之 gossip(流言演算法) #GossipServer(Gossip服務端)
## 1、GossipServer概述
GossipServer相關程式碼,分佈在protos/gossip、gossip/comm目錄下。目錄結構如下:
* protos/gossip目錄:
* message.pb.go,GossipClient介面定義及實現,GossipServer介面定義。
* gossip/comm目錄:
* comm.go,Comm介面定義。
* conn.go,connFactory介面定義,以及connectionStore結構體及方法。
* comm_impl.go,commImpl結構體及方法(同時實現GossipServer介面/Comm介面/connFactory介面)。
* demux.go,ChannelDeMultiplexer結構體及方法。
## 2、GossipClient介面定義及實現
### 2.1、GossipClient介面定義
```go
type GossipClient interface {
// GossipStream is the gRPC stream used for sending and receiving messages
GossipStream(ctx context.Context, opts ...grpc.CallOption) (Gossip_GossipStreamClient, error)
// Ping is used to probe a remote peer's aliveness
Ping(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error)
}
//程式碼在protos/gossip/message.pb.go
```
### 2.2、GossipClient介面實現
```go
type gossipClient struct {
cc *grpc.ClientConn
}
func NewGossipClient(cc *grpc.ClientConn) GossipClient {
return &gossipClient{cc}
}
func (c *gossipClient) GossipStream(ctx context.Context, opts ...grpc.CallOption) (Gossip_GossipStreamClient, error) {
stream, err := grpc.NewClientStream(ctx, &_Gossip_serviceDesc.Streams[0], c.cc, "/gossip.Gossip/GossipStream", opts...)
if err != nil {
return nil, err
}
x := &gossipGossipStreamClient{stream}
return x, nil
}
func (c *gossipClient) Ping(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error) {
out := new(Empty)
err := grpc.Invoke(ctx, "/gossip.Gossip/Ping", in, out, c.cc, opts...)
if err != nil {
return nil, err
}
return out, nil
}
//程式碼在protos/gossip/message.pb.go
```
### 2.3、Gossip_GossipStreamClient介面定義及實現
```go
type Gossip_GossipStreamClient interface {
Send(*Envelope) error
Recv() (*Envelope, error)
grpc.ClientStream
}
type gossipGossipStreamClient struct {
grpc.ClientStream
}
func (x *gossipGossipStreamClient) Send(m *Envelope) error {
return x.ClientStream.SendMsg(m)
}
func (x *gossipGossipStreamClient) Recv() (*Envelope, error) {
m := new(Envelope)
if err := x.ClientStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
//程式碼在protos/gossip/message.pb.go
```
## 3、GossipServer介面定義
### 3.1、GossipServer介面定義
```go
type GossipServer interface {
// GossipStream is the gRPC stream used for sending and receiving messages
GossipStream(Gossip_GossipStreamServer) error
// Ping is used to probe a remote peer's aliveness
Ping(context.Context, *Empty) (*Empty, error)
}
func RegisterGossipServer(s *grpc.Server, srv GossipServer) {
s.RegisterService(&_Gossip_serviceDesc, srv)
}
func _Gossip_GossipStream_Handler(srv interface{}, stream grpc.ServerStream) error {
return srv.(GossipServer).GossipStream(&gossipGossipStreamServer{stream})
}
func _Gossip_Ping_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
in := new(Empty)
if err := dec(in); err != nil {
return nil, err
}
if interceptor == nil {
return srv.(GossipServer).Ping(ctx, in)
}
info := &grpc.UnaryServerInfo{
Server: srv,
FullMethod: "/gossip.Gossip/Ping",
}
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
return srv.(GossipServer).Ping(ctx, req.(*Empty))
}
return interceptor(ctx, in, info, handler)
}
var _Gossip_serviceDesc = grpc.ServiceDesc{
ServiceName: "gossip.Gossip",
HandlerType: (*GossipServer)(nil),
Methods: []grpc.MethodDesc{
{
MethodName: "Ping",
Handler: _Gossip_Ping_Handler,
},
},
Streams: []grpc.StreamDesc{
{
StreamName: "GossipStream",
Handler: _Gossip_GossipStream_Handler,
ServerStreams: true,
ClientStreams: true,
},
},
Metadata: "gossip/message.proto",
}
//程式碼在protos/gossip/message.pb.go
```
### 3.2、Gossip_GossipStreamServer介面定義及實現
```go
type Gossip_GossipStreamServer interface {
Send(*Envelope) error
Recv() (*Envelope, error)
grpc.ServerStream
}
type gossipGossipStreamServer struct {
grpc.ServerStream
}
func (x *gossipGossipStreamServer) Send(m *Envelope) error {
return x.ServerStream.SendMsg(m)
}
func (x *gossipGossipStreamServer) Recv() (*Envelope, error) {
m := new(Envelope)
if err := x.ServerStream.RecvMsg(m); err != nil {
return nil, err
}
return m, nil
}
//程式碼在protos/gossip/message.pb.go
```
## 4、Comm介面/connFactory介面定義
### 4.1、Comm介面定義
```go
type Comm interface {
//返回此例項的 PKI id
GetPKIid() common.PKIidType
//向節點傳送訊息
Send(msg *proto.SignedGossipMessage, peers ...*RemotePeer)
//探測遠端節點是否有響應
Probe(peer *RemotePeer) error
//握手驗證遠端節點
Handshake(peer *RemotePeer) (api.PeerIdentityType, error)
Accept(common.MessageAcceptor) <-chan proto.ReceivedMessage
//獲取懷疑離線節點的只讀通道
PresumedDead() <-chan common.PKIidType
//關閉到某個節點的連線
CloseConn(peer *RemotePeer)
//關閉
Stop()
}
//程式碼在gossip/comm/comm.go
```
### 4.2、connFactory介面定義
```go
type connFactory interface {
createConnection(endpoint string, pkiID common.PKIidType) (*connection, error)
}
//程式碼在gossip/comm/conn.go
```
## 5、commImpl結構體及方法(同時實現GossipServer介面/Comm介面/connFactory介面)
### 5.1、commImpl結構體定義
```go
type commImpl struct {
selfCertHash []byte
peerIdentity api.PeerIdentityType
idMapper identity.Mapper
logger *logging.Logger
opts []grpc.DialOption
secureDialOpts func() []grpc.DialOption
connStore *connectionStore
PKIID []byte
deadEndpoints chan common.PKIidType
msgPublisher *ChannelDeMultiplexer
lock *sync.RWMutex
lsnr net.Listener
gSrv *grpc.Server
exitChan chan struct{}
stopWG sync.WaitGroup
subscriptions []chan proto.ReceivedMessage
port int
stopping int32
}
//程式碼在gossip/comm/comm_impl.go
```
### 5.2、commImpl結構體方法
```go
//conn.serviceConnection(),啟動連線服務
func (c *commImpl) GossipStream(stream proto.Gossip_GossipStreamServer) error
//return &proto.Empty{}
func (c *commImpl) Ping(context.Context, *proto.Empty) (*proto.Empty, error)
func (c *commImpl) GetPKIid() common.PKIidType
//向指定節點傳送訊息
func (c *commImpl) Send(msg *proto.SignedGossipMessage, peers ...*RemotePeer)
//探測遠端節點是否有響應,_, err = cl.Ping(context.Background(), &proto.Empty{})
func (c *commImpl) Probe(remotePeer *RemotePeer) error
//握手驗證遠端節點,_, err = cl.Ping(context.Background(), &proto.Empty{})
func (c *commImpl) Handshake(remotePeer *RemotePeer) (api.PeerIdentityType, error)
func (c *commImpl) Accept(acceptor common.MessageAcceptor) <-chan proto.ReceivedMessage
func (c *commImpl) PresumedDead() <-chan common.PKIidType
func (c *commImpl) CloseConn(peer *RemotePeer)
func (c *commImpl) Stop()
//建立並啟動gRPC Server,以及註冊GossipServer例項
func NewCommInstanceWithServer(port int, idMapper identity.Mapper, peerIdentity api.PeerIdentityType,
//將GossipServer例項註冊至peerServer
func NewCommInstance(s *grpc.Server, cert *tls.Certificate, idStore identity.Mapper,
func extractRemoteAddress(stream stream) string
func readWithTimeout(stream interface{}, timeout time.Duration, address string) (*proto.SignedGossipMessage, error)
//建立gRPC Server,grpc.NewServer(serverOpts...)
func createGRPCLayer(port int) (*grpc.Server, net.Listener, api.PeerSecureDialOpts, []byte)
//建立與服務端連線
func (c *commImpl) createConnection(endpoint string, expectedPKIID common.PKIidType) (*connection, error)
//向指定節點傳送訊息
func (c *commImpl) sendToEndpoint(peer *RemotePeer, msg *proto.SignedGossipMessage)
//return atomic.LoadInt32(&c.stopping) == int32(1)
func (c *commImpl) isStopping() bool
func (c *commImpl) emptySubscriptions()
func (c *commImpl) authenticateRemotePeer(stream stream) (*proto.ConnectionInfo, error)
func (c *commImpl) disconnect(pkiID common.PKIidType)
func (c *commImpl) createConnectionMsg(pkiID common.PKIidType, certHash []byte, cert api.PeerIdentityType, signer proto.Signer) (*proto.SignedGossipMessage, error)
//程式碼在gossip/comm/comm_impl.go
```
#### 5.2.1、func NewCommInstanceWithServer(port int, idMapper identity.Mapper, peerIdentity api.PeerIdentityType,secureDialOpts api.PeerSecureDialOpts, dialOpts ...grpc.DialOption) (Comm, error)
建立並啟動gRPC Server,以及註冊GossipServer例項
```go
func NewCommInstanceWithServer(port int, idMapper identity.Mapper, peerIdentity api.PeerIdentityType,
secureDialOpts api.PeerSecureDialOpts, dialOpts ...grpc.DialOption) (Comm, error) {
var ll net.Listener
var s *grpc.Server
var certHash []byte
if len(dialOpts) == 0 {
//peer.gossip.dialTimeout,gRPC連線撥號的超時
dialOpts = []grpc.DialOption{grpc.WithTimeout(util.GetDurationOrDefault("peer.gossip.dialTimeout", defDialTimeout))}
}
if port > 0 {
//建立gRPC Server,grpc.NewServer(serverOpts...)
s, ll, secureDialOpts, certHash = createGRPCLayer(port)
}
commInst := &commImpl{
selfCertHash: certHash,
PKIID: idMapper.GetPKIidOfCert(peerIdentity),
idMapper: idMapper,
logger: util.GetLogger(util.LoggingCommModule, fmt.Sprintf("%d", port)),
peerIdentity: peerIdentity,
opts: dialOpts,
secureDialOpts: secureDialOpts,
port: port,
lsnr: ll,
gSrv: s,
msgPublisher: NewChannelDemultiplexer(),
lock: &sync.RWMutex{},
deadEndpoints: make(chan common.PKIidType, 100),
stopping: int32(0),
exitChan: make(chan struct{}, 1),
subscriptions: make([]chan proto.ReceivedMessage, 0),
}
commInst.connStore = newConnStore(commInst, commInst.logger)
if port > 0 {
commInst.stopWG.Add(1)
go func() {
defer commInst.stopWG.Done()
s.Serve(ll) //啟動gRPC Server
}()
//commInst註冊到gRPC Server
proto.RegisterGossipServer(s, commInst)
}
return commInst, nil
}
//程式碼在gossip/comm/comm_impl.go
```
#### 5.2.2、func NewCommInstance(s *grpc.Server, cert *tls.Certificate, idStore identity.Mapper,peerIdentity api.PeerIdentityType, secureDialOpts api.PeerSecureDialOpts,dialOpts ...grpc.DialOption) (Comm, error)
將GossipServer例項註冊至peerServer
```go
func NewCommInstance(s *grpc.Server, cert *tls.Certificate, idStore identity.Mapper,
peerIdentity api.PeerIdentityType, secureDialOpts api.PeerSecureDialOpts,
dialOpts ...grpc.DialOption) (Comm, error) {
dialOpts = append(dialOpts, grpc.WithTimeout(util.GetDurationOrDefault("peer.gossip.dialTimeout", defDialTimeout)))
//構造commImpl
commInst, err := NewCommInstanceWithServer(-1, idStore, peerIdentity, secureDialOpts, dialOpts...)
if cert != nil {
inst := commInst.(*commImpl)
inst.selfCertHash = certHashFromRawCert(cert.Certificate[0])
}
proto.RegisterGossipServer(s, commInst.(*commImpl))
return commInst, nil
}
//程式碼在gossip/comm/comm_impl.go
```
//建立與服務端連線
#### 5.2.3、func (c *commImpl) createConnection(endpoint string, expectedPKIID common.PKIidType) (*connection, error)
```go
func (c *commImpl) createConnection(endpoint string, expectedPKIID common.PKIidType) (*connection, error) {
var err error
var cc *grpc.ClientConn
var stream proto.Gossip_GossipStreamClient
var pkiID common.PKIidType
var connInfo *proto.ConnectionInfo
var dialOpts []grpc.DialOption
dialOpts = append(dialOpts, c.secureDialOpts()...)
dialOpts = append(dialOpts, grpc.WithBlock())
dialOpts = append(dialOpts, c.opts...)
cc, err = grpc.Dial(endpoint, dialOpts...)
cl := proto.NewGossipClient(cc)
if _, err = cl.Ping(context.Background(), &proto.Empty{}); err != nil {
cc.Close()
return nil, err
}
ctx, cf := context.WithCancel(context.Background())
stream, err = cl.GossipStream(ctx)
connInfo, err = c.authenticateRemotePeer(stream)
pkiID = connInfo.ID
conn := newConnection(cl, cc, stream, nil)
conn.pkiID = pkiID
conn.info = connInfo
conn.logger = c.logger
conn.cancel = cf
h := func(m *proto.SignedGossipMessage) {
c.msgPublisher.DeMultiplex(&ReceivedMessageImpl{
conn: conn,
lock: conn,
SignedGossipMessage: m,
connInfo: connInfo,
})
}
conn.handler = h
return conn, nil
}
//程式碼在gossip/comm/comm_impl.go
```
## 6、connectionStore和connection結構體及方法
### 6.1、connection結構體及方法
```go
type connection struct {
cancel context.CancelFunc
info *proto.ConnectionInfo
outBuff chan *msgSending
logger *logging.Logger // logger
pkiID common.PKIidType // pkiID of the remote endpoint
handler handler // function to invoke upon a message reception
conn *grpc.ClientConn // gRPC connection to remote endpoint
cl proto.GossipClient // gRPC stub of remote endpoint
clientStream proto.Gossip_GossipStreamClient // client-side stream to remote endpoint
serverStream proto.Gossip_GossipStreamServer // server-side stream to remote endpoint
stopFlag int32 // indicates whether this connection is in process of stopping
stopChan chan struct{} // a method to stop the server-side gRPC call from a different go-routine
sync.RWMutex // synchronizes access to shared variables
}
//構造connection
func newConnection(cl proto.GossipClient, c *grpc.ClientConn, cs proto.Gossip_GossipStreamClient, ss proto.Gossip_GossipStreamServer) *connection
//關閉connection
func (conn *connection) close()
//atomic.LoadInt32(&(conn.stopFlag)) == int32(1)
func (conn *connection) toDie() bool
//conn.outBuff <- m,其中m為msgSending{envelope: msg.Envelope,onErr: onErr,}
func (conn *connection) send(msg *proto.SignedGossipMessage, onErr func(error))
//go conn.readFromStream(errChan, msgChan)、go conn.writeToStream(),同時msg := <-msgChan,conn.handler(msg)
func (conn *connection) serviceConnection() error
//迴圈不間斷從conn.outBuff取資料,然後stream.Send(m.envelope)
func (conn *connection) writeToStream()
//迴圈不間斷envelope, err := stream.Recv()、msg, err := envelope.ToGossipMessage()、msgChan <- msg
func (conn *connection) readFromStream(errChan chan error, msgChan chan *proto.SignedGossipMessage)
//獲取conn.serverStream
func (conn *connection) getStream() stream
//程式碼在gossip/comm/conn.go
```
### 6.2、connectionStore結構體及方法
```go
type connectionStore struct {
logger *logging.Logger // logger
isClosing bool // whether this connection store is shutting down
connFactory connFactory // creates a connection to remote peer
sync.RWMutex // synchronize access to shared variables
pki2Conn map[string]*connection //connection map, key為pkiID,value為connection
destinationLocks map[string]*sync.RWMutex //mapping between pkiIDs and locks,
// used to prevent concurrent connection establishment to the same remote endpoint
}
//構造connectionStore
func newConnStore(connFactory connFactory, logger *logging.Logger) *connectionStore
//從connection map中獲取連線,如無則建立並啟動連線,並寫入connection map中
func (cs *connectionStore) getConnection(peer *RemotePeer) (*connection, error)
//連線數量
func (cs *connectionStore) connNum() int
//關閉指定連線
func (cs *connectionStore) closeConn(peer *RemotePeer)
//關閉所有連線
func (cs *connectionStore) shutdown()
func (cs *connectionStore) onConnected(serverStream proto.Gossip_GossipStreamServer, connInfo *proto.ConnectionInfo) *connection
//註冊連線
func (cs *connectionStore) registerConn(connInfo *proto.ConnectionInfo, serverStream proto.Gossip_GossipStreamServer) *connection
//關閉指定連線
func (cs *connectionStore) closeByPKIid(pkiID common.PKIidType)
//程式碼在gossip/comm/conn.go
```
#### 6.2.1、func (cs *connectionStore) getConnection(peer *RemotePeer) (*connection, error)
```go
func (cs *connectionStore) getConnection(peer *RemotePeer) (*connection, error) {
cs.RLock()
isClosing := cs.isClosing
cs.RUnlock()
pkiID := peer.PKIID
endpoint := peer.Endpoint
cs.Lock()
destinationLock, hasConnected := cs.destinationLocks[string(pkiID)]
if !hasConnected {
destinationLock = &sync.RWMutex{}
cs.destinationLocks[string(pkiID)] = destinationLock
}
cs.Unlock()
destinationLock.Lock()
cs.RLock()
//從connection map中獲取
conn, exists := cs.pki2Conn[string(pkiID)]
if exists {
cs.RUnlock()
destinationLock.Unlock()
return conn, nil
}
cs.RUnlock()
//建立連線
createdConnection, err := cs.connFactory.createConnection(endpoint, pkiID)
destinationLock.Unlock()
conn = createdConnection
cs.pki2Conn[string(createdConnection.pkiID)] = conn
go conn.serviceConnection() //啟動連線的訊息接收處理、以及向對方節點傳送訊息
return conn, nil
}
//程式碼在gossip/comm/conn.go
```
## 7、ChannelDeMultiplexer結構體及方法(多路複用器)
```go
type ChannelDeMultiplexer struct {
channels []*channel
lock *sync.RWMutex
closed int32
}
//構造ChannelDeMultiplexer
func NewChannelDemultiplexer() *ChannelDeMultiplexer
//atomic.LoadInt32(&m.closed) == int32(1)
func (m *ChannelDeMultiplexer) isClosed() bool
//關閉
func (m *ChannelDeMultiplexer) Close()
//新增通道
func (m *ChannelDeMultiplexer) AddChannel(predicate common.MessageAcceptor) chan interface{}
//挨個通道傳送訊息
func (m *ChannelDeMultiplexer) DeMultiplex(msg interface{})
//程式碼在
```
網址:http://www.qukuailianxueyuan.io/
欲領取造幣技術與全套虛擬機器資料
區塊鏈技術交流QQ群:756146052 備註:CSDN
尹成學院微信:備註:CSDN
網址:http://www.qukuailianxueyuan.io/
欲領取造幣技術與全套虛擬機器資料
區塊鏈技術交流QQ群:756146052 備註:CSDN
尹成學院微信:備註:CSDN
相關文章
- Fabric 1.0原始碼分析(15)gossip(流言演算法)原始碼Go演算法
- Fabric 1.0原始碼分析(17)gossip(流言演算法) #deliverclient(deliver客戶端)原始碼Go演算法client客戶端
- 區塊鏈教程Fabric1.0原始碼分析流言演算法Gossip服務端二-兄弟連區塊鏈原始碼演算法Go服務端
- 區塊鏈教程Fabric1.0原始碼分析流言演算法Gossip服務端一兄弟連區塊鏈教程區塊鏈原始碼演算法Go服務端
- Fabric 1.0原始碼分析(30) Orderer #BroadcastServer(Broadcast服務端)原始碼ASTServer服務端
- Fabric 1.0原始碼分析(35)Peer #EndorserServer(Endorser服務端)原始碼Server服務端
- 基於Gossip流言演算法實現alertmanager高可用Go演算法
- Fabric 1.0原始碼分析(13)events(事件服務)原始碼事件
- Fabric 1.0原始碼分析(25) Orderer原始碼
- Fabric 1.0原始碼分析(31) Peer原始碼
- Fabric 1.0原始碼分析(36) Peer #EndorserClient(Endorser客戶端)原始碼client客戶端
- Fabric 1.0原始碼分析(37) Peer #DeliverClient(Deliver客戶端)原始碼client客戶端
- Fabric 1.0原始碼分析(38) Peer #BroadcastClient(Broadcast客戶端)原始碼ASTclient客戶端
- Fabric 1.0原始碼分析(1)BCCSP(區塊鏈加密服務提供者)原始碼區塊鏈加密
- Fabric 1.0原始碼分析(24)MSP(成員關係服務提供者)原始碼
- Fabric 1.0原始碼分析(3)Chaincode(鏈碼)原始碼AI
- Fabric 1.0原始碼分析(40) Proposal(提案)原始碼
- Fabric 1.0原始碼分析(14) flogging(Fabric日誌系統)原始碼
- Fabric 1.0原始碼分析(18) Ledger(賬本)原始碼
- Fabric 1.0原始碼分析(43) Tx(Transaction 交易)原始碼
- Fabric 1.0原始碼分析(47)Fabric 1.0.4 go程式碼量統計原始碼Go
- Fabric 1.0原始碼分析(42)scc(系統鏈碼)原始碼
- Fabric 1.0原始碼分析(26)Orderer #ledger(Orderer Ledger)原始碼
- Fabric 1.0原始碼分析(39) policy(背書策略)原始碼
- Fabric 1.0原始碼分析(45)gRPC(Fabric中註冊的gRPC Service)原始碼RPC
- Fabric 1.0原始碼分析(10)consenter(共識外掛)原始碼
- Fabric 1.0原始碼分析(23)LevelDB(KV資料庫)原始碼資料庫
- Fabric 1.0原始碼分析(44)Tx #RWSet(讀寫集)原始碼
- Gossip 協議詳解Go協議
- 聊聊apache gossip的ActiveGossiperApacheGo
- 啥是Gossip協議?Go協議
- Fabric 1.0原始碼分析(5)Chaincode(鏈碼)體系總結原始碼AI
- Fabric 1.0原始碼分析(6)configtx(配置交易) #ChannelConfig(通道配置)原始碼
- Fabric 1.0原始碼分析(20) Ledger #idStore(ledgerID資料庫)原始碼資料庫
- Fabric 1.0原始碼分析(29) Orderer #multichain(多鏈支援包)原始碼AI
- Fabric 1.0原始碼分析(41)putils(protos/utils工具包)原始碼
- Fabric 1.0原始碼分析(46)ECDSA(橢圓曲線數字簽名演算法)原始碼演算法
- Fabric 1.0原始碼分析(2) blockfile(區塊檔案儲存)原始碼BloC