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

上篇文章——Hyperledger Fabric从源码分析事件机制(一),我从 cmd 中添加--waitForEvent参数去监听事件这一入口出发,分析了DeliverClient这一侧的相关源代码,接下来这篇文章将会解析DeliverServer这一侧的相关源代码,准备好上车了兄弟们。


从proto文件中发现DeliverServer

事件相关的pb.go文件在protos/peer/events.pb.go中,关于DeliverServer的部分在576行:

// DeliverServer is the server API for Deliver service.
type DeliverServer interface {
	// deliver first requires an Envelope of type ab.DELIVER_SEEK_INFO with
	// Payload data as a marshaled orderer.SeekInfo message,
	// then a stream of block replies is received
	Deliver(Deliver_DeliverServer) error
	// deliver first requires an Envelope of type ab.DELIVER_SEEK_INFO with
	// Payload data as a marshaled orderer.SeekInfo message,
	// then a stream of **filtered** block replies is received
	DeliverFiltered(Deliver_DeliverFilteredServer) error
}

// 通过这个函数注册DeliverServer
func RegisterDeliverServer(s *grpc.Server, srv DeliverServer) {
	s.RegisterService(&_Deliver_serviceDesc, srv)
}

// Deliver流的Handler
func _Deliver_Deliver_Handler(srv interface{}, stream grpc.ServerStream) error {
	return srv.(DeliverServer).Deliver(&deliverDeliverServer{stream})
}

type Deliver_DeliverServer interface {
	Send(*DeliverResponse) error
	Recv() (*common.Envelope, error)
	grpc.ServerStream
}

type deliverDeliverServer struct {
	grpc.ServerStream
}

func (x *deliverDeliverServer) Send(m *DeliverResponse) error {
	return x.ServerStream.SendMsg(m)
}

func (x *deliverDeliverServer) Recv() (*common.Envelope, error) {
	m := new(common.Envelope)
	if err := x.ServerStream.RecvMsg(m); err != nil {
		return nil, err
	}
	return m, nil
}

// DeliverFiltered流的Handler
func _Deliver_DeliverFiltered_Handler(srv interface{}, stream grpc.ServerStream) error {
	return srv.(DeliverServer).DeliverFiltered(&deliverDeliverFilteredServer{stream})
}

type Deliver_DeliverFilteredServer interface {
	Send(*DeliverResponse) error
	Recv() (*common.Envelope, error)
	grpc.ServerStream
}

type deliverDeliverFilteredServer struct {
	grpc.ServerStream
}

func (x *deliverDeliverFilteredServer) Send(m *DeliverResponse) error {
	return x.ServerStream.SendMsg(m)
}

func (x *deliverDeliverFilteredServer) Recv() (*common.Envelope, error) {
	m := new(common.Envelope)
	if err := x.ServerStream.RecvMsg(m); err != nil {
		return nil, err
	}
	return m, nil
}

// grpc服务对象
var _Deliver_serviceDesc = grpc.ServiceDesc{
	ServiceName: "protos.Deliver",
	HandlerType: (*DeliverServer)(nil),
	Methods:     []grpc.MethodDesc{},
	Streams: []grpc.StreamDesc{
		{
			StreamName:    "Deliver",
			Handler:       _Deliver_Deliver_Handler,
			ServerStreams: true,
			ClientStreams: true,
		},
		{
			StreamName:    "DeliverFiltered",
			Handler:       _Deliver_DeliverFiltered_Handler,
			ServerStreams: true,
			ClientStreams: true,
		},
	},
	Metadata: "peer/events.proto",
}

peer 节点启动时注册 DeliverServer

看看RegisterDeliverServer()这个方法在哪被调用了

abServer := peer.NewDeliverEventsServer(mutualTLS, policyCheckerProvider, &peer.DeliverChainManager{}, metricsProvider)
pb.RegisterDeliverServer(peerServer.Server(), abServer)

上述代码在peer/node/start.go的274行。

因此破案了,在peer节点启动的时候,启动了DeliverServer。其中主要是abServer这个对象实现了DeliverServer的相关接口,那么来看下NewDeliverEventsServer这个方法吧,生成了一个DeliverEventsServer对象,在core/peer/deliverevents.go的132行:

