Hyperledger Fabric从源码分析链码容器启动过程

链码容器启动过程

每个实例化之后的链码都会以容器的形式启动起来,下面举一个byfn.sh启动的例子:

$ docker ps
CONTAINER ID        IMAGE                                                                                                       COMMAND                  CREATED             STATUS              
a908ea5fa0ee        dev-peer1.org2.example.com-mycc-1.0-26c2ef32838554aac4f7ad6f100aca865e87959c9a126e86d764c8d01f8346ab        "chaincode -peer.add…"   42 hours ago        Up 42 hours                                                          dev-peer1.org2.example.com-mycc-1.0
9495cf13bc81        dev-peer0.org1.example.com-mycc-1.0-384f11f484b9302df90b453200cfb25174305fce8f53f4e94d45ee3b6cab0ce9        "chaincode -peer.add…"   42 hours ago        Up 42 hours                                                          dev-peer0.org1.example.com-mycc-1.0
d7d5c7912f71        dev-peer0.org2.example.com-mycc-1.0-15b571b3ce849066b7ec74497da3b27e54e0df1345daff3951b94245ce09c42b        "chaincode -peer.add…"   42 hours ago        Up 42 hours                                                          dev-peer0.org2.example.com-mycc-1.0

容器启动以后执行的命令是:

chaincode -peer.address [peer地址]

该命令会执行链码的main函数,一般来说链码的main函数都是下面的样子:

func main() {
	err := shim.Start(new(SimpleChaincode))
	if err != nil {
		fmt.Printf("Error starting Simple chaincode: %s", err)
	}
}

主要是执行了shim.Start()方法,这是链码容器启动的关键函数,今天就来解析一个这个函数到底做了一下什么事。

梳理一下链码启动的整个过程:

  1. 链码安装
  2. 链码实例化
    1. 实例化模拟提案中由peer节点执行ProcessProposal()
    2. 其中有个环节会启动链码容器(这里暂时不展开讨论,涉及到的代码较多)
    3. 链码容器启动时执行命令chaincode -peer.address [peer地址]
    4. 该命令启动后执行链码源代码的main函数
    5. main函数中调用shim.Start()方法完成链码的启动

shim.Start 方法

看看这个方法具体做了什么吧,在core/chaincode/shim/chaincode.go的132行

// chaincodes.
func Start(cc Chaincode) error {
  // 做一个链码日志的设置
	SetupChaincodeLogging()
  // 从环境变量中获取chaincodeName,如下
  // CORE_CHAINCODE_ID_NAME=mycc:1.0
	chaincodename := viper.GetString("chaincode.id.name")
	if chaincodename == "" {
		return errors.New("error chaincode id not provided")
	}
	
  // 创建一个工厂
	err := factory.InitFactories(factory.GetDefaultOpts())
	if err != nil {
		return errors.WithMessage(err, "internal error, BCCSP could not be initialized with default options")
	}

	// 设置streamGetter为userChaincodeStreamGetter
  // 即将用户链码以流的方式读取进来
	if streamGetter == nil {
		streamGetter = userChaincodeStreamGetter
	}
	
  // 执行上面设置的streamGetter,参数传一个chaincodename
	stream, err := streamGetter(chaincodename)
	if err != nil {
		return err
	}
	
  // 这个函数很重要,与peer节点聊天,即与peer节点通信
  // 链码容器通过这个函数与安装到的peer节点进行通信
	err = chatWithPeer(chaincodename, stream, cc)

	return err
}

Start()方法主体逻辑很简单,它由几个重要的函数构成:

  • SetupChaincodeLogging(),设置日志
  • InitFactories(),创建工厂
  • userChaincodeStreamGetter(),将用户链码以流的方式拿到
  • chatWithPeer(),与peer节点通信

设置日志的函数我们就不看了,主要是从环境变量中获取一些日志设置。下面就来看下剩下的三个函数究竟做了什么

InitFactories()

先看下这个函数的参数GetDefaultOpts(),在bccsp/factory/opts.go的20行:

// GetDefaultOpts offers a default implementation for Opts
// returns a new instance every time
func GetDefaultOpts() *FactoryOpts {
	return &FactoryOpts{
		ProviderName: "SW",
		SwOpts: &SwOpts{
			HashFamily: "SHA2",
			SecLevel:   256,

			Ephemeral: true,
		},
	}
}

它返回了一个FactoryOpts对象实例,可以看到该对象主要是一些加密有关的字段,这里不做展开了。这样可以猜想,InitFactories()方法,也是做了一些链码设置加密操作的一些内容,这里就不展开看了,有兴趣的可以去看下,代码在bccsp/factory/nopkcs11.go

userChaincodeStreamGetter()

接下来就是userChaincodeStreamGetter()方法了,在core/chaincode/shim/chaincode.go的82行:

//the non-mock user CC stream establishment func
func userChaincodeStreamGetter(name string) (PeerChaincodeStream, error) {
  // 这一行就是读取链码容器启动执行的命令的那个参数的,拿到peer地址
	flag.StringVar(&peerAddress, "peer.address", "", "peer address")
	if viper.GetBool("peer.tls.enabled") {
    // 下面是一些有关tls的几个操作
    // 获取tls密钥地址,在用户安装链码的时候指定 
		keyPath := viper.GetString("tls.client.key.path")
    // 获取tls证书地址
		certPath := viper.GetString("tls.client.cert.path")
		
    // 读取秘钥数据
		data, err1 := ioutil.ReadFile(keyPath)
		if err1 != nil {
			err1 = errors.Wrap(err1, fmt.Sprintf("error trying to read file content %s", keyPath))
			chaincodeLogger.Errorf("%+v", err1)
			return nil, err1
		}
		key = string(data)
		
    // 读取证书数据
		data, err1 = ioutil.ReadFile(certPath)
		if err1 != nil {
			err1 = errors.Wrap(err1, fmt.Sprintf("error trying to read file content %s", certPath))
			chaincodeLogger.Errorf("%+v", err1)
			return nil, err1
		}
		cert = string(data)
	}

	flag.Parse()

	chaincodeLogger.Debugf("Peer address: %s", getPeerAddress())

	// 与peer节点建立连接
	clientConn, err := newPeerClientConnection()
	if err != nil {
		err = errors.Wrap(err, "error trying to connect to local peer")
		chaincodeLogger.Errorf("%+v", err)
		return nil, err
	}

来看下newPeerClientConnection()方法,在core/chaincode/shim/chaincode.go的308行:

func newPeerClientConnection() (*grpc.ClientConn, error) {
  // 拿到peerAddress,那是一个全局变量,如果是空会从环境变量中拿
  // 如果还是拿不到就会报错
	var peerAddress = getPeerAddress()
	
  // 设置一些与peer节点的keepalive参数
	kaOpts := &comm.KeepaliveOptions{
		ClientInterval: time.Duration(1) * time.Minute,
		ClientTimeout:  time.Duration(20) * time.Second,
	}
  // 根据是否设置了tls,传入不同的参数,执行NewClientConnectionWithAddres方法
	if viper.GetBool("peer.tls.enabled") {
		return comm.NewClientConnectionWithAddress(peerAddress, true, true,
			comm.InitTLSForShim(key, cert), kaOpts)
	}
	return comm.NewClientConnectionWithAddress(peerAddress, true, false, nil, kaOpts)
}

来看下NewClientConnectionWithAddres()方法,在core/comm/connection.go的206行:

// NewClientConnectionWithAddress Returns a new grpc.ClientConn to the given address
func NewClientConnectionWithAddress(peerAddress string, block bool, tslEnabled bool,
	creds credentials.TransportCredentials, ka *KeepaliveOptions) (*grpc.ClientConn, error) {
  // 参数:1.peer地址 2.是否阻塞等待dial返回 3.是否开启tls 4.tls配置(不开则传nil) 5.keepalive参数
  // 创建一个grpc的拨号参数
	var opts []grpc.DialOption
	
  // 设置keepalive部分参数
	if ka != nil {
		opts = ClientKeepaliveOptions(ka)
	} else {
		// set to the default options
		opts = ClientKeepaliveOptions(DefaultKeepaliveOptions)
	}
	
  // 设置tls部分参数
	if tslEnabled {
		opts = append(opts, grpc.WithTransportCredentials(creds))
	} else {
		opts = append(opts, grpc.WithInsecure())
	}
  // 设置block参数
	if block {
		opts = append(opts, grpc.WithBlock())
	}
  // 设置最大接收消息大小,最大发送消息大小
	opts = append(opts, grpc.WithDefaultCallOptions(
		grpc.MaxCallRecvMsgSize(MaxRecvMsgSize),
		grpc.MaxCallSendMsgSize(MaxSendMsgSize),
	))
  // 设置一个超时上下文
	ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)
	defer cancel()
  // grpc拨号连接
	conn, err := grpc.DialContext(ctx, peerAddress, opts...)
	if err != nil {
		return nil, err
	}
  // 返回连接
	return conn, err
}

