综述
fabirc源码解析6中讲述了peer结点如何创建和注册grpc服务,接下来的几篇文章将对peer注册的各个服务进行详述。该篇讲述ChaincodeSupport服务,ChaincodeSupport服务为每个peer提供了chaincode操作的支持。registerChaincodeSupport(peerServer.Server())
一句,位于/fabric/peer/node/start.Go文件中的serve函数中,给peerServer注册了ChaincodeSupport服务。
ChaincodeSupport的服务原型和生成的go定义在/fabric/protos/peer/下的chaincode_shim.proto和chaincode_shim.pb.go中,核心的实现代码在/fabric/core/chaincode/chaincode_support.go中。主要的定义的是一个rpc Register(stream ChaincodeMessage) returns (stream ChaincodeMessage) {}
服务。该服务实现客户端和服务器端ChaincodeMessage类型流数据的交换。用于服务端流数据交换的grpc流服务接口象为/fabric/protos/peer/chaincode_shim.pb.go中的ChaincodeSupport_RegisterServer,在/fabric/core/Container/ccintf/ccintf.go中有对应用于容器内部间的流接口ChaincodeStream。
ChaincodeSupport的服务是一个全局单例,该单例对象定义在chaincode_support.go中,var theChaincodeSupport *ChaincodeSupport
。ChaincodeSupport对象自身存储一系列配置值,而接收和处理客户端ChaincodeMessage类型消息的任务其实是委托给了一个个Handler对象。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
FSM
FSM是finite state machine的缩写,有限状态机,是ChaincodeSupport服务使用到的一个第三方库,在github.com/looplab/fsm可以下载。FSM将一个事物从状态A向状态B的转化看作一个事件,并可以设置在进入/离开某个状态时自动调用的时机函数。每个状态事件、状态、时机函数都用字符串关键字表示。在此简单介绍一下用法:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
结构概览:
任何项目中,服务是以所能提供的操作为中心的,ChaincodeSupport服务的操作(即可被外部调用的函数)有Launch,Register,Execute,HandleChaincodeStream,Stop。
Register
追溯ChaincodeSupport对象挂载的Register函数,最终调用的是/fabric/core/chaincode/handler.go中的HandleChaincodeStream函数。在HandleChaincodeStream函数中:
- 1
- 2
- 1
- 2
创建了一个Handler,然后调用Handler的processStream函数对客户端发送的流数据进行了处理。这两个函数都在同文件中实现。newChaincodeSupportHandler函数所传入的两个参数值得注意,一个是chaincodeSupport,一个是stream。前者是Register服务所在的ChaincodeSupport对象自身,赋值给了Hanlder对象成员chaincodeSupport,为的是让Handler对象处理接收数据时能够使用ChaincodeSupport对象的服务;后者是Register服务的grpc流接口,赋值给了Handler对象成员ChatStream,为的是Handler能够从客户端接收到数据。后文还会提到这点。
Handler
newChaincodeSupportHandler创建并初始化了一个Handler,初始化的成员有:
* ChatStream - grpc流服务接口,是用Register函数传进来的。
* chaincodeSupport - chaincodeSupport自身。
* nextState - 状态通道。
* FSM - 状态机,参看上文。
* policyChecker - 策略检查器,将在对应主题文章中详述。
processStream
processStream用recv标识、| errc | msgAvil | nextState | keepalivetimer |四个频道、select三者相互配合,形成了对客户端消息的接收控制。然后调用HandleMessage、serialSend、serialSendAsync处理接收到的消息。
- errc - 错误频道
- msgAvil - ChaincodeMessage频道
- nextState - 包含ChaincodeMessage的频道
- keepalivetime - 心跳频道
流程如下:
HandleMessage
HandleMessage处理ChaincodeMessage数据的方式完全是由Handler中的状态机FSM驱动的。在newChaincodeSupportHandler有大段代码是初始化其状态机的:
- 1
- 1
状态机FSM所注册的状态事件有:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
状态机FSM所涉及的事件状态有:
- 1
- 2
- 3
- 4
- 5
- 1
- 2
- 3
- 4
- 5
状态机FSM 状态事件所调用的时机函数为:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
在HandleMessage函数中,对传入的数据msg简单验证后,eventErr := handler.FSM.Event(msg.Type.String(), msg)
触发了状态机的状态事件,进而触发了对应的时机函数。
以“REGISTER类型的ChaincodeMessage”为例。客户端通过grpc发送REGISTER类型的ChaincodeMessage信息,服务端通过msgAvil频道接收后传入HandlerMessage函数,状态机对应执行REGISTER状态事件,从状态createdstate向状态establishedstate转变,同时在转变之前自动触发beforeRegisterEvent时机函数完成注册。当状态进入establishedstate时,又接着触发了“enter_established”所对应的enterEstablishedState时机函数去通知客户端注册已经正确完成。
在beforeRegisterEvent函数中,err = handler.chaincodeSupport.registerHandler(handler)
完成了注册,使用的是前文所提到的在创建Handler时传入进来的ChaincodeSupport对象的registerHandler函数。所谓的注册,也不过是将Handler对象赋值给ChaincodeSupport对象中的runningChaincodes中的chaincodeMap映射:chainID作key,以Handler对象为成员handler值的chaincodeRTEnv对象作value。
serialSend或serialSendAsync
都是使用Handler中grpc服务端流接口ChatStream成员发送ChaincodeMessage消息的函数,两者都将应答ChaincodeMessage信息发送给客户端,也都实现了将所发送的ChaincodeMessage信息串行化的目的。区别在于serialSend是阻塞发送,而serialSendAsync是利用新启goroutine进行非阻塞发送,且这些非阻塞的goroutine中任何一个发生发送消息的错误,都会利用errc频道将错误发送给processStream函数。
小结
不同类型的ChaincodeMessage的消息,能够触发状态机不同的状态事件,处理数据,完成Chaincode上的操作。有关其他类型事件以及具体的实现,在此不再赘述。强调一句,ChaincodeSupport服务是以状态机驱动的为chaincode提供支持的一项服务