// NewDeliverEventsServer creates a peer.Deliver server to deliver block and
// filtered block events
func NewDeliverEventsServer(mutualTLS bool, policyCheckerProvider PolicyCheckerProvider, chainManager deliver.ChainManager, metricsProvider metrics.Provider) peer.DeliverServer {
  // mutualTLS是TLS相关的一些东西
  // policyCheckerProvider主要是做ACL的一些检测,暂时不关心
  // chainManager传进来的是一个空的DeliverChainManager对象
  // metricsProvider是指标相关的东西
	timeWindow := viper.GetDuration("peer.authentication.timewindow")
	if timeWindow == 0 {
		defaultTimeWindow := 15 * time.Minute
		logger.Warningf("`peer.authentication.timewindow` not set; defaulting to %s", defaultTimeWindow)
		timeWindow = defaultTimeWindow
	}
	metrics := deliver.NewMetrics(metricsProvider)
  // 最终返回的是一个server对象
	return &server{
    // NewHandler创建了一个deliver.Handler对象
		dh:                    deliver.NewHandler(chainManager, timeWindow, mutualTLS, metrics, false),
		policyCheckerProvider: policyCheckerProvider,
	}
}

这个server对象最终实现了DeliverDeliverFiltered接口,实现了DeliverServer

DeliverFiltered接口的实现

上篇文章中说到,cmd 最终建立的是DeliverFiltered流,因此最终会走到serverDeliverFiltered方法,看下这个方法的实现,在core/peer/deliverevents.go的101行:

// Deliver sends a stream of blocks to a client after commitment
func (s *server) DeliverFiltered(srv peer.Deliver_DeliverFilteredServer) error {
	logger.Debugf("Starting new DeliverFiltered handler")
	defer dumpStacktraceOnPanic()
	// getting policy checker based on resources.Event_FilteredBlock resource name
	deliverServer := &deliver.Server{
		Receiver:      srv,
		PolicyChecker: s.policyCheckerProvider(resources.Event_FilteredBlock),
		ResponseSender: &filteredBlockResponseSender{
			Deliver_DeliverFilteredServer: srv,
		},
	}
  // 执行Handle方法处理
	return s.dh.Handle(srv.Context(), deliverServer)
}

Handle() 方法

最终执行的是Handle()方法,看一下,在common/deliver/deliver.go的152行:

// Handle receives incoming deliver requests.
func (h *Handler) Handle(ctx context.Context, srv *Server) error {
	addr := util.ExtractRemoteAddress(ctx)
	logger.Debugf("Starting new deliver loop for %s", addr)
  // 指标相关
	h.Metrics.StreamsOpened.Add(1)
	defer h.Metrics.StreamsClosed.Add(1)
  // 死循环从客户端获取消息
	for {
		logger.Debugf("Attempting to read seek info message from %s", addr)
		envelope, err := srv.Recv()
		if err == io.EOF {
			logger.Debugf("Received EOF from %s, hangup", addr)
			return nil
		}
		if err != nil {
			logger.Warningf("Error reading from %s: %s", addr, err)
			return err
		}
		
    // 主要的执行函数deliverBlocks
		status, err := h.deliverBlocks(ctx, srv, envelope)
		if err != nil {
			return err
		}
		
    // 响应statusResponse
		err = srv.SendStatusResponse(status)
		if status != cb.Status_SUCCESS {
			return err
		}
		if err != nil {
			logger.Warningf("Error sending to %s: %s", addr, err)
			return err
		}
		
    // 等待新的SeekInfo信息
		logger.Debugf("Waiting for new SeekInfo from %s", addr)
	}
}

deliverBlocks() 方法

主要逻辑都在deliverBlocks()中,在common/deliver/deliver.go的194行:

func (h *Handler) deliverBlocks(ctx context.Context, srv *Server, envelope *cb.Envelope) (status cb.Status, err error) {
	addr := util.ExtractRemoteAddress(ctx)
  // 获取消息的payload,出错就返回Status_BAD_REQUEST状态
	payload, err := utils.UnmarshalPayload(envelope.Payload)
	if err != nil {
		logger.Warningf("Received an envelope from %s with no payload: %s", addr, err)
		return cb.Status_BAD_REQUEST, nil
	}
	
  // 如果payload.Header为空就返回Status_BAD_REQUEST状态
	if payload.Header == nil {
		logger.Warningf("Malformed envelope received from %s with bad header", addr)
		return cb.Status_BAD_REQUEST, nil
	}
	
  // 获取channelHeader,出错就返回Status_BAD_REQUEST状态
	chdr, err := utils.UnmarshalChannelHeader(payload.Header.ChannelHeader)
	if err != nil {
		logger.Warningf("Failed to unmarshal channel header from %s: %s", addr, err)
		return cb.Status_BAD_REQUEST, nil
	}
  // 验证ChannelHeader,出错就返回Status_BAD_REQUEST状态
  err = h.validateChannelHeader(ctx, chdr)
	if err != nil {
		logger.Warningf("Rejecting deliver for %s due to envelope validation error: %s", addr, err)
		return cb.Status_BAD_REQUEST, nil
	}
  
  // 获取通道信息,也就是链信息,这块不展开了
  chain := h.ChainManager.GetChain(chdr.ChannelId)
	if chain == nil {
		// 通道找不到,就返回Status_NOT_FOUND状态
		logger.Debugf("Rejecting deliver for %s because channel %s not found", addr, chdr.ChannelId)
		return cb.Status_NOT_FOUND, nil
	}
  
  labels := []string{
		"channel", chdr.ChannelId,
    // 这里filtered为true
		"filtered", strconv.FormatBool(isFiltered(srv)),
	}
  // 指标相关
  h.Metrics.RequestsReceived.With(labels...).Add(1)
	defer func() {
		labels := append(labels, "success", strconv.FormatBool(status == cb.Status_SUCCESS))
		h.Metrics.RequestsCompleted.With(labels...).Add(1)
	}()
  
  // 获取SeekInfo,出错就返回Status_BAD_REQUEST状态
  seekInfo := &ab.SeekInfo{}
	if err = proto.Unmarshal(payload.Data, seekInfo); err != nil {
		logger.Warningf("[channel: %s] Received a signed deliver request from %s with malformed seekInfo payload: %s", chdr.ChannelId, addr, err)
		return cb.Status_BAD_REQUEST, nil
	}
  
  erroredChan := chain.Errored()
	if seekInfo.ErrorResponse == ab.SeekInfo_BEST_EFFORT {
		// 在SeekInfo_BEST_EFFORT时(表示尽力而为),设置erroredChan = nil
    // 这样下面就会走default分支,也就是什么都不做
		erroredChan = nil
	}
	select {
	case <-erroredChan:
    // 如果出错,返回Status_SERVICE_UNAVAILABLE状态
		logger.Warningf("[channel: %s] Rejecting deliver request for %s because of consenter error", chdr.ChannelId, addr)
		return cb.Status_SERVICE_UNAVAILABLE, nil
	default:
	}
  
  // 创建一个SessionAccessControl对象,出错就返回Status_BAD_REQUEST状态
  accessControl, err := NewSessionAC(chain, envelope, srv.PolicyChecker, chdr.ChannelId, h.ExpirationCheckFunc)
	if err != nil {
		logger.Warningf("[channel: %s] failed to create access control object due to %s", chdr.ChannelId, err)
		return cb.Status_BAD_REQUEST, nil
	}
  
  // 评估ACL(访问控制权限)相关的一些东西
  // 如果没有权限,就会返回Status_FORBIDDEN状态
  if err := accessControl.Evaluate(); err != nil {
		logger.Warningf("[channel: %s] Client authorization revoked for deliver request from %s: %s", chdr.ChannelId, addr, err)
		return cb.Status_FORBIDDEN, nil
	}
  
  // 如果seekInfo的Start或者是Stop为nil,返回Status_BAD_REQUEST状态
  if seekInfo.Start == nil || seekInfo.Stop == nil {
		logger.Warningf("[channel: %s] Received seekInfo message from %s with missing start or stop %v, %v", chdr.ChannelId, addr, seekInfo.Start, seekInfo.Stop)
		return cb.Status_BAD_REQUEST, nil
	}
  
  logger.Debugf("[channel: %s] Received seekInfo (%p) %v from %s", chdr.ChannelId, seekInfo, seekInfo, addr)
	
  // 这里应该是获取通道账本的迭代器,用来迭代访问区块
  // 迭代器从seekInfo.Start开始,number为起始块号
  // 这块逻辑跟账本有关,就不展开了
	cursor, number := chain.Reader().Iterator(seekInfo.Start)
	defer cursor.Close()
  
  var stopNum uint64
  // 判断seekInfo.Stop的类型
	switch stop := seekInfo.Stop.Type.(type) {
	case *ab.SeekPosition_Oldest:
    // 如果seekInfo.Stop的类型是SeekPosition_Oldest
    // 那就就将stopNum设置为迭代器迭代的起始块号
		stopNum = number
	case *ab.SeekPosition_Newest:
		// 如果seekInfo.Stop的类型是SeekPosition_Start
    // 如果seekInfo.Start的类型也是SeekPosition_Start,那就就将stopNum设置为迭代器迭代的起始块号
    // 否则,stopNum设置为当前账本高度-1
    // 注意需要-1,否则可能会返回多个块,可能预期的只是一个块
		if proto.Equal(seekInfo.Start, seekInfo.Stop) {
			stopNum = number
			break
		}
		stopNum = chain.Reader().Height() - 1
	case *ab.SeekPosition_Specified:
    // 如果seekInfo.Stop的类型是SeekPosition_Specified
    // 那么就将stopNum设置为指定的number
    // 如果指定的stopnum比迭代器起始块的高度还要小,那么就报错
		stopNum = stop.Specified.Number
		if stopNum < number {
			logger.Warningf("[channel: %s] Received invalid seekInfo message from %s: start number %d greater than stop number %d", chdr.ChannelId, addr, number, stopNum)
			return cb.Status_BAD_REQUEST, nil
		}
	}
  
  // 死循环
  for {
    // 如果seekInfo.Behavior是SeekInfo_FAIL_IF_NOT_READY
    // 表示如果块没有准备好久返回错误
		if seekInfo.Behavior == ab.SeekInfo_FAIL_IF_NOT_READY {
			if number > chain.Reader().Height()-1 {
        // 如果当前迭代起始块大于当前区块高度-1,表示找不到,返回Status_NOT_FOUND状态
				return cb.Status_NOT_FOUND, nil
			}
		}
		
    // 定义两个变量
		var block *cb.Block
		var status cb.Status
		
    // 迭代结束通道,发送迭代完成通知
		iterCh := make(chan struct{})
		go func() {
      // 不停的迭代,调用next
			block, status = cursor.Next()
			close(iterCh)
		}()

		select {
		case <-ctx.Done():
      // 表示在区块取回之间上下午退出了,返回Status_INTERNAL_SERVER_ERROR状态
			logger.Debugf("Context canceled, aborting wait for next block")
			return cb.Status_INTERNAL_SERVER_ERROR, errors.Wrapf(ctx.Err(), "context finished before block retrieved")
		case <-erroredChan:
      // TODO, today, the only user of the errorChan is the orderer consensus implementations.  If the peer ever reports
			// this error, we will need to update this error message, possibly finding a way to signal what error text to return.
			// 这块看不太懂这个erroredChan啥意思,好像是共识有关的,给排序节点用的,返回Status_SERVICE_UNAVAILABLE状态
			logger.Warningf("Aborting deliver for request because the backing consensus implementation indicates an error")
			return cb.Status_SERVICE_UNAVAILABLE, nil
		case <-iterCh:
			// Iterator has set the block and status vars
      // 到这表示迭代完了
		}

		if status != cb.Status_SUCCESS {
      // 迭代完status不为Status_SUCCESS则返回对应的status
			logger.Errorf("[channel: %s] Error reading from channel, cause was: %v", chdr.ChannelId, status)
			return status, nil
		}

		// increment block number to support FAIL_IF_NOT_READY deliver behavior
    // 这个值++为下一个循环开始的判断做准备
    // 因为number++以后,number > chain.Reader().Height()-1应该就会成立了
    // 这个时候FAIL_IF_NOT_READY 的行为会返回
		number++
		
    // 继续再评估一下ACL
		if err := accessControl.Evaluate(); err != nil {
			logger.Warningf("[channel: %s] Client authorization revoked for deliver request from %s: %s", chdr.ChannelId, addr, err)
			return cb.Status_FORBIDDEN, nil
		}

		logger.Debugf("[channel: %s] Delivering block [%d] for (%p) for %s", chdr.ChannelId, block.Header.Number, seekInfo, addr)
		
    // 将这次迭代拿到的区块返回,发送失败返回Status_INTERNAL_SERVER_ERROR状态
		if err := srv.SendBlockResponse(block); err != nil {
			logger.Warningf("[channel: %s] Error sending to %s: %s", chdr.ChannelId, addr, err)
			return cb.Status_INTERNAL_SERVER_ERROR, err
		}
		
    //指标设置
		h.Metrics.BlocksSent.With(labels...).Add(1)
		
    //如果block的区块好和stopnum一样,那就要停止了
    //所以一般如果要无限循环的话,stopnum一般就设置为math.MaxInt64了
    //这一点在上一篇文章中提到过
		if stopNum == block.Header.Number {
			break
		}
	}
	
	logger.Debugf("[channel: %s] Done delivering to %s for (%p)", chdr.ChannelId, addr, seekInfo)
	
  // 完成之后还是要返回一个Status_SUCCESS状态
	return cb.Status_SUCCESS, nil
}