回到userChaincodeStreamGetter()中:

	// 刚刚看到这里
	clientConn, err := newPeerClientConnection()
	if err != nil {
		err = errors.Wrap(err, "error trying to connect to local peer")
		chaincodeLogger.Errorf("%+v", err)
		return nil, err
	}

	chaincodeLogger.Debugf("os.Args returns: %s", os.Args)
	// 创建chaincodeSupport客户端,就一个字段clientConn
	// chaincodeSupport服务在peer端有启动
	chaincodeSupportClient := pb.NewChaincodeSupportClient(clientConn)
	// 注册chaincodeSupport客户端
	stream, err := chaincodeSupportClient.Register(context.Background())
	if err != nil {
		return nil, errors.WithMessage(err, fmt.Sprintf("error chatting with leader at address=%s", getPeerAddress()))
	}

	return stream, nil
}

chaincodeSupportClient.Register()部分源代码在protos/peer/chaincode_shim.pb.go的1016行:

type ChaincodeSupportClient interface {
	Register(ctx context.Context, opts ...grpc.CallOption) (ChaincodeSupport_RegisterClient, error)
}

type chaincodeSupportClient struct {
	cc *grpc.ClientConn
}

func NewChaincodeSupportClient(cc *grpc.ClientConn) ChaincodeSupportClient {
	return &chaincodeSupportClient{cc}
}

func (c *chaincodeSupportClient) Register(ctx context.Context, opts ...grpc.CallOption) (ChaincodeSupport_RegisterClient, error) {
	stream, err := c.cc.NewStream(ctx, &_ChaincodeSupport_serviceDesc.Streams[0], "/protos.ChaincodeSupport/Register", opts...)
	if err != nil {
		return nil, err
	}
	x := &chaincodeSupportRegisterClient{stream}
	return x, nil
}

type ChaincodeSupport_RegisterClient interface {
	Send(*ChaincodeMessage) error
	Recv() (*ChaincodeMessage, error)
	grpc.ClientStream
}

type chaincodeSupportRegisterClient struct {
	grpc.ClientStream
}

chaincodeSupportRegisterClient发送和接收的消息类型是ChaincodeMessage,这里提一下这个消息类型,后续会经常看到这个类型,它是链码容器与peer节点传输的消息类型

userChaincodeStreamGetter()方法到这里就结束了。

chatWithPeer()

最后一个方法了,这个方法是一个大头,userChaincodeStreamGetter()方法已经完成了与peer节点的通信了,它返回了一个stram对象,是一个接口PeerChaincodeStream,只提供了三个方法:

// PeerChaincodeStream interface for stream between Peer and chaincode instance.
type PeerChaincodeStream interface {
	Send(*pb.ChaincodeMessage) error
	Recv() (*pb.ChaincodeMessage, error)
	CloseSend() error
}

stream传给chatWithPeer作为参数

if streamGetter == nil {
   streamGetter = userChaincodeStreamGetter
}

stream, err := streamGetter(chaincodename)
if err != nil {
   return err
}

// cc即为链码对象,实现了Init和Invoke接口
err = chatWithPeer(chaincodename, stream, cc)

看下chatWithPeer()吧,在core/chaincode/shim/chaincode.go322行:

