经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 其他 » 区块链 » 查看文章
Fabric1.4源码中链码容器启动过程解析
来源:cnblogs  作者:触不可及`  时间:2019/6/24 8:37:42  对本文有异议

想写点东西记录一下最近看的一些Fabric源码,本文使用的是fabric1.4的版本,所以对于其他版本的fabric,内容可能会有所不同。
其实我仅仅知道Go语言一些语法的使用,并不太熟悉Go语言,所以解析的内容可能会有误,欢迎大家批评指正。
本文想针对Fabric中链码容器的启动过程进行源码的解析。这里的链码指的是用户链码不是系统链码,顺便回顾一下系统链码:
lscc(Life Cycle System ChainCode)生命周期系统链码
cscc(Configuration System ChainCode)配置系统链码
escc(Endorser System ChainCode)背书系统链码
qscc(Query System ChainCode)查询系统链码
vscc(Verification System ChainCode)验证系统链码
本文主要解析的是用户链码的启动过程。

1 起点

  1. #这是用户端链码的main方法,也是整个流程的入口点,调用了shim包中的Start(cc Chaincode)方法.
  2. func main(){
  3. err :=shim.Start(new(Chaincode))
  4. if err != nil {
  5. fmt.Printf("Error starting Chaincode: %s",err)
  6. }
  7. }

首先定位到fabric/core/chaincode/shim/chaincode.go这个文件中的Start方法,这里是链码启动的起点。
可以看到传的参数就是chaincode,接下来分析一下启动过程

  1. #方法中第一行代码,根据名字可以看出是对链码的Log进行设置
  2. SetupChaincodeLogging()
  3. #从输入中获取用户定义的链码的名称
  4. chaincodename := viper.GetString("chaincode.id.name")
  5. #如果没有输入链码名称,直接返回没有提供链码id的错误,下面则不再执行
  6. if chaincodename == "" {
  7. return errors.New("error chaincode id not provided")
  8. }
  9. #看名字是一个工厂方法,点进行看一下
  10. err := factory.InitFactories(factory.GetDefaultOpts())

首先进入到factory.GetDefaultOpts()方法中:

  1. func GetDefaultOpts() *FactoryOpts {
  2. return &FactoryOpts{
  3. ProviderName: "SW",
  4. SwOpts: &SwOpts{
  5. HashFamily: "SHA2", #HASH类型
  6. SecLevel: 256, #HASH级别
  7. Ephemeral: true,
  8. },
  9. }
  10. }
  11. #可以猜到这个方法是获取默认的加密操作,使用SHA256进行数据加密

不难猜到factory.InitFactories这个方法就是为当前链码设置加密操作的一系列内容。回到Start()方法中接着往下看.

  1. #这一部分就是将链码数据以流的方式读取进来,userChaincodeStreamGetter是一个方法,点进去看一下
  2. if streamGetter == nil {
  3. streamGetter = userChaincodeStreamGetter
  4. }
  5. stream, err := streamGetter(chaincodename)
  6. if err != nil {
  7. return err
  8. }

userChaincodeStreamGetter还是在这个文件中第82行:

  1. #这里的name是链码名称,读取到链码数据后以PeerChainCodeStream的方式返回
  2. func userChaincodeStreamGetter(name string) (PeerChaincodeStream, error) {
  3. #获取peer.address
  4. flag.StringVar(&peerAddress, "peer.address", "", "peer address")
  5. //判断是否使能TLS
  6. if viper.GetBool("peer.tls.enabled") {
  7. #获取tls密钥地址,在用户安装链码的时候指定
  8. keyPath := viper.GetString("tls.client.key.path")
  9. #获取tls证书地址
  10. certPath := viper.GetString("tls.client.cert.path")
  11. #从文件中读取密钥数据
  12. data, err1 := ioutil.ReadFile(keyPath)
  13. if err1 != nil {
  14. err1 = errors.Wrap(err1, fmt.Sprintf("error trying to read file content %s", keyPath))
  15. chaincodeLogger.Errorf("%+v", err1)
  16. return nil, err1
  17. }
  18. key = string(data)
  19. #从文件中读取证书数据
  20. data, err1 = ioutil.ReadFile(certPath)
  21. if err1 != nil {
  22. err1 = errors.Wrap(err1, fmt.Sprintf("error trying to read file content %s", certPath))
  23. chaincodeLogger.Errorf("%+v", err1)
  24. return nil, err1
  25. }
  26. cert = string(data)
  27. }
  28. #解析命令行参数到定义的flag
  29. flag.Parse()
  30. #日志输出
  31. chaincodeLogger.Debugf("Peer address: %s", getPeerAddress())
  32. //与peer节点建立连接
  33. clientConn, err := newPeerClientConnection()

看一下这个方法里面的内容,还是这个文件第317行:

  1. func newPeerClientConnection() (*grpc.ClientConn, error) {
  2. #首先获取到peer节点的地址
  3. var peerAddress = getPeerAddress()
  4. #看名字就知道了,设置与链码之间的心中信息
  5. kaOpts := &comm.KeepaliveOptions{
  6. ClientInterval: time.Duration(1) * time.Minute,
  7. ClientTimeout: time.Duration(20) * time.Second,
  8. }

判断是否使能了TLS,然后根据结果建立链接,如何建立链接就不再细看了,我们回到之前的部分

  1. if viper.GetBool("peer.tls.enabled") {
  2. return comm.NewClientConnectionWithAddress(peerAddress, true, true,
  3. comm.InitTLSForShim(key, cert), kaOpts)
  4. }
  5. return comm.NewClientConnectionWithAddress(peerAddress, true, false, nil, kaOpts)
  6. }

还是之前的userChaincodeStreamGetter方法

  1. clientConn, err := newPeerClientConnection()
  2. if err != nil {
  3. err = errors.Wrap(err, "error trying to connect to local peer")
  4. chaincodeLogger.Errorf("%+v", err)
  5. return nil, err
  6. }
  7. chaincodeLogger.Debugf("os.Args returns: %s", os.Args)
  8. #接下来是这个方法,返回一个ChaincodeSupportClient实例,对应着链码容器
  9. chaincodeSupportClient := pb.NewChaincodeSupportClient(clientConn)
  10. //这一步是与peer节点建立gRPC连接
  11. stream, err := chaincodeSupportClient.Register(context.Background())
  12. if err != nil {
  13. return nil, errors.WithMessage(err, fmt.Sprintf("error chatting with leader at address=%s", getPeerAddress()))
  14. }
  15. return stream, nil
  16. }

这个方法结束之后,链码容器与Peer节点已经建立起了连接,接下来链码容器与Peer节点开始互相发送消息了。
返回到Start()方法中,还剩最后的一个方法chatWithPeer()

  1. err = chatWithPeer(chaincodename, stream, cc)
  2. return err
  3. }

看一下链码容器与Peer节点是如何互相通信的。这个方法是链码容器启动的过程中最重要的方法,包含所有的通信流程。chatWithPeer()在331行:

  1. func chatWithPeer(chaincodename string, stream PeerChaincodeStream, cc Chaincode)
  2. #传入的参数有链码名称,流(这个是之前链码容器与Peer节点建立gRPC连接所返回的),链码

首先第一步是新建一个ChaincodeHandler对象:是非常重要的一个对象。看一下该对象的内容,在core/chaincode/shim/handler.go文件中第166行:

  1. func newChaincodeHandler(peerChatStream PeerChaincodeStream, chaincode Chaincode) *Handler {
  2. v := &Handler{
  3. ChatStream: peerChatStream, #与Peer节点通信的流
  4. cc: chaincode, #链码
  5. }
  6. v.responseChannel = make(map[string]chan pb.ChaincodeMessage) #链码信息响应通道
  7. v.state = created #表示将链码容器的状态更改为created
  8. return v handler返回
  9. }

这个ChaincodeHandler对象是链码侧完成链码与Peer节点之前所有的消息的控制逻辑。
继续往下看:

  1. #在方法执行结束的时候关闭gRPC连接
  2. defer stream.CloseSend()
  3. #获取链码名称
  4. chaincodeID := &pb.ChaincodeID{Name: chaincodename}
  5. #将获取的链码名称序列化为有效载荷.
  6. payload, err := proto.Marshal(chaincodeID)
  7. if err != nil {
  8. return errors.Wrap(err, "error marshalling chaincodeID during chaincode registration")
  9. }
  10. #日志输出,这个日志信息在安装链码的时候应该有看到过吧
  11. chaincodeLogger.Debugf("Registering.. sending %s", pb.ChaincodeMessage_REGISTER)
  12. #链码容器通过handler开始通过gRPC连接向Peer节点发送第一个消息了,链码容器向Peer节点发送REGISTER消息,并附上链码的名称
  13. if err = handler.serialSend(&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_REGISTER, Payload: payload}); err != nil {
  14. return errors.WithMessage(err, "error sending chaincode REGISTER")
  15. }
  16. #定义一个接收消息的结构体
  17. type recvMsg struct {
  18. msg *pb.ChaincodeMessage
  19. err error
  20. }
  21. msgAvail := make(chan *recvMsg, 1)
  22. errc := make(chan error)
  23. receiveMessage := func() {
  24. in, err := stream.Recv()
  25. msgAvail <- &recvMsg{in, err}
  26. }
  27. #接收由Peer节点返回的响应消息
  28. go receiveMessage()

接下来的部分就是链码容器与Peer节点详细的通信过程了:

2链码侧向Peer节点发送REGISTER消息

  1. #前面的部分都是接收到错误消息的各种输出逻辑,不再细看,我们看default这一部分,这一部分是正常情况下消息的处理情况:
  2. for {
  3. select {
  4. case rmsg := <-msgAvail:
  5. switch {
  6. case rmsg.err == io.EOF:
  7. err = errors.Wrapf(rmsg.err, "received EOF, ending chaincode stream")
  8. chaincodeLogger.Debugf("%+v", err)
  9. return err
  10. case rmsg.err != nil:
  11. err := errors.Wrap(rmsg.err, "receive failed")
  12. chaincodeLogger.Errorf("Received error from server, ending chaincode stream: %+v", err)
  13. return err
  14. case rmsg.msg == nil:
  15. err := errors.New("received nil message, ending chaincode stream")
  16. chaincodeLogger.Debugf("%+v", err)
  17. return err
  18. default:
  19. #这一句日志输出应该看到过好多次吧。
  20. chaincodeLogger.Debugf("[%s]Received message %s from peer", shorttxid(rmsg.msg.Txid), rmsg.msg.Type)
  21. #重要的一个方法,在链码容器与Peer节点建立起了联系后,主要通过该方法对消息逻辑进行处理,我们点进行看一下。
  22. err := handler.handleMessage(rmsg.msg, errc)
  23. if err != nil {
  24. err = errors.WithMessage(err, "error handling message")
  25. return err
  26. }
  27. #当消息处理完成后,再次接收消息。
  28. go receiveMessage()
  29. }
  30. #最后是发送失败的处理
  31. case sendErr := <-errc:
  32. if sendErr != nil {
  33. err := errors.Wrap(sendErr, "error sending")
  34. return err
  35. }
  36. }
  37. }

一个重要的方法:handleMessagecore/chaincode/shim/handler.go文件第801行:

  1. func (handler *Handler) handleMessage(msg *pb.ChaincodeMessage, errc chan error) error {
  2. #如果链码容器接收到Peer节点发送的心跳消息后,直接将心跳消息返回,双方就一直保持联系。
  3. if msg.Type == pb.ChaincodeMessage_KEEPALIVE {
  4. chaincodeLogger.Debug("Sending KEEPALIVE response")
  5. handler.serialSendAsync(msg, nil) // ignore errors, maybe next KEEPALIVE will work
  6. return nil
  7. }
  8. #我们先看到这里,如果再往下看的话可能会乱掉,所以还是按照逻辑顺序进行说明。

先说一下链码侧所做的工作:

  • 首先进行各项基本配置,然后建立起与Peer节点的gRPC连接。
  • 创建Handler,并更改Handler状态为created
  • 发送REGISTER消息到Peer节点。
  • 等待Peer节点返回的信息

3Peer节点接收到REGISTER消息后

之前讲的都是链码侧的一系列流程,我们之前提到链码侧与Peer节点之间的第一个消息内容是由链码侧发送至Peer节点的REGISTER消息。接下来我们看一下Peer节点在接收到该消息后是如果进行处理的。
代码在core/chaincode/handler.go文件中第174行,这里不是处理消息的开始,但是对于我们要说的链码容器启动过程中消息的处理刚好衔接上,所以就直接从这里开始了。另外很重要的一点,这里已经转换到Peer节点侧了,不是之前说的链码侧,我们看一下代码:

  1. func (h *Handler) handleMessage(msg *pb.ChaincodeMessage) error {
  2. chaincodeLogger.Debugf("[%s] Fabric side handling ChaincodeMessage of type: %s in state %s", shorttxid(msg.Txid), msg.Type, h.state)
  3. #这边也是首先判断是不是心跳信息,如果是心跳信息的话就什么也不做,与之前不同的是链码侧在收到心跳信息后会返回Peer节点一个心跳信息。
  4. if msg.Type == pb.ChaincodeMessage_KEEPALIVE {
  5. return nil
  6. }
  7. #之前我们提到,创建handler时,更改状态为created,所以这里进入到handleMessageCreatedState这个方法内.
  8. switch h.state {
  9. case Created:
  10. return h.handleMessageCreatedState(msg)
  11. case Ready:
  12. return h.handleMessageReadyState(msg)
  13. default:
  14. return errors.Errorf("handle message: invalid state %s for transaction %s", h.state, msg.Txid)
  15. }
  16. }

handleMessageCreatedState这个方法在第191行,方法内容很简单,判断消息类型是不是REGISTER,如果是则进入HandlerRegister(msg)方法内,如果不是则返回错误信息。

  1. func (h *Handler) handleMessageCreatedState(msg *pb.ChaincodeMessage) error {
  2. switch msg.Type {
  3. case pb.ChaincodeMessage_REGISTER:
  4. h.HandleRegister(msg)
  5. default:
  6. return fmt.Errorf("[%s] Fabric side handler cannot handle message (%s) while in created state", msg.Txid, msg.Type)
  7. }
  8. return nil
  9. }

接下来我们看一下HandleRegister这个方法,在第495行:

  1. func (h *Handler) HandleRegister(msg *pb.ChaincodeMessage) {
  2. chaincodeLogger.Debugf("Received %s in state %s", msg.Type, h.state)
  3. #获取链码ID
  4. chaincodeID := &pb.ChaincodeID{}
  5. #反序列化
  6. err := proto.Unmarshal(msg.Payload, chaincodeID)
  7. if err != nil {
  8. chaincodeLogger.Errorf("Error in received %s, could NOT unmarshal registration info: %s", pb.ChaincodeMessage_REGISTER, err)
  9. return
  10. }
  11. h.chaincodeID = chaincodeID
  12. #这一行就是将链码注册到当前Peer节点上
  13. err = h.Registry.Register(h)
  14. if err != nil {
  15. h.notifyRegistry(err)
  16. return
  17. }
  18. Peer节点侧的handler获取链码名称
  19. h.ccInstance = ParseName(h.chaincodeID.Name)
  20. chaincodeLogger.Debugf("Got %s for chaincodeID = %s, sending back %s", pb.ChaincodeMessage_REGISTER, chaincodeID, pb.ChaincodeMessage_REGISTERED)
  21. #然后将REGISTERED消息返回给链码侧
  22. if err := h.serialSend(&pb.ChaincodeMessage{Type: pb.ChaincodeMessage_REGISTERED}); err != nil {
  23. chaincodeLogger.Errorf("error sending %s: %s", pb.ChaincodeMessage_REGISTERED, err)
  24. h.notifyRegistry(err)
  25. return
  26. }
  27. //更新handler状态为Established
  28. h.state = Established
  29. chaincodeLogger.Debugf("Changed state to established for %+v", h.chaincodeID)
  30. #还有这个方法也要看一下
  31. h.notifyRegistry(nil)
  32. }

简单来说HandleRegister的功能就是将链码注册到Peer节点上,并发送RESIGSERED到链码侧,最后更新handler状态为Established,我们看一下notifyRegistry方法,在478行:

  1. func (h *Handler) notifyRegistry(err error) {
  2. if err == nil {
  3. //再往里面看,方法在459行
  4. err = h.sendReady()
  5. }
  6. if err != nil {
  7. h.Registry.Failed(h.chaincodeID.Name, err)
  8. chaincodeLogger.Errorf("failed to start %s", h.chaincodeID)
  9. return
  10. }
  11. h.Registry.Ready(h.chaincodeID.Name)
  12. }
  13. #sendReady()
  14. func (h *Handler) sendReady() error {
  15. chaincodeLogger.Debugf("sending READY for chaincode %+v", h.chaincodeID)
  16. ccMsg := &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_READY}
  17. #Peer节点又向链码容器发送了READY消息
  18. if err := h.serialSend(ccMsg); err != nil {
  19. chaincodeLogger.Errorf("error sending READY (%s) for chaincode %+v", err, h.chaincodeID)
  20. return err
  21. }
  22. #同时更新handler状态为Ready
  23. h.state = Ready
  24. chaincodeLogger.Debugf("Changed to state ready for chaincode %+v", h.chaincodeID)
  25. return nil
  26. }

到这里,Peer节点暂时分析完成,又到了链码侧对Peer节点发送的消息进行处理的流程.
我们先总结一下这一部分Peer节点做了哪些工作:

  • 首先当Peer节点接收到链码侧发送的REGISTER消息后,将链码注册到Peer端的Handler上,发送REGISTERED到链码侧,更新Handler的状态为Established
  • 然后Peer节点向链码侧发送READY消息,同时更新Handler的状态为Ready

4链码侧的回应

我们回到链码侧之前的这一部分core/chaincode/chaincode.go中第364行,这里是链码铡对接收到的Peer节点发送的消息进行处理的逻辑,至于发生错误的情况就不再说明,我们看handleMessage这个方法。

  1. go receiveMessage()
  2. for {
  3. #相关代码
  4. ...
  5. err := handler.handleMessage(rmsg.msg, errc)
  6. ...
  7. #相关代码
  8. go receiveMessage()
  9. }

handleMessage这个方法在core/chaincode/shim/handler.go这个文件中,第801行。

  1. #主要就是这一部分:
  2. switch handler.state {
  3. case ready:
  4. err = handler.handleReady(msg, errc)
  5. case established:
  6. err = handler.handleEstablished(msg, errc)
  7. case created:
  8. err = handler.handleCreated(msg, errc)
  9. default:
  10. 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)
  11. }
  • 首先链码侧接收到Peer节点发送的REGISTERED消息后,这里链码侧的handler与Peer节点侧的handler并不是同一个,不要搞混了。判断当前链码侧handler的状态为created,进入到handleCreated方法中,在792行:
  1. #将链码侧的handler的状态更改为established
  2. if msg.Type == pb.ChaincodeMessage_REGISTERED {
  3. handler.state = established
  4. return nil
  5. }
  • 当链码侧接收到Peer节点发送的READY消息后,又一次进入上面的逻辑,由于链码侧的handler的状态已经更改为established,所以这次进入到handleEstablished方法中。在783行:
  1. #然后将链码侧的handler的状态更改为ready
  2. if msg.Type == pb.ChaincodeMessage_READY {
  3. handler.state = ready
  4. return nil
  5. }
  • 另外,当用户对链码进行实例化操作时,会通过Peer节点向链码侧发送INIT消息,这里涉及到背书过程,之后再对背书过程进行讨论,我们在这里只关注链码侧接收到INIT消息后的逻辑,还是handleMessage这个方法中:
  1. #当判断到消息类型为INIT时,会执行这个方法。
  2. handler.handleInit(msg, errc)

handler.handleInit(msg, errc)方法在第177行:

  1. func (handler *Handler) handleInit(msg *pb.ChaincodeMessage, errc chan error) {
  2. go func() {
  3. var nextStateMsg *pb.ChaincodeMessage
  4. defer func() {
  5. #这一名相当于更新链码的状态
  6. handler.triggerNextState(nextStateMsg, errc)
  7. }()
  8. #判断错误信息
  9. errFunc := func(err error, payload []byte, ce *pb.ChaincodeEvent, errFmt string, args ...interface{}) *pb.ChaincodeMessage {
  10. if err != nil {
  11. // Send ERROR message to chaincode support and change state
  12. if payload == nil {
  13. payload = []byte(err.Error())
  14. }
  15. chaincodeLogger.Errorf(errFmt, args...)
  16. return &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid, ChaincodeEvent: ce, ChannelId: msg.ChannelId}
  17. }
  18. return nil
  19. }
  20. #获取用户输入的参数
  21. input := &pb.ChaincodeInput{}
  22. #反序列化
  23. unmarshalErr := proto.Unmarshal(msg.Payload, input)
  24. if nextStateMsg = errFunc(unmarshalErr, nil, nil, "[%s] Incorrect payload format. Sending %s", shorttxid(msg.Txid), pb.ChaincodeMessage_ERROR.String()); nextStateMsg != nil {
  25. return
  26. }
  27. #ChaincodeStub应该很熟悉了,很重要的一个对象,包含一项提案中所需要的内容。在``core/chaincode/shim/chaincode.go``文件中第53行,有兴趣可以点进去看一下
  28. stub := new(ChaincodeStub)
  29. #这一行代码的意思就是将提案中的信息抽取出来赋值到ChaincodeStub这个对象中
  30. err := stub.init(handler, msg.ChannelId, msg.Txid, input, msg.Proposal)
  31. if nextStateMsg = errFunc(err, nil, stub.chaincodeEvent, "[%s] Init get error response. Sending %s", shorttxid(msg.Txid), pb.ChaincodeMessage_ERROR.String()); nextStateMsg != nil {
  32. return
  33. }
  34. #这里的Init方法就是链码中所写的Init()方法,就不再解释了
  35. res := handler.cc.Init(stub)
  36. chaincodeLogger.Debugf("[%s] Init get response status: %d", shorttxid(msg.Txid), res.Status)
  37. #ERROR的值为500,OK=200,ERRORTHRESHOLD = 400,大于等于400就代表错误信息或者被背书节点拒绝。
  38. if res.Status >= ERROR {
  39. err = errors.New(res.Message)
  40. 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 {
  41. return
  42. }
  43. }
  44. resBytes, err := proto.Marshal(&res)
  45. if err != nil {
  46. payload := []byte(err.Error())
  47. chaincodeLogger.Errorf("[%s] Init marshal response error [%s]. Sending %s", shorttxid(msg.Txid), err, pb.ChaincodeMessage_ERROR)
  48. nextStateMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid, ChaincodeEvent: stub.chaincodeEvent}
  49. return
  50. }
  51. // Send COMPLETED message to chaincode support and change state
  52. nextStateMsg = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_COMPLETED, Payload: resBytes, Txid: msg.Txid, ChaincodeEvent: stub.chaincodeEvent, ChannelId: stub.ChannelId}
  53. chaincodeLogger.Debugf("[%s] Init succeeded. Sending %s", shorttxid(msg.Txid), pb.ChaincodeMessage_COMPLETED)
  54. #到这里就结束了,会调用上面的handler.triggerNextState(nextStateMsg, errc)方法,这个方法将初始化数据与COMPLETED状态发送至Peer节点。
  55. }()
  56. }

这个方法还是比较简单的,一共做了这些事情:

  • 获取用户的输入数据
  • 新建一个ChainCodeStub对象,然后将用户输入的数据赋值给该对象
  • 调用用户链码中的Init()方法
  • 将所有数据封装成ChainCodeMessage,类型为COMPLETED,发送至Peer节点。

这个时候链码已经初始化完成,已经进入了可被调用(invoke)的状态.
之后的流程就差不多了,Peer节点发送TRANSACTION消息给链码侧,调用Invoke()方法,之后链码侧发送具体的调用方法到Peer节点,由Peer节点进行相应的处理,最后返回RESPONSE消息到链码侧,链码侧接收到RESPONSE消息后,返回COMPLETED消息到Peer节点。

5总结

到这里,Peer节点与链码侧的handler都处于READY状态,链码容器已经启动完成。最后总结一下整体的流程:

  1. 通过用户端链码中的main方法,调用了core/chaincode/shim/chaincode.go中的Start()方法,从而开始了链码的启动。
  2. 首先进行相关的配置比如基本的加密,证书的读取。
  3. 创建与Peer节点之间的gRPC连接,创建handler实例。
  4. 由链码容器向Peer节点发送第一个消息:REGISTER,然后等待接收由Peer节点发送的消息。如果接收到的是心跳消息,则向Peer节点返回心跳消息。
  5. Peer节点接收到链码容器发送的REGISTER消息后,将其注册到Peer节点端的handler上。
  6. Peer节点发送REGISTERED消息到链码侧,同时更新Peer节点端的handler状态为Established
  7. Peer节点发送Ready消息到链码侧,同时更新Peer节点端的handler状态为Ready
  8. 链码侧接收到由Peer节点发送的REGISTERED消息后,更新链码侧的handler状态为Established
  9. 链码侧接收到由Peer节点发送的READY消息后,更新链码侧的handler状态为ready
  10. 当用户执行实例化链码时,通过Peer节点向链码侧发送INIT消息。链码侧接收到INIT消息后,根据用户输入的参数进行实例化操作。实例化完成后,返回COMPLETED消息到Peer节点。
  11. 到这里链码容器已经启动,可以对链码数据进行查询调用等操作了。

另外,阅读Fabric源码中有一些没有看明白或者分析有误的地方,还望大家能够批评指正。

最后附上参考文档:传送门
以及Fabric源码地址:传送门

原文链接:http://www.cnblogs.com/cbkj-xd/p/11072761.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号