代码很长,不过好在没有很多需要跳转的地方。需要注意的一个地方是,发送消息给DeliverClient的时候,只有两种响应类型:

// ResponseSender defines the interface a handler must implement to send
// responses.
type ResponseSender interface {
	SendStatusResponse(status cb.Status) error
	SendBlockResponse(block *cb.Block) error
}

一般拿到一个区块的时候,就会执行SendBlockResponse(),最终返回的响应类型是DeliverResponse_FilteredBlock。在返回一个status时,包括各种错误,或是Status_SUCCESS,都会执行SendStatusResponse(),最终返回的响应类型是DeliverResponse_Status。返回status就代表DeliverServer这边的逻辑处理结束了,需要通知DeliverClientDeliverClient具体怎么处理就看各自实现了。

通过SendBlockResponse() 发送响应

最后看下SendBlockResponse()方法,它是最终返回一个事件响应的方法,代码在core/peer/deliverevents.go的79行:

type blockEvent common.Block

// SendBlockResponse generates deliver response with block message
func (fbrs *filteredBlockResponseSender) SendBlockResponse(block *common.Block) error {
   // blockEvent主要就是用了另一个类型要包装一下commmon.block类型
  // 以便可以自定义一个toFilteredBlock方法
   b := blockEvent(*block)
  // 这块主要就是过滤区块了
   filteredBlock, err := b.toFilteredBlock()
   if err != nil {
      logger.Warningf("Failed to generate filtered block due to: %s", err)
     // 出错调用SendStatusResponse返回错误状态
      return fbrs.SendStatusResponse(common.Status_BAD_REQUEST)
   }
  // 创建响应类型DeliverResponse,type是DeliverResponse_FilteredBlock
   response := &peer.DeliverResponse{
      Type: &peer.DeliverResponse_FilteredBlock{FilteredBlock: filteredBlock},
   }
   return fbrs.Send(response)
}