func chatWithPeer(chaincodename string, stream PeerChaincodeStream, cc Chaincode) error {
	// Create the shim handler responsible for all control logic
	handler := newChaincodeHandler(stream, cc)
	defer stream.CloseSend()

第一步就是创建一个ChaincodeHandler对象,这是一个很重要的对象,链码容器与peer节点的交互本质上就是两边的ChaincodeHandler在通过GRPC通信不断地发送和介绍消息。看下newChaincodeHandler()方法,在core/chaincode/shim/handler.go的166行:

// Handler handler implementation for shim side of chaincode.
type Handler struct {
  // 需要锁来保护链码,防止peer的并发请求
	sync.Mutex 
	// 序列化锁,在serialSend时用
	serialLock sync.Mutex
	To         string
  // grpc流,就是userChaincodeStreamGetter()创建的那个stream
	ChatStream PeerChaincodeStream
  // 链码对象
	cc         Chaincode
  // handler状态,有created,ready,established三种状态
	state      state
	// 链码信息响应通道
	responseChannel map[string]chan pb.ChaincodeMessage
}

// NewChaincodeHandler returns a new instance of the shim side handler.
func newChaincodeHandler(peerChatStream PeerChaincodeStream, chaincode Chaincode) *Handler {
	v := &Handler{
		ChatStream: peerChatStream,
		cc:         chaincode,
	}
	v.responseChannel = make(map[string]chan pb.ChaincodeMessage)
	v.state = created
	return v
}

回到chatWithPeer():

	// ....
	// Send the ChaincodeID during register.
	// 创建ChaincodeID对象
	chaincodeID := &pb.ChaincodeID{Name: chaincodename}
	payload, err := proto.Marshal(chaincodeID)
	if err != nil {
		return errors.Wrap(err, "error marshalling chaincodeID during chaincode registration")
	}

	// Register on the stream
	chaincodeLogger.Debugf("Registering.. sending %s", pb.ChaincodeMessage_REGISTER)
	// 这里就开始发送第一个流信息了
	// 类型是ChaincodeMessage_REGISTER,payload主要就是包含chaincodeName
	// 主要是用于注册,serialSend函数很简单,主要就是调用stream流的send方法发送消息
	if err = handler.serialSend(&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_REGISTER, Payload: payload}); err != nil {
		return errors.WithMessage(err, "error sending chaincode REGISTER")
	}
	
	// holds return values from gRPC Recv below
	type recvMsg struct {
		msg *pb.ChaincodeMessage
		err error
	}
	msgAvail := make(chan *recvMsg, 1)
	errc := make(chan error)
	
	// 这个函数就是从grpc中接收对端消息了
	receiveMessage := func() {
		in, err := stream.Recv()
		msgAvail <- &recvMsg{in, err}
	}
	
	// 下面这块逻辑就是死循环不断地从grpc中接收消息
	go receiveMessage()
	for {
		select {
		case rmsg := <-msgAvail:
      // 从msgAvail中拿取接收到的消息
			switch {
			case rmsg.err == io.EOF:
        // 接收到EOF表示对端关闭了,这边就退出结束并退出
				err = errors.Wrapf(rmsg.err, "received EOF, ending chaincode stream")
				chaincodeLogger.Debugf("%+v", err)
				return err
			case rmsg.err != nil:
        // err不为空表示接收出错了,返回err
				err := errors.Wrap(rmsg.err, "receive failed")
				chaincodeLogger.Errorf("Received error from server, ending chaincode stream: %+v", err)
				return err
			case rmsg.msg == nil:
        // 接收到空的msg了,返回err
				err := errors.New("received nil message, ending chaincode stream")
				chaincodeLogger.Debugf("%+v", err)
				return err
			default:
        // 默认情况是正常接收
				chaincodeLogger.Debugf("[%s]Received message %s from peer", shorttxid(rmsg.msg.Txid), rmsg.msg.Type)
        // 处理对端发过来的消息
				err := handler.handleMessage(rmsg.msg, errc)
				if err != nil {
					err = errors.WithMessage(err, "error handling message")
					return err
				}
				
        // 处理完以后重新起一个协程去监听,这里是不是搞一个协程池好一点?
				go receiveMessage()
         // 回到主循环继续从grpc中接收消息
			}
		// 如果handleMessage后续发送过程出错了,会往errc中发送err
    // 可以看到handler.handleMessage第二个参数是errc
		case sendErr := <-errc:
			if sendErr != nil {
				err := errors.Wrap(sendErr, "error sending")
				return err
			}
		}
	}
}

chatWithPeer()方法到这里就结束了,主要就是不断地发送消息,接收消息,处理接收到的消息,继续发送消息的过程,这也就是我之前讲为什么链码与peer节点交互的过程本质上是两个ChaincodeHandler之间在不断收发处理消息的过程,这个消息的类型就是ChaincodeMessage类型。先来简单看下这个类型:

type ChaincodeMessage struct {
  // 消息类型
	Type      ChaincodeMessage_Type 
  // 时间戳
	Timestamp *timestamp.Timestamp  
  // payload
	Payload   []byte                
  // 交易ID
	Txid      string                
  // 签名提案
	Proposal  *SignedProposal       
  // 链码事件
	ChaincodeEvent *ChaincodeEvent 
	// channel id
	ChannelId            string   
	XXX_NoUnkeyedLiteral struct{}
	XXX_unrecognized     []byte  
	XXX_sizecache        int32   
}

因此,到这里,后续的工作就是分析链码容器与peer节点两侧的ChaincodeHandler具体是怎么处理消息的就好了。

chatWithPeer()中,链码侧发送了第一个消息给peer节点,类型为ChaincodeMessage_REGISTER,下面看下peer节点这边做了什么

Peer节点处理流程

peer节点的handler的具体实现在core/chaincode/handler.go中,注意链码侧的handler实现在core/chaincode/shim/handler.go中。具体的处理流程函数在402行ProcessStream()中。

我们暂时不关心peer节点中的handler流程是如何起来的。不过我追踪了一下这个函数的调用,好像也是在处理链码实例化提案的时候起来了。

也就是说,在peer节点执行链码实例化的过程中,不仅仅启动了链码容器,也启动了本地与链码容器进行通信的handler,都是在执行ChaincodeSupport.Launch()方法的时候启动的。

上述结论不一定是对的,但是我们暂时不关心,后续有机会可以解析一下这块的逻辑,总之我们找到了peer节点的handler的处理流程,下面就先来看一下吧:

func (h *Handler) ProcessStream(stream ccintf.ChaincodeStream) error {
  // 执行完之后注销
	defer h.deregister()

	h.mutex.Lock()
	h.streamDoneChan = make(chan struct{})
	h.mutex.Unlock()
	defer close(h.streamDoneChan)

	h.chatStream = stream
	h.errChan = make(chan error, 1)
	
  // keepalive通道
	var keepaliveCh <-chan time.Time
	if h.Keepalive != 0 {
		ticker := time.NewTicker(h.Keepalive)
		defer ticker.Stop()
		keepaliveCh = ticker.C
	}
	
  // ===============================
	// 下面这块几乎和链码侧的处理流程一模一样
	type recvMsg struct {
		msg *pb.ChaincodeMessage
		err error
	}
	msgAvail := make(chan *recvMsg, 1)

	receiveMessage := func() {
		in, err := h.chatStream.Recv()
		msgAvail <- &recvMsg{in, err}
	}

	go receiveMessage()
	for {
		select {
		case rmsg := <-msgAvail:
			switch {
			// Defer the deregistering of the this handler.
			case rmsg.err == io.EOF:
				chaincodeLogger.Debugf("received EOF, ending chaincode support stream: %s", rmsg.err)
				return rmsg.err
			case rmsg.err != nil:
				err := errors.Wrap(rmsg.err, "receive failed")
				chaincodeLogger.Errorf("handling chaincode support stream: %+v", err)
				return err
			case rmsg.msg == nil:
				err := errors.New("received nil message, ending chaincode support stream")
				chaincodeLogger.Debugf("%+v", err)
				return err
			default:
				err := h.handleMessage(rmsg.msg)
				if err != nil {
					err = errors.WithMessage(err, "error handling message, ending stream")
					chaincodeLogger.Errorf("[%s] %+v", shorttxid(rmsg.msg.Txid), err)
					return err
				}

				go receiveMessage()
			}

		case sendErr := <-h.errChan:
			err := errors.Wrapf(sendErr, "received error while sending message, ending chaincode support stream")
			chaincodeLogger.Errorf("%s", err)
			return err
		case <-keepaliveCh:
			// 发送一个keepalive消息
			h.serialSendAsync(&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_KEEPALIVE})
			continue
		}
	}
}

大体上和链码侧的处理逻辑几乎相同,最终也是走到了handleMessage()方法中,看下这个方法,在core/chaincode/handler.go的178行:

// handleMessage is called by ProcessStream to dispatch messages.
func (h *Handler) handleMessage(msg *pb.ChaincodeMessage) error {
	chaincodeLogger.Debugf("[%s] Fabric side handling ChaincodeMessage of type: %s in state %s", shorttxid(msg.Txid), msg.Type, h.state)
	// 判断是不是心跳消息,如果是就什么都不做
	if msg.Type == pb.ChaincodeMessage_KEEPALIVE {
		return nil
	}
	
  // 这里的state的赋值和链码侧的不一样
  // 链码侧的赋值用的string类型,而peer这边用的int类型
  // handler在创建的时候没有给state赋值,state就是默认零值0
  // 而Created状态对应的就是0,Established是1,Ready是2
	switch h.state {
	case Created:
    // 因此,在peerHandler接收到第一条消息时,会走这个case
		return h.handleMessageCreatedState(msg)
	case Ready:
		return h.handleMessageReadyState(msg)
	default:
		return errors.Errorf("handle message: invalid state %s for transaction %s", h.state, msg.Txid)
	}
}

看下handleMessageCreatedState()方法,因为走的是这个case,在core/chaincode/handler.go的195行:

func (h *Handler) handleMessageCreatedState(msg *pb.ChaincodeMessage) error {
	switch msg.Type {
    // 之前在看链码侧时,链码侧发送的第一个消息的类型的就是ChaincodeMessage_REGISTER
	case pb.ChaincodeMessage_REGISTER:
		h.HandleRegister(msg)
	default:
    // handleMessageCreatedState只接收ChaincodeMessage_REGISTER这个消息类型
		return fmt.Errorf("[%s] Fabric side handler cannot handle message (%s) while in created state", msg.Txid, msg.Type)
	}
	return nil
}

看下HandleRegister()方法,因为走的是这个case,在core/chaincode/handler.go的508行:

// handleRegister is invoked when chaincode tries to register.
func (h *Handler) HandleRegister(msg *pb.ChaincodeMessage) {
	chaincodeLogger.Debugf("Received %s in state %s", msg.Type, h.state)
  // 从消息中获取payload,主要就是链码ID
	chaincodeID := &pb.ChaincodeID{}
	err := proto.Unmarshal(msg.Payload, chaincodeID)
	if err != nil {
		chaincodeLogger.Errorf("Error in received %s, could NOT unmarshal registration info: %s", pb.ChaincodeMessage_REGISTER, err)
		return
	}

	// Now register with the chaincodeSupport
	h.chaincodeID = chaincodeID
  // 将链码注册到peer节点上
	err = h.Registry.Register(h)
	if err != nil {
    // 该函数在注册成功后发送,如果err不为空会报错
		h.notifyRegistry(err)
		return
	}

	// 解析链码名字,包括链码name,version,以及链码id
	h.ccInstance = ParseName(h.chaincodeID.Name)

	chaincodeLogger.Debugf("Got %s for chaincodeID = %s, sending back %s", pb.ChaincodeMessage_REGISTER, chaincodeID, pb.ChaincodeMessage_REGISTERED)
  // serialSend顺序发送数据,即往链码侧发送消息了
  // 消息类型为ChaincodeMessage_REGISTERED标识已经注册
	if err := h.serialSend(&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_REGISTERED}); err != nil {
		chaincodeLogger.Errorf("error sending %s: %s", pb.ChaincodeMessage_REGISTERED, err)
		h.notifyRegistry(err)
		return
	}
	
  // 将当前handler的状态改为Established
	h.state = Established

	chaincodeLogger.Debugf("Changed state to established for %+v", h.chaincodeID)

	// 这个函数一会看一下,它最终还会将handler的状态更新为ready
	h.notifyRegistry(nil)
}

上述流程很简单,主要就是将链码注册到peer节点上,并发送ChaincodeMessage_REGISTERED消息给链码侧,同时更新handler的状态为Established,最后调用h.notifyRegistry(nil)handler的状态置为Ready

有两个方法看一下,一个是Register()方法用于注册:

func (r *HandlerRegistry) Register(h *Handler) error {
	r.mutex.Lock()
	defer r.mutex.Unlock()
	key := h.chaincodeID.Name

	if r.handlers[key] != nil {
		chaincodeLogger.Debugf("duplicate registered handler(key:%s) return error", key)
		return errors.Errorf("duplicate chaincodeID: %s", h.chaincodeID.Name)
	}

	// This chaincode was not launched by the peer but is attempting
	// to register. Only allowed in development mode.
	if r.launching[key] == nil && !r.allowUnsolicitedRegistration {
		return errors.Errorf("peer will not accept external chaincode connection %v (except in dev mode)", h.chaincodeID.Name)
	}
	
  // 将handler注册到map中,key就是chaincodeID
	r.handlers[key] = h

	chaincodeLogger.Debugf("registered handler complete for chaincode %s", key)
	return nil
}

还有一个是notifyRegistry()方法:

// notifyRegistry will send ready on registration success and
// update the launch state of the chaincode in the handler registry.
func (h *Handler) notifyRegistry(err error) {
	if err == nil {
		err = h.sendReady()
	}

	if err != nil {
		h.Registry.Failed(h.chaincodeID.Name, err)
		chaincodeLogger.Errorf("failed to start %s", h.chaincodeID)
		return
	}

	h.Registry.Ready(h.chaincodeID.Name)
}

// sendReady sends READY to chaincode serially (just like REGISTER)
func (h *Handler) sendReady() error {
	chaincodeLogger.Debugf("sending READY for chaincode %+v", h.chaincodeID)
	ccMsg := &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_READY}

	// if error in sending tear down the h
	if err := h.serialSend(ccMsg); err != nil {
		chaincodeLogger.Errorf("error sending READY (%s) for chaincode %+v", err, h.chaincodeID)
		return err
	}

	h.state = Ready

	chaincodeLogger.Debugf("Changed to state ready for chaincode %+v", h.chaincodeID)

	return nil
}

因此,在HandleRegister()方法最后一行执行h.notifyRegistry(nil)以后,因为err==nil,会执行sendReady()方法,并最终再发送一个ChaincodeMessage_READY消息给链码侧,同时将handler自身的state改为Ready

如果err!=nil,会输出一个错误日志,表示这次失败。

到这里peer节点handler的处理流程就执行完了,到最后发送了两个消息给链码侧:

  1. 首先peer节点接收到链码侧发送来的ChaincodeMessage_REGISTER以后,响应一个ChaincodeMessage_REGISTERED消息,更新handler的状态为Established
  2. 之后再发送一个ChaincodeMessage_READY消息给链码侧,更新handler的状态为Ready

链码侧的处理流程

我们回到链码侧的handleMessage()方法:

