04.Fabric源码解析——配置系统

Fabric源码解析4——配置系统

Fabric的配置系统是程序原始数据的来源之一,虽然简单却很重要。在阅读源码过程中对于具象化程序也很有帮助。在分析peer的具体交易工作之前,我们可以先分析一下fabric的配置系统。我们还将我们的目光聚焦在/fabric/peer/main.go的main函数中,除了一系列mainCmd的命令操作,还有viper进行的一系列配置操作,并通过err := common.InitConfig(cmdRoot)进行了配置的初始化。

Fabric索取配置的途径有:环境变量,命令行参数,各种格式的配置文件。其中以配置文件为主,环境变量和命令行参数辅助,三者可以相互作用。主要的配置文件有core.yaml,orderer.yaml等,在/fabric/sampleconfig中有示例。主要使用的配置代码集中在/fabric/core/config下。

viper简介

Fabric的配置系统主要运用第三方包viper,可在github.com/spf13/viper下载。viper可以对系统环境变量,yaml/json等格式的配置文件甚至是远程配置进行读取和设置,并可以在不重启服务的情况下动态设置新的配置项的值并使之实时生效,是一个专门处理配置的解决方案。 而且,viper,眼镜蛇,称自己与cobra(见peer命令结构一文)是companion,足见使用viper的理由。

viper的基础用法如下

//设置一个要读取的配置文件名(不包含后缀),一个viper只支持一个文件名
viper.SetConfigName("config")
//设置一个搜索配置文件的路径,viper的搜索路径可以有多个
viper.AddConfigPath("/etc/appname/")
viper.AddConfigPath(".")
//读取配置文件
viper.ReadInConfig()
//获取其中一个name项的值
viper.Get("name")
//将name的值设置为Bill
viper.Set("name", "Bill")

viper搜索路径和文件

peer命令对core.yaml的引入也是通过viper,具体过程如下:

1. /fabric/peer/main.go中定义const cmdRoot = "core"。
2. main函数中调用err := common.InitConfig(cmdRoot),该参数一路向下传递。
3. InitConfig函数在/fabric/peer/common/common.go中定义,其中调用了config.InitViper(nil, cmdRoot)和viper.ReadInConfig()。
4. InitViper在/fabric/core/config/config.go中定义,接收cmdRoot作为参数,最终调用了viper.SetConfigName(),也即将core设置为了配置文件名。
5. common.InitConfig(cmdRoot)中的viper.ReadInConfig()则读取了该配置文件

orderer命令则使用orderer.yaml配置文件,由viper引入,具体过程如下:

1. 在/fabric/orderer/main.go中main函数调用了config.Load()。
2. Load在/fabric/orderer/localconfig/config.go中定义。该文件中定义了Prefix = "ORDERER"和configNamestring,并在init初始化函数中将configName赋值为strings.ToLower(Prefix),即orderer,也即所用的配置文件名为orderer。Load函数新建了一个用于orderer自己的viper,并调用了cf.InitViper(config, configName),其中config参数为新建的用于orderer自身的viper,configName为配置文件名orderer
3. InitViper在/fabric/core/config/config.go中定义,最终调用了viper.SetConfigName(),也即将orderer设置为了配置文件名Load随后调用了config.ReadInConfig(),读取了配置文件

InitViper

上述步骤中peer和orderer在初始化配置文件时,最终都将调用的终点指向了/fabric/core/config/config.go中定义InitViper()。下面集中分析InitViper

1. 首先判断环境变量FABRIC_CFG_PATH是否有值,如果有值,则是手工定义了FABRIC的配置文件所在路径。参考Getting Started中关于手工设置export FABRIC_CFG_PATH=$PWD(当前目录)。

2. 若没有定义该环境变量的值,则用代码添加三个路径作为搜索配置文件的路径:当前工作目录,$GOPATH/src/github.com/hyperledger/fabric/sampleconfig,/etc/hyperledger/fabric。

3. 当前工作目录,调用addConfigPath(v, “./”)添加,其内部调用的是viper.AddConfigPath()。

4. $GOPATH/src/github.com/hyperledger/fabric/sampleconfig,通过调用AddDevConfigPath(v)添加。