toFilteredBlock() 方法过滤区块

来看下toFilteredBlock()方法,在core/peer/deliverevents.go的157行:

func (block *blockEvent) toFilteredBlock() (*peer.FilteredBlock, error) {
  // 创建要返回的FilteredBlock对象,赋值Number字段为区块高度
	filteredBlock := &peer.FilteredBlock{
		Number: block.Header.Number,
	}
	
  // 拿到区块元数据中的common.BlockMetadataIndex_TRANSACTIONS_FILTER部分的内容,
  // TxValidationFlags是一个[]uint8类型,这里是做了一个类型转化
	txsFltr := util.TxValidationFlags(block.Metadata.Metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER])
  // block.Data.Data就是每条交易数据
  // txIndex是下标,ebytes是一个[]byte类型,是Envelope类型的序列化内容
	for txIndex, ebytes := range block.Data.Data {
		var env *common.Envelope
		var err error
		
    // 当前交易为空,continue
		if ebytes == nil {
			logger.Debugf("got nil data bytes for tx index %d, "+
				"block num %d", txIndex, block.Header.Number)
			continue
		}
		
    // 获取env对象
		env, err = utils.GetEnvelopeFromBlock(ebytes)
		if err != nil {
			logger.Errorf("error getting tx from block, %s", err)
			continue
		}

		// 获取env的payload,这里出错直接返回,而不是continue
		payload, err := utils.GetPayload(env)
		if err != nil {
			return nil, errors.WithMessage(err, "could not extract payload from envelope")
		}
		
    // payload.Header为空continue
		if payload.Header == nil {
			logger.Debugf("transaction payload header is nil, %d, block num %d",
				txIndex, block.Header.Number)
			continue
		}
    // 获取channelHeader
		chdr, err := utils.UnmarshalChannelHeader(payload.Header.ChannelHeader)
		if err != nil {
			return nil, err
		}
		
    // 设置filteredBlock的ChannelID
		filteredBlock.ChannelId = chdr.ChannelId
		
    // 创建FilteredTransaction对象
    // 包括交易ID,头部类型,以及交易验证码(这个东西还不太懂)
		filteredTransaction := &peer.FilteredTransaction{
			Txid:             chdr.TxId,
			Type:             common.HeaderType(chdr.Type),
			TxValidationCode: txsFltr.Flag(txIndex),
		}
		
    // 如果交易类型是HeaderType_ENDORSER_TRANSACTION
		if filteredTransaction.Type == common.HeaderType_ENDORSER_TRANSACTION {
      // 那么就获取交易的具体数据,得到peer.Transaction对象tx
			tx, err := utils.GetTransaction(payload.Data)
			if err != nil {
				return nil, errors.WithMessage(err, "error unmarshal transaction payload for block event")
			}
			
      // 将tx.Actions(类型为[]*TransactionAction)转化为transactionActions类型(是[]*TransactionAction的别名)
      // 本质上是拿到Actions,事件就存储在交易的Acitons字段中
      // 来看下toFilteredActions方法,过滤事件去了
			filteredTransaction.Data, err = transactionActions(tx.Actions).toFilteredActions()
			if err != nil {
				logger.Errorf(err.Error())
				return nil, err
			}
		}
		
    // 设置filteredBlock的FilteredTransactions字段
		filteredBlock.FilteredTransactions = append(filteredBlock.FilteredTransactions, filteredTransaction)
	}
	
  // 最终返回filteredBlock
	return filteredBlock, nil
}