func (handler *Handler) handleMessage(msg *pb.ChaincodeMessage, errc chan error) error {
	if msg.Type == pb.ChaincodeMessage_KEEPALIVE {
		chaincodeLogger.Debug("Sending KEEPALIVE response")
    // 链码侧接收到keepalive以后,与peer节点不同,他会回应一个消息给peer节点
    // 而peer节点收到keepalive以后是什么都不做,因为它有一个定时器在定期发送keepalive
    // 这里采用异步发送的方式,不关心是否出错,因为可能下次keepalive能接收到
		handler.serialSendAs而peer节点收到keepalive以后是什么都不做ync(msg, nil) 
		return nil
	}
	chaincodeLogger.Debugf("[%s] Handling ChaincodeMessage of type: %s(state:%s)", shorttxid(msg.Txid), msg.Type, handler.state)

	var err error
	
  // 判断handelr的状态
	switch handler.state {
	case ready:
		err = handler.handleReady(msg, errc)
	case established:
		err = handler.handleEstablished(msg, errc)
	case created:
    // 链码侧的handler在创建的时候,state就是created
    // 直到接收到消息之前,该状态一直都是created,因此走这个case
		err = handler.handleCreated(msg, errc)
	default:
		err = errors.Errorf("[%s] Chaincode handler cannot handle message (%s) with payload size (%d) while in state: %s", msg.Txid, msg.Type, len(msg.Payload), handler.state)
	}

	if err != nil {
    // 如果err不为空,表示处理出错
    // 则会发送一个ChaincodeMessage_ERROR消息给peer节点,并返回错误
		payload := []byte(err.Error())
		errorMsg := &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid}
		handler.serialSend(errorMsg)
		return err
	}

	return nil
}

看下handleCreated()方法,在core/chaincode/shim/handler.go的820行:

//handle created state
func (handler *Handler) handleCreated(msg *pb.ChaincodeMessage, errc chan error) error {
	if msg.Type == pb.ChaincodeMessage_REGISTERED {
    // peer节点发送来的消息类型就是ChaincodeMessage_REGISTERED
		handler.state = established
		return nil
	}
	return errors.Errorf("[%s] Chaincode handler cannot handle message (%s) with payload size (%d) while in state: %s", msg.Txid, msg.Type, len(msg.Payload), handler.state)
}

这里peer节点发送来的消息类型就是ChaincodeMessage_REGISTERED类型(peer节点发送的第一条消息),链码侧收到以后,将状态改为established

之后,链码侧会收到peer节点发送来的第二条消息,因为当前链码侧handler状态为established,因此走handleEstablished()这个方法,在core/chaincode/shim/handler.go的811行:

//handle established state
func (handler *Handler) handleEstablished(msg *pb.ChaincodeMessage, errc chan error) error {
	if msg.Type == pb.ChaincodeMessage_READY {
		handler.state = ready
		return nil
	}
	return errors.Errorf("[%s] Chaincode handler cannot handle message (%s) with payload size (%d) while in state: %s", msg.Txid, msg.Type, len(msg.Payload), handler.state)
}

这里peer节点发送来的消息类型就是ChaincodeMessage_READY类型(peer节点发送的第二条消息),链码侧收到以后,将状态改为ready

至此到这里,链码侧与peer节点的handler的状态就都是ready

handleReady

不妨再看看,链码侧与peer节点的handlerhandleReady都接受哪些类型。

链码侧

代码在core/chaincode/shim/handler.go的774行

//handle ready state
func (handler *Handler) handleReady(msg *pb.ChaincodeMessage, errc chan error) error {
   switch msg.Type {
   case pb.ChaincodeMessage_RESPONSE:
      if err := handler.sendChannel(msg); err != nil {
         chaincodeLogger.Errorf("[%s] error sending %s (state:%s): %s", shorttxid(msg.Txid), msg.Type, handler.state, err)
         return err
      }
      chaincodeLogger.Debugf("[%s] Received %s, communicated (state:%s)", shorttxid(msg.Txid), msg.Type, handler.state)
      return nil

   case pb.ChaincodeMessage_ERROR:
      if err := handler.sendChannel(msg); err != nil {
         chaincodeLogger.Errorf("[%s] error sending %s (state:%s): %s", shorttxid(msg.Txid), msg.Type, handler.state, err)
      }

      chaincodeLogger.Debugf("[%s] Error Received %s, communicated (state:%s)", shorttxid(msg.Txid), msg.Type, handler.state)

      //we don't return error on ERROR
      return nil

   case pb.ChaincodeMessage_INIT:
      chaincodeLogger.Debugf("[%s] Received %s, initializing chaincode", shorttxid(msg.Txid), msg.Type)
      // Call the chaincode's Run function to initialize
      handler.handleInit(msg, errc)
      return nil

   case pb.ChaincodeMessage_TRANSACTION:
      chaincodeLogger.Debugf("[%s] Received %s, invoking transaction on chaincode(state:%s)", shorttxid(msg.Txid), msg.Type, handler.state)
      // Call the chaincode's Run function to invoke transaction
      handler.handleTransaction(msg, errc)
      return nil
   }

   return errors.Errorf("[%s] Chaincode handler cannot handle message (%s) with payload size (%d) while in state: %s", msg.Txid, msg.Type, len(msg.Payload), handler.state)
}

主要处理四种类型:

  • ChaincodeMessage_RESPONSE
  • ChaincodeMessage_ERROR
  • ChaincodeMessage_INIT
  • ChaincodeMessage_TRANSACTION

前两个主要是往handlerresponseChannel发送数据,发送 response 或者是 error

ChaincodeMessage_INIT主要是处理实例化的,实例化的时候,peer侧会发送ChaincodeMessage_INIT类型的消息过来,最终会调用链码的Init方法

ChaincodeMessage_TRANSACTION主要是处理链码调用的,最终会调用链码的Invoke方法。

看下handleInit()方法,在core/chaincode/shim/handler.go的177行:

// handleInit handles request to initialize chaincode.
func (handler *Handler) handleInit(msg *pb.ChaincodeMessage, errc chan error) {
	// The defer followed by triggering a go routine dance is needed to ensure that the previous state transition
	// is completed before the next one is triggered. The previous state transition is deemed complete only when
	// the beforeInit function is exited. Interesting bug fix!!
	go func() {
		var nextStateMsg *pb.ChaincodeMessage

		defer func() {
      // 异步地发送处理结果给peer节点
      // nextStateMsg再之后的处理流程中会被复制,包括出错或是成功
			handler.triggerNextState(nextStateMsg, errc)
		}()
		
    // 错误处理函数
		errFunc := func(err error, payload []byte, ce *pb.ChaincodeEvent, errFmt string, args ...interface{}) *pb.ChaincodeMessage {
			if err != nil {
				// Send ERROR message to chaincode support and change state
				if payload == nil {
					payload = []byte(err.Error())
				}
				chaincodeLogger.Errorf(errFmt, args...)
				return &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid, ChaincodeEvent: ce, ChannelId: msg.ChannelId}
			}
			return nil
		}
		// 获取peer节点传过来的参数,是一个chaincodeInput结构体
		input := &pb.ChaincodeInput{}
		unmarshalErr := proto.Unmarshal(msg.Payload, input)
		if nextStateMsg = errFunc(unmarshalErr, nil, nil, "[%s] Incorrect payload format. Sending %s", shorttxid(msg.Txid), pb.ChaincodeMessage_ERROR.String()); nextStateMsg != nil {
			return
		}

		// Call chaincode's Run
		// Create the ChaincodeStub which the chaincode can use to callback
    // 创建stub对象
		stub := new(ChaincodeStub)
    // 执行stub的初始化,将提案中的信息抽取出来赋值到这个对象中
		err := stub.init(handler, msg.ChannelId, msg.Txid, input, msg.Proposal)
		if nextStateMsg = errFunc(err, nil, stub.chaincodeEvent, "[%s] Init get error response. Sending %s", shorttxid(msg.Txid), pb.ChaincodeMessage_ERROR.String()); nextStateMsg != nil {
			return
		}
    // 执行链码的Init方法
		res := handler.cc.Init(stub)
		chaincodeLogger.Debugf("[%s] Init get response status: %d", shorttxid(msg.Txid), res.Status)

		if res.Status >= ERROR {
      // 出错处理
			err = errors.New(res.Message)
			if nextStateMsg = errFunc(err, []byte(res.Message), stub.chaincodeEvent, "[%s] Init get error response. Sending %s", shorttxid(msg.Txid), pb.ChaincodeMessage_ERROR.String()); nextStateMsg != nil {
				return
			}
		}

		resBytes, err := proto.Marshal(&res)
		if err != nil {
			payload := []byte(err.Error())
			chaincodeLogger.Errorf("[%s] Init marshal response error [%s]. Sending %s", shorttxid(msg.Txid), err, pb.ChaincodeMessage_ERROR)
			nextStateMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid, ChaincodeEvent: stub.chaincodeEvent}
			return
		}

		// Send COMPLETED message to chaincode support and change state
    // 设置ChaincodeMessage_COMPLETED消息,表示完成
    // 最终会调用defer中的triggerNextState方法异步发送给peer
		nextStateMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_COMPLETED, Payload: resBytes, Txid: msg.Txid, ChaincodeEvent: stub.chaincodeEvent, ChannelId: stub.ChannelId}
		chaincodeLogger.Debugf("[%s] Init succeeded. Sending %s", shorttxid(msg.Txid), pb.ChaincodeMessage_COMPLETED)
	}()
}

看下handleTransaction()方法,在core/chaincode/shim/handler.go的238行:

// handleTransaction Handles request to execute a transaction.
func (handler *Handler) handleTransaction(msg *pb.ChaincodeMessage, errc chan error) {
	go func() {
		//better not be nil
		var nextStateMsg *pb.ChaincodeMessage

		defer func() {
			handler.triggerNextState(nextStateMsg, errc)
		}()

		errFunc := func(err error, ce *pb.ChaincodeEvent, errStr string, args ...interface{}) *pb.ChaincodeMessage {
			if err != nil {
				payload := []byte(err.Error())
				chaincodeLogger.Errorf(errStr, args...)
				return &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid, ChaincodeEvent: ce, ChannelId: msg.ChannelId}
			}
			return nil
		}

		// 获取用户传递的参数,由peer传过来
		input := &pb.ChaincodeInput{}
		unmarshalErr := proto.Unmarshal(msg.Payload, input)
		if nextStateMsg = errFunc(unmarshalErr, nil, "[%s] Incorrect payload format. Sending %s", shorttxid(msg.Txid), pb.ChaincodeMessage_ERROR.String()); nextStateMsg != nil {
			return
		}

		// Call chaincode's Run
		// Create the ChaincodeStub which the chaincode can use to callback
		stub := new(ChaincodeStub)
		err := stub.init(handler, msg.ChannelId, msg.Txid, input, msg.Proposal)
		if nextStateMsg = errFunc(err, stub.chaincodeEvent, "[%s] Transaction execution failed. Sending %s", shorttxid(msg.Txid), pb.ChaincodeMessage_ERROR.String()); nextStateMsg != nil {
			return
		}
    // 调用链码的Invoke
		res := handler.cc.Invoke(stub)

		// Endorser will handle error contained in Response.
		resBytes, err := proto.Marshal(&res)
		if nextStateMsg = errFunc(err, stub.chaincodeEvent, "[%s] Transaction execution failed. Sending %s", shorttxid(msg.Txid), pb.ChaincodeMessage_ERROR.String()); nextStateMsg != nil {
			return
		}

		// Send COMPLETED message to chaincode support and change state
		chaincodeLogger.Debugf("[%s] Transaction completed. Sending %s", shorttxid(msg.Txid), pb.ChaincodeMessage_COMPLETED)
		nextStateMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_COMPLETED, Payload: resBytes, Txid: msg.Txid, ChaincodeEvent: stub.chaincodeEvent, ChannelId: stub.ChannelId}
	}()
}

上述两个方法处理逻辑基本相同,梳理一下流程:

  1. 获取用户传递的参数
  2. 创建并初始化stub对象
  3. 调用链码的Init或者是Invoke方法
  4. 异步发送ChaincodeMessage_COMPLETED消息给peer节点

下面再回过头来看下处理ChaincodeMessage_RESPONSEChaincodeMessage_ERROR的流程。看下sendChannel()方法,在core/chaincode/shim/handler.go的110行:

func (handler *Handler) sendChannel(msg *pb.ChaincodeMessage) error {
   handler.Lock()
   defer handler.Unlock()
  	// responseChannel map为空则报错
   if handler.responseChannel == nil {
      return errors.Errorf("[%s] Cannot send message response channel", shorttxid(msg.Txid))
   }
  // 以 ChannelId+Txid 作为map的key
   txCtxID := handler.getTxCtxId(msg.ChannelId, msg.Txid)
  // 如果 map[key] 对应的channel为 nil,则报错
   if handler.responseChannel[txCtxID] == nil {
      return errors.Errorf("[%s] sendChannel does not exist", shorttxid(msg.Txid))
   }

   chaincodeLogger.Debugf("[%s] before send", shorttxid(msg.Txid))
  // 往对应的channel中发送数据
   handler.responseChannel[txCtxID] <- *msg
   chaincodeLogger.Debugf("[%s] after send", shorttxid(msg.Txid))

   return nil
}

我们想要知道responseChannel中对应的 key,value是在哪里复制,追踪了一下是在createChannel()方法中,在core/chaincode/shim/handler.go的95行:

func (handler *Handler) createChannel(channelID, txid string) (chan pb.ChaincodeMessage, error) {
	handler.Lock()
	defer handler.Unlock()
  // responseChannel map为空则报错
	if handler.responseChannel == nil {
		return nil, errors.Errorf("[%s] cannot create response channel", shorttxid(txid))
	}
  // 以 ChannelId+Txid 作为map的key
	txCtxID := handler.getTxCtxId(channelID, txid)
  // 如果key对应的value已经存在则报错
	if handler.responseChannel[txCtxID] != nil {
		return nil, errors.Errorf("[%s] channel exists", shorttxid(txCtxID))
	}
  // 创建channel
	c := make(chan pb.ChaincodeMessage)
  // 赋值
	handler.responseChannel[txCtxID] = c
  // 返回相应的通道
	return c, nil
}