5. AddDevConfigPath首先调用GetDevConfigDir(),读取GOPATH路径下是否存在src/github.com/hyperledger/fabric/sampleconfig目录,若存在,则通过filepath.Join拼接GOPATH路径下是否存在src/github.com/hyperledger/fabric/sampleconfig目录,若存在,则通过filepath.Join拼接GOPATH和src/github.com/hyperledger/fabric/sampleconfig,形成完整的路径并返回。
6. AddDevConfigPath接着调用addConfigPath函数,其内部调用的是viper.AddConfigPath()。/etc/hyperledger/fabric。定义了OfficialPath = “/etc/hyperledger/fabric”常量,如果该路径存在,则调用addConfigPath加入该路径。
7. 调用SetConfigName()设置配置文件名,所指的的配置文件名configName是由参数传递进来的。

8. 由经由InitViper,形成了以下viper配置:
 - 搜索路径(二选一)
FABRIC_CFG_PATH指定的路径
./,$GOPATH/src/github.com/hyperledger/fabric/sampleconfig,/etc/hyperledger/fabric
- 搜索的配置文件名
- core —— 核心配置,供各个模块使用
- orderer —— orderer配置,orderer使用
- 另外注意InitViper的第一个参数,v 
- *viper.Viper。在InitViper函数中,无论是添加搜索路径(使用的是addConfigPath函数),还是设置要搜索的配置文件名(viper自身的SetConfigName函数),都分为全局的viper和特定的viper(也就是参数v)。最终由viper.AddConfigPath或viper.SetConfigName完成的,则是全局的,由v.AddConfigPath或v.SetConfigName完成的,则是特定的。这样就可以很方便的初始化需要单独使用viper的模块,如orderer就是单独使用一条毒蛇,其在/fabric/orderer/localconfig/config.go中的Load函数中,config := viper.New()新养了一条自己的蛇,然后将此蛇通过参数传给InitViper,cf.InitViper(config, configName)。

安全文件配置

安全配置相关的代码在/fabric/peer/main.go中没有体现,而是在/fabric/peer/node/start.go中的serve函数中才初次出现。若grpc服务中使用了TLS网络,则需要.key,.crt,.ca文件配套文件。在此简略介绍,关于TLS,将会在将来专门的文章中详细介绍。

在/fabric/peer/node/start.go中的serve函数中secureConfig, err := peer.GetSecureConfig()获取安全配置。

使用的安全配置结构为/fabric/core/comm/server.go中定义的SecureServerConfig,用于一个grpc服务端实例。
.key,.crt,.ca文件所在的目录都是在core.yaml中定义都的tls文件夹中,当使用TLS网络时,会读取这些文件的数据到SecureServerConfig对象中。在GetSecureConfig()函数中,使用ioutil.ReadFile读取由/fabric/core/config/config.go中定义的config.GetPath(“…”)函数获取的tls路径下的相应文件。如/etc/hyperledger/fabric/tls/server.crt。

命令选项配置

以peer start命令为例,在/fabric/peer/node/start.go中startCmd()函数中,flags.BoolVarP(&chaincodeDevMode, “peer-chaincodedev”, “”, false,“Whether peer in chaincode development mode”)设置了peer start命令的选项之一为peer-chaincodedev,用于赋值文件中的全局变量chaincodeDevMode,该变量指定了chaincode的模式。chaincode的模式在core.yaml中也有定义,chaincode.mode的值为net,为默认选项。而当执行peer start 命令时指定了选项peer-chaincodedev=true,也即将chaincodeDevMode赋值为true,在serve()函数中,就会使用viper.Set(“chaincode.mode”, chaincode.DevModeUserRunsChaincode)将chaincode的模式值设置成了dev。

环境变量配置

在fabric目前的阶段,各个peer都是在容器中运行的,因此环境变量指的是各个容器中的环境变量。在各个容器的启动脚本中对容器的一些环境变量也进行了设置。在peer-base-no-tls.yaml(见源码解析1——线头)中,如peer容器中,设置了如下环境变量:

- CORE_PEER_ADDRESSAUTODETECT=true
- CORE_VM_ENDPOINT=unix:///host/var/run/docker.sock
- CORE_VM_DOCKER_HOSTCONFIG_NETWORKMODE=e2ecli_default
- CORE_LOGGING_LEVEL=ERROR
...

在fabric/peer/main.go中的开始,即对容器的环境变量进行了获取并设置:

//设置了环境变量前置,在此也就是peer
viper.SetEnvPrefix(cmdRoot)
//将环境变量加载进来了
viper.AutomaticEnv()
replacer := strings.NewReplacer(".", "_")
//将环境变量中的_换成.,这样就和yaml文件的配置相匹配了。
//因为viper读取yaml文件所形成的配置项就是按层级并以.分隔的格式,如peer.address
viper.SetEnvKeyReplacer(replacer)

你可能感兴趣的:(04.Fabric源码解析——配置系统)