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