看下这个函数在哪里被调用了,有好几处地方被调用了,挑一处比较重要的方法callPeerWithChaincodeMsg(),在core/chaincode/shim/handler.go的289行:

// callPeerWithChaincodeMsg sends a chaincode message (for e.g., GetState along with the key) to the peer for a given txid
// and receives the response.
func (handler *Handler) callPeerWithChaincodeMsg(msg *pb.ChaincodeMessage, channelID, txid string) (pb.ChaincodeMessage, error) {
	// Create the channel on which to communicate the response from the peer
	var respChan chan pb.ChaincodeMessage
	var err error
  // 这里调用了createChannel,创建了channel
	if respChan, err = handler.createChannel(channelID, txid); err != nil {
		return pb.ChaincodeMessage{}, err
	}
	
  // 执行完以后删除相应的通道
	defer handler.deleteChannel(channelID, txid)
	
 	// 看下这个函数
	return handler.sendReceive(msg, respChan)
}

其中有个sendReceive()方法很重要,这个我们等下看,先看下callPeerWithChaincodeMsg()方法在哪被调用了,我这里贴一张图过来

Hyperledger Fabric从源码分析链码容器启动过程_第1张图片

随便找一处调用看一看,追溯回去以后发现,其实都是ChaincodeStub的一些接口方法,比如GetState(),PutState()等等。而这些方法一般在InovkeInit调用。

到这里就破案了,最后还有一个sendReceive()方法再看下,在core/chaincode/shim/handler.go的129行:

//sends a message and selects
func (handler *Handler) sendReceive(msg *pb.ChaincodeMessage, c chan pb.ChaincodeMessage) (pb.ChaincodeMessage, error) {
	errc := make(chan error, 1)
  // 异步发送消息,例如在GetState()方法调用好以后
  // 生成一个消息,类型为ChaincodeMessage_GET_STATE
	handler.serialSendAsync(msg, errc)

	//the serialsend above will send an err or nil
	//the select filters that first error(or nil)
	//and continues to wait for the response
	//it is possible that the response triggers first
	//in which case the errc obviously worked and is
	//ignored
	for {
		select {
		case err := <-errc:
			if err == nil {
				continue
			}
			//would have been logged, return false
			return pb.ChaincodeMessage{}, err
		case outmsg, val := <-c:
      // c就是传进来的respchan,是在createChannel时创建的
			if !val {
				return pb.ChaincodeMessage{}, errors.New("unexpected failure on receive")
			}
			return outmsg, nil
		}
	}
}

OK,到这里以后整个处理逻辑就通了,以invoke为例:

  1. peer节点发送ChaincodeMessage_TRANSACTION消息给链码侧
  2. 链码侧收到以后,执行handleTransaction()方法,并最终调用链码的Invoke()方法
  3. 如果链码的Invoke()中没有调用其他操作账本的方法,例如GetState(),PutState()等等,那么handleTransaction()最终会返回一个ChaincodeMessage_COMPLETEDpeer节点
  4. 如果有其他操作账本的方法,只要调用了createChannel()
    1. 链码侧创建一个以 channelID+txID 作为 key 的响应通道
    2. 链码侧根据对应的方法设置对应的消息类型(例如GetState()方法类型为ChaincodeMessage_GET_STATE),发送到peer节点,由peer节点再做相应的处理
    3. peer节点处理完以后再发送一个ChaincodeMessage_RESPONSE类型的消息给链码侧
    4. 链码侧收到以后将这个消息发送到对应的响应通道中去
    5. 收到响应以后,链码侧逻辑回到handleTransaction()中,再返回一个ChaincodeMessage_COMPLETEDpeer节点

链码侧的分析就到这里了,下面看看peer节点如何处理handleReady

peer节点

代码在core/chaincode/handler.go的205行:

func (h *Handler) handleMessageReadyState(msg *pb.ChaincodeMessage) error {
	switch msg.Type {
    // 如果是ChaincodeMessage_COMPLETED和ChaincodeMessage_ERROR类型则发送通知,这个方法一会再看
	case pb.ChaincodeMessage_COMPLETED, pb.ChaincodeMessage_ERROR:
		h.Notify(msg)
	// 根据不同的消息类型做相应的处理逻辑
	case pb.ChaincodeMessage_PUT_STATE:
		go h.HandleTransaction(msg, h.HandlePutState)
	case pb.ChaincodeMessage_DEL_STATE:
		go h.HandleTransaction(msg, h.HandleDelState)
	case pb.ChaincodeMessage_INVOKE_CHAINCODE:
		go h.HandleTransaction(msg, h.HandleInvokeChaincode)
	case pb.ChaincodeMessage_GET_STATE:
		go h.HandleTransaction(msg, h.HandleGetState)
	case pb.ChaincodeMessage_GET_STATE_BY_RANGE:
		go h.HandleTransaction(msg, h.HandleGetStateByRange)
	case pb.ChaincodeMessage_GET_QUERY_RESULT:
		go h.HandleTransaction(msg, h.HandleGetQueryResult)
	case pb.ChaincodeMessage_GET_HISTORY_FOR_KEY:
		go h.HandleTransaction(msg, h.HandleGetHistoryForKey)
	case pb.ChaincodeMessage_QUERY_STATE_NEXT:
		go h.HandleTransaction(msg, h.HandleQueryStateNext)
	case pb.ChaincodeMessage_QUERY_STATE_CLOSE:
		go h.HandleTransaction(msg, h.HandleQueryStateClose)
	case pb.ChaincodeMessage_GET_PRIVATE_DATA_HASH:
		go h.HandleTransaction(msg, h.HandleGetPrivateDataHash)
	case pb.ChaincodeMessage_GET_STATE_METADATA:
		go h.HandleTransaction(msg, h.HandleGetStateMetadata)
	case pb.ChaincodeMessage_PUT_STATE_METADATA:
		go h.HandleTransaction(msg, h.HandlePutStateMetadata)
	default:
		return fmt.Errorf("[%s] Fabric side handler cannot handle message (%s) while in ready state", msg.Txid, msg.Type)
	}

	return nil
}

根据不同的消息类型,最终它们都执行了HandleTransaction()方法,第一个参数是msg,第二个参数是指定的处理方法,各不相同。那么下面来看下HandleTransaction()方法,在core/chaincode/handler.go的251行:

