Hyperledger Fabric从源码分析事件机制(一)

Fabric 允许应用程序在调用链码的时候监听链码中设置的事件,在监听到相应的事件后做相应的处理过程。

Fabric1.4 中的事件机制与老版本的事件机制源码部分有所不同,用的是Deliver的方式,所以老版本的代码与 Fabric1.4 版本的代码会有所不同。

本文将从源码解析整个事件机制的处理流程的。


回顾链码调用源码

链码调用监听事件部分代码

在之前的文章——Hyperledger Fabric从源码分析链码查询与调用,我有提到在ChaincodeInvokeOrQuery()函数中有对是否等待链码事件的处理,先来回顾一下:

func ChaincodeInvokeOrQuery(/*参数省略*/) (*pb.ProposalResponse, error) {
   
	// ...........
	if invoke {
   
    // invoke走这里
		// ..........
			var dg *deliverGroup
			var ctx context.Context
			if waitForEvent {
   
        // 这个参数是事件相关,命令中可以设置--waitForEvent参数,表示是否监听事件
				var cancelFunc context.CancelFunc
				ctx, cancelFunc = context.WithTimeout(context.Background(), waitForEventTimeout)
				defer cancelFunc()
				
        // 创建DeliverGroup
				dg = newDeliverGroup(deliverClients, peerAddresses, certificate, channelID, txid)
        // 连接所有peer节点上的deliver service
				err := dg.Connect(ctx)
				if err != nil {
   
					return nil, err
				}
			}

      // 将交易发送给排序节点
			if err = bc.Send(env); err != nil {
   
				return proposalResp, errors.WithMessage(err, fmt.Sprintf("error sending transaction for %s", funcName))
			}

			if dg != nil && ctx != nil {
   
        // 等待事件被记录到账本中
				err = dg.Wait(ctx)
				if err != nil {
   
					return nil, err
				}
			}
		}
	}

	return proposalResp, nil
}

创建deliverGroup对象

如果在执行peer chaincode invoke命令的时候设置了--waitForEvent参数,表示会等待并监听链码上的事件。函数中调用newDeliverGroup()创建了一个dg对象,看下这个函数,在peer/chaincode/common.go的557行:

type deliverGroup struct {
   
	Clients     []*deliverClient	// 所有背书节点的deliverClient
	Certificate tls.Certificate		// tls证书
	ChannelID   string	// 通道ID
	TxID        string	// txID
	mutex       sync.Mutex	
	Error       error	
	wg          sync.WaitGroup	
}

func newDeliverGroup(deliverClients []api.PeerDeliverClient, peerAddresses []string, certificate tls.Certificate, channelID string, txid string) *deliverGroup {
   
  // deliverClients表示所有连接背书节点的deliverClients
  // peerAddress表示所有背书节点的address
  // certificate表示tls证书
  // channelID为通道ID,txid为交易id
	clients := make([]*deliverClient, len(deliverClients))
	for i, client := range deliverClients {
   
		dc := &deliverClient{
   
			Client:  client,
			Address: peerAddresses[i],
		}
		clients[i] = dc
	}

	dg := &deliverGroup{
   
		Clients:     clients,
		Certificate: certificate,
		ChannelID:   channelID,
		TxID:        txid,
	}

	return dg
}

InitFactory() 创建 deliverClients

deliverClients参数在InitCmdFactory()中创建,这个函数我们之前只是简单介绍了一下,它是创建了一个链码命令工厂,其中保存了很多clients,其中就包括了DeliverClients,现在就来仔细看一下它是如何创建这些clients的,代码在peer/chaincode/common.go的349行:

// ChaincodeCmdFactory holds the clients used by ChaincodeCmd
type ChaincodeCmdFactory struct {
   
	EndorserClients []pb.EndorserClient	// 背书客户端,用于背书
	DeliverClients  []api.PeerDeliverClient	// Deliver客户端,用于向peer的DeliverServer发送消息,主要就是事件
	Certificate     tls.Certificate	// tls证书相关信息
	Signer          msp.SigningIdentity	// 用于消息的签名
	BroadcastClient common.BroadcastClient	// 广播客户端,用于向orderer节点发送消息
}

// InitCmdFactory init the ChaincodeCmdFactory with default clients
func InitCmdFactory(cmdName string, isEndorserRequired, isOrdererRequired bool) (*ChaincodeCmdFactory, error) {
   
	var err error
	var endorserClients []pb.EndorserClient
	var deliverClients []api.PeerDeliverClient
	if isEndorserRequired {
   
    // 是否需要背书,一般使用peer chaincode package命令打包链码的时候该参数为false
    // 主要是验证peer连接的一些参数
		if err = validatePeerConnectionParameters(cmdName); err != nil {
   
			return nil, errors.WithMessage(err, "error validating peer connection parameters")
		}
    // 如果没有指定peerAddresses,它的值默认为[]string{""},包含一个空字符串,也就是下面的循环只会执行一次
    for i, address := range peerAddresses {
   
			var tlsRootCertFile string
			if tlsRootCertFiles != nil {
   
        // 如果没有指定tlsRootCertFiles,它的值默认为[]string{""}
				tlsRootCertFile = tlsRootCertFiles[i]
			}
      // 获取指定peer的背书客户端
			endorserClient, err := common.GetEndorserClientFnc(address, tlsRootCertFile)
			if err != nil {
   
				return nil, errors.WithMessage(err, fmt.Sprintf("error getting endorser client for %s", cmdName))
			}
			endorserClients = append(endorserClients, endorserClient)
      // 获取指定peer的deliver客户端
			deliverClient, err := common.GetPeerDeliverClientFnc(address, tlsRootCertFile)
			if err != nil {
   
				return nil, errors.WithMessage(err, fmt.Sprintf("error getting deliver client for %s", cmdName))
			}
			deliverClients = append(deliverClients, deliverClient)
		}
    if len(endorserClients) == 0 {
   
      // 如果endorserClients为长度为0则报错
			return nil, errors.New("no endorser clients retrieved - this might indicate a bug")
		}
	}
  // 获取证书
  certificate, err := common.GetCertificateFnc()
	if err != nil {
   
		return nil, errors.WithMessage(err, "error getting client cerificate")
	}
	// 获取签名者
	signer, err := common.GetDefaultSignerFnc()
	if err != nil {
   
		return nil, errors.WithMessage(err, "error getting default signer")
	}
  
  var broadcastClient common.BroadcastClient
  // 如果需要发送交易给排序节点的操作,isOrdererRequired为空
  // query的时候就会为false,invoke的时候就会为空
	if isOrdererRequired {
   
		if len(common
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值