toFilteredActions() 过滤事件信息

看下toFilteredActions()方法,在core/peer/deliverevents.go的222行:

type FilteredTransactionActions struct {
	ChaincodeActions     []*FilteredChaincodeAction 
	XXX_NoUnkeyedLiteral struct{}                   
	XXX_unrecognized     []byte                     
	XXX_sizecache        int32                      
}

type FilteredChaincodeAction struct {
  // ChaincodeEvent就是链码事件结构体,这个在之前的文章中有提到
	ChaincodeEvent       *ChaincodeEvent 
	XXX_NoUnkeyedLiteral struct{}        
	XXX_unrecognized     []byte          
	XXX_sizecache        int32           
}

func (ta transactionActions) toFilteredActions() (*peer.FilteredTransaction_TransactionActions, error) {
  // 创建一个FilteredTransactionActions对象
	transactionActions := &peer.FilteredTransactionActions{}
  // 本质上就是遍历交易的Actions字段,每个action是Action类型
	for _, action := range ta {
    // 获取action的payload字段
		chaincodeActionPayload, err := utils.GetChaincodeActionPayload(action.Payload)
		if err != nil {
			return nil, errors.WithMessage(err, "error unmarshal transaction action payload for block event")
		}
		
    // chaincodeActionPayload.Action为空跳过
		if chaincodeActionPayload.Action == nil {
			logger.Debugf("chaincode action, the payload action is nil, skipping")
			continue
		}
    
    // 获取chaincodeActionPayload.Action.ProposalResponsePayload
    // 是一个ProposalResponsePayload类型
		propRespPayload, err := utils.GetProposalResponsePayload(chaincodeActionPayload.Action.ProposalResponsePayload)
		if err != nil {
			return nil, errors.WithMessage(err, "error unmarshal proposal response payload for block event")
		}
		
    // 获取propRespPayload.Extension字段,事件就存储在其中
    // 该类型是一个ChaincodeAction类型
		caPayload, err := utils.GetChaincodeAction(propRespPayload.Extension)
		if err != nil {
			return nil, errors.WithMessage(err, "error unmarshal chaincode action for block event")
		}
		
    // 获取caPayload.Events字段,该类型就是一个ChaincodeEvents类型
    // 到这里就拿到事件了
		ccEvent, err := utils.GetChaincodeEvents(caPayload.Events)
		if err != nil {
			return nil, errors.WithMessage(err, "error unmarshal chaincode event for block event")
		}
		
    // 获取链码ID
		if ccEvent.GetChaincodeId() != "" {
      // 其实就是一个事件的拷贝操作
      // 注意这里没有把事件的payload拷贝过去
			filteredAction := &peer.FilteredChaincodeAction{
				ChaincodeEvent: &peer.ChaincodeEvent{
					TxId:        ccEvent.TxId,
					ChaincodeId: ccEvent.ChaincodeId,
					EventName:   ccEvent.EventName,
          // 没有拷贝payload字段
				},
			}
      // 加上这个事件
			transactionActions.ChaincodeActions = append(transactionActions.ChaincodeActions, filteredAction)
		}
	}
  // 最终返回结果
	return &peer.FilteredTransaction_TransactionActions{
		TransactionActions: transactionActions,
	}, nil
}

