Fabric 1.4 源码分析 背书节点和链码容器交互
本文档主要介绍背书节点和链码容器交互流程,在Endorser背书节点章节中,无论是deploy、upgrade或者调用链码,最后都会调用ChaincodeSupport.LaunchInit()/Launch()以及ChaincodeSupport.execute()方法。其中Launch()方法启动链码容器,execute()方法调用链码。
1. 准备
ChaincodeSupport.Launch()首先进行判断,根据peer侧该版本链码的Handler是否存在,存在则表示已运行。若不存在,则调用lscc链码方法cs.Lifecycle.ChaincodeContainerInfo()获取启动链码所需的数据ChaincodeContainerInfo。再调用cs.Launcher.Launch()方法启动链码。再判断是否注册了handler。
func (cs *ChaincodeSupport) Launch(chainID, chaincodeName, chaincodeVersion string, qe ledger.QueryExecutor) (*Handler, error) {
cname := chaincodeName + ":" + chaincodeVersion
if h := cs.HandlerRegistry.Handler(cname); h != nil {
return h, nil
}
ccci, err := cs.Lifecycle.ChaincodeContainerInfo(chaincodeName, qe)
if err != nil {
// TODO: There has to be a better way to do this...
if cs.UserRunsCC {
chaincodeLogger.Error(
"You are attempting to perform an action other than Deploy on Chaincode that is not ready and you are in developer mode. Did you forget to Deploy your chaincode?",
)
}
return nil, errors.Wrapf(err, "[channel %s] failed to get chaincode container info for %s", chainID, cname)
}
if err := cs.Launcher.Launch(ccci); err != nil {
return nil, errors.Wrapf(err, "[channel %s] could not launch chaincode %s", chainID, cname)
}
h := cs.HandlerRegistry.Handler(cname)
if h == nil {
return nil, errors.Wrapf(err, "[channel %s] claimed to start chaincode container for %s but could not find handler", chainID, cname)
}
return h, nil
}
type ChaincodeContainerInfo struct {
Name string
Version string
Path string
Type string
CodePackage []byte
// ContainerType is not a great name, but 'DOCKER' and 'SYSTEM' are the valid types
ContainerType string
}
Launch()主要实现方法在core/chaincode/runtime_launcher.go Launch()方法。在该方法中,会调用r.Runtime.Start(ccci, codePackage)启动链码,在该方法中,首先会调用c.LaunchConfig(cname, ccci.Type)生成创建链码所需的参数LaunchConfig(链码类型go/java/nodejs,以及TLS配置),然后构造启动链码容器请求StartContainerReq。接着调用c.Processor.Process(ccci.ContainerType, scr)正式启动链码容器。操作完成后,通过Launch()里面的select—case语句阻塞获取结果,并结束程序运行。
func (r *RuntimeLauncher) Launch(ccci *ccprovider.ChaincodeContainerInfo) error {
...
if !alreadyStarted {
...
go func() {
if err := r.Runtime.Start(ccci, codePackage); err != nil {
startFailCh <- errors.WithMessage(err, "error starting container")
return
}
exitCode, err := r.Runtime.Wait(ccci)
if err != nil {
launchState.Notify(errors.Wrap(err, "failed to wait on container exit"))
}
launchState.Notify(errors.Errorf("container exited with %d", exitCode))
}()
}
var err error
select {
case <-launchState.Done():
err = errors.WithMessage(launchState.Err(), "chaincode registration failed")
case err = <-startFailCh:
launchState.Notify(err)
r.Metrics.LaunchFailures.With("chaincode", cname).Add(1)