// HandleTransaction is a middleware function that obtains and verifies a transaction
// context prior to forwarding the message to the provided delegate. Response messages
// returned by the delegate are sent to the chat stream. Any errors returned by the
// delegate are packaged as chaincode error messages.
func (h *Handler) HandleTransaction(msg *pb.ChaincodeMessage, delegate handleFunc) {
	chaincodeLogger.Debugf("[%s] handling %s from chaincode", shorttxid(msg.Txid), msg.Type.String())
	if !h.registerTxid(msg) {
    // 防止重复的TXID
		return
	}

	startTime := time.Now()
	var txContext *TransactionContext
	var err error
  // 获取交易上下文
  // 对ChaincodeMessage_INVOKE_CHAINCODE单独处理,保证统一上下文
	if msg.Type == pb.ChaincodeMessage_INVOKE_CHAINCODE {
		txContext, err = h.getTxContextForInvoke(msg.ChannelId, msg.Txid, msg.Payload, "")
	} else {
		txContext, err = h.isValidTxSim(msg.ChannelId, msg.Txid, "no ledger context")
	}

	chaincodeName := h.chaincodeID.Name + ":" + h.chaincodeID.Version
	meterLabels := []string{
		"type", msg.Type.String(),
		"channel", msg.ChannelId,
		"chaincode", chaincodeName,
	}
  // 指标相关设置
	h.Metrics.ShimRequestsReceived.With(meterLabels...).Add(1)

	var resp *pb.ChaincodeMessage
	if err == nil {
    // 根据不同的类型做不同的处理操作,得到resp
		resp, err = delegate(msg, txContext)
	}

	if err != nil {
    // 出错返回ChaincodeMessage_ERROR错误消息
		err = errors.Wrapf(err, "%s failed: transaction ID: %s", msg.Type, msg.Txid)
		chaincodeLogger.Errorf("[%s] Failed to handle %s. error: %+v", shorttxid(msg.Txid), msg.Type, err)
		resp = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: []byte(err.Error()), Txid: msg.Txid, ChannelId: msg.ChannelId}
	}

	chaincodeLogger.Debugf("[%s] Completed %s. Sending %s", shorttxid(msg.Txid), msg.Type, resp.Type)
  // 这里删除,对应最开始registerTxid中的ADD
	h.ActiveTransactions.Remove(msg.ChannelId, msg.Txid)
  // 异步发送响应
	h.serialSendAsync(resp)

	meterLabels = append(meterLabels, "success", strconv.FormatBool(resp.Type != pb.ChaincodeMessage_ERROR))
	// 指标相关设置	
  h.Metrics.ShimRequestDuration.With(meterLabels...).Observe(time.Since(startTime).Seconds())
	h.Metrics.ShimRequestsCompleted.With(meterLabels...).Add(1)
}

HandleTransaction()方法主要是获取当前交易的上下文,然后执行相应的消息类型的方法做相应的处理操作,并最终返回一个ChaincodeMessage_RESPONSE类型的消息给链码侧。

再来看下Notify()方法,在core/chaincode/handler.go的544行:

func (h *Handler) Notify(msg *pb.ChaincodeMessage) {
  // 获取交易上下文
	tctx := h.TXContexts.Get(msg.ChannelId, msg.Txid)
	if tctx == nil {
		chaincodeLogger.Debugf("notifier Txid:%s, channelID:%s does not exist for handling message %s", msg.Txid, msg.ChannelId, msg.Type)
		return
	}
	
	chaincodeLogger.Debugf("[%s] notifying Txid:%s, channelID:%s", shorttxid(msg.Txid), msg.Txid, msg.ChannelId)
  // 往交易上下文的ResponseNotifier中发送消息
	tctx.ResponseNotifier <- msg
	tctx.CloseQueryIterators()
}

主要起的是一个通知的作用,看下ResponseNotifier这个变量在哪用到了,追溯到了handler.Execute()方法,在core/chaincode/handler.go的1240行:

func (h *Handler) Execute(txParams *ccprovider.TransactionParams, cccid *ccprovider.CCContext, msg *pb.ChaincodeMessage, timeout time.Duration) (*pb.ChaincodeMessage, error) {
	chaincodeLogger.Debugf("Entry")
	defer chaincodeLogger.Debugf("Exit")

	txParams.CollectionStore = h.getCollectionStore(msg.ChannelId)
	txParams.IsInitTransaction = (msg.Type == pb.ChaincodeMessage_INIT)

	txctx, err := h.TXContexts.Create(txParams)
	if err != nil {
		return nil, err
	}
	defer h.TXContexts.Delete(msg.ChannelId, msg.Txid)

	if err := h.setChaincodeProposal(txParams.SignedProp, txParams.Proposal, msg); err != nil {
		return nil, err
	}

	h.serialSendAsync(msg)

	var ccresp *pb.ChaincodeMessage
	select {
    // 这里用到了,从这里拿响应
	case ccresp = <-txctx.ResponseNotifier:
		// response is sent to user or calling chaincode. ChaincodeMessage_ERROR
		// are typically treated as error
	case <-time.After(timeout):
		err = errors.New("timeout expired while executing transaction")
		ccName := cccid.Name + ":" + cccid.Version
		h.Metrics.ExecuteTimeouts.With("chaincode", ccName).Add(1)
	case <-h.streamDone():
		err = errors.New("chaincode stream terminated")
	}

	return ccresp, err
}

而如果我们追踪这个Exucte()方法,可以追溯到模拟提案中的callChaincode()方法,这个在之前写的文章Hyperledger Fabric从源码分析背书提案过程中有提到。如果能够追溯到这的朋友,恭喜你已经把整个背书模拟提案的流程基本上都搞清楚了!!!!

整体流程总结

总结一下吧,总结一下整体的模拟提案的流程,真的是太不容易了!!!

这里假设链码已经安装了,我们从实例化开始

  1. 客户端应用程序发送实例化模拟提案
  2. peer节点收到提案后,执行模拟提案
  3. peer节点判断链码容器是否启动,如果没有启动,则启动链码容器,调用shim.Start()方法,并最终建立起链码侧与peer节点两端的handlerGRPC通信。
    1. 链码侧发送REGISTER消息给peer节点,当前链码侧状态为created
    2. peer节点当前状态为created,收到REGISTER消息以后,将其注册到peer节点的handler上,发送REGISTERED消息给链码侧,同时更新peer节点状态为Established
    3. peer节点再发送Ready消息给链码侧,同时更新peer节点状态为Ready
    4. 链码侧收到REGISTERED消息后,更新链码侧状态为Established
    5. 链码侧收到Ready消息后,更新链码侧状态为Ready
    6. 此时两侧状态都是Ready状态,互相通信
  4. peer节点判断提案是lsccdeploy请求,则发送INIT消息给链码侧
  5. 链码侧收到INIT消息以后,调用链码的Init()方法,并返回COMPLETED消息
  6. peer节点收到COMPLETED消息以后,执行Notify()方法,往交易上下文的响应通道中发送响应数据
  7. callChaincode()方法内部会最终会调用到一个Execute()方法,监听这个通道,并拿到响应数据
  8. 拿到响应数据之后,进行背书,并最终将背书结果发送给客户端应用程序
  9. 客户端应用程序收到背书结果后,生成一笔交易,发送给排序节点
  10. 排序节点排序好之后,发送给记账节点,记账节点验证无误之后记账,整个流程结束

看了好多天的源码,能把自己看的解析心得分享出来很开心,希望大家看了解析之后也能对Fabric模拟提案的整体过程有更深入的了解

你可能感兴趣的:(Fabric1.4学习,区块链)