DeliverFiltered 与 Deliver 的差别

好了到这里就分析完了。细心的朋友到这里其实就会发现一个问题,在做事件拷贝的时候,这一部分并没有将事件的payload字段拷贝过去,这就是DeliverFiltered这个服务做的,它最终把事件的部分内容返回回去了,不包括事件的payload信息,也就是事件的具体内容,只包含了TxId,ChaincodeId,EventName三个事件字段信息。

既然这样,那么Deliver服务做的应该就是完整的事件信息了,可以来看一下,代码在core/peer/deliverevents.go的52行:

// SendBlockResponse generates deliver response with block message
func (brs *blockResponseSender) SendBlockResponse(block *common.Block) error {
	response := &peer.DeliverResponse{
    // 直接将整个block返回
		Type: &peer.DeliverResponse_Block{Block: block},
	}
	return brs.Send(response)
}

这里甚至是直接将整个block返回了,都不带过滤事件的,看这个要从block中获取事件的操作应该要有DeliverClient来做了,这样就可以拿到事件的具体信息。

这个问题其实在protos/peer/events.pb.go中已经说明了:

type DeliverResponse_Block struct {
  // 这里直接返回
	Block *common.Block `protobuf:"bytes,2,opt,name=block,proto3,oneof"`
}

type DeliverResponse_FilteredBlock struct {
  // 这里过滤事件后返回
	FilteredBlock *FilteredBlock `protobuf:"bytes,3,opt,name=filtered_block,json=filteredBlock,proto3,oneof"`
}

很多SDK提供了类似BLOCK_FILTERED的标志来让用户选择是注册DeliverFiltered还是Deliver服务,针对不同的类型做出不同的措施。

建议事件如果只是做一个通知的作用,采用DeliverFiltered服务会更轻量,如果需要了解事件中的具体内容,则必须使用Deliver服务

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值