Hyperledger Fabric(2) - 源码分析之Config 配置模块的设计

1. 背景

一个优秀的区块链开源性项目,配置参数的读取是必不可少的。通常来讲,项目会基于现有配置读取框架上进行开发。

本文主要从源码角度探索Fabric 的配置读取原理。

2.查找相关代码

Hyperledger Fabric(1) - 整体架构和源码结构 文章中可知/fabric/core目录是项目的核心代码区,所以从此处入手查找配置参数的读取模块是否在其中。

果不其然/fabric/core/config目录即是配置的代码,查看 /fabric/core/config/config.go 代码内容可发现Fabric 的配置系统主要运用第三方包 viper

viper可以对系统环境变量、 yaml/json 等格式的配置文件甚至是远程配置进行读取和设置 ,并可以在不重启服务的情况下动态设置新的配 项的值并使之实时生效。是一个专门处理配置的解决方案,拥有眼镜蛇的称号,和命令行的库 cobra 出自同一个开发者 spfl3 之手。

3. 源码分析

3.1 读取配置文件主模块

/fabric/core/config/config.go 中代码量不多,主要看InitViper函数,具体查看以下中文注释

func InitViper(v *viper.Viper, configName string) error {
    
    
	var altPath = os.Getenv("FABRIC_CFG_PATH")
	// 首先判断环境变量 FABRIC_CFG_PATH 是否有值, 如果有值说明是用户人为定义FABRIC 配置文件的路径
	if altPath != "" {
    
    
		// If the user has overridden the path with an envvar, its the only path
		// we will consider
		//判断路径是否存在
		if !dirExists(altPath) {
    
    
			return fmt.Errorf("FABRIC_CFG_PATH %s does not exist", altPath)
		}
		//如果路径存在,将FABRIC_CFG_PATH 内容添加作为配置文件的路径
		AddConfigPath(v, altPath)
	/*若没有定义该环境变量的值 ,则用代码添加两个路径作为搜索配置文件的路径
		1.首先将当前路径作为搜索配置文件的路径
		2.如果OfficialPath = "/etc/hyperledger/fabric"存在,则使用此路径作为搜索配置文件的路径
	*/
	} else {
    
    
		// If we get here, we should use the default paths in priority order:
		//
		// *) CWD
		// *) /etc/hyperledger/fabric

		// CWD
		AddConfigPath(v, "./")

		// And finally, the official path
		if dirExists(OfficialPath) {
    
    
			AddConfigPath(v, OfficialPath)
		}
	}

	/*调用 SetConfigName()设置配置文件名,
	  所指的的配置文件名 configName 是由参数传递进来的*/
	// Now set the configuration file.
	if v != nil {
    
    
		v.SetConfigName(configName)
	} else {
    
    
		viper.SetConfigName(configName)
	}

	return nil
}

另外需要注意的是 InitViper 第一个参数 *viper.Viper 。在InitViper 函数中,无论是添加搜索路径(使用的是 AddConfigPath 函数),还是设置要搜索的配置文件名( SetConfigName函数),都分为全局的 viper 和特定的 viper(也就是参数最终由viper.AddConfigPathviper. SetConfigName 完成是全局的; 由 v.AddConfigPathv.SetConfigName 完成则是特定的。

这样可以很方便地初始化需要单独使用 viper的模块 。如下篇文章将要分析的 orderer 模块,即是采用单独的viper来进行配置。

3.2 peer命令搜索路径和配置文件

/fabric/cmd从命令方式看,大概率是此项目主题或者工具的命令启动代码,从此入口可以找到/fabric/cmd/peer/main.go文件,代码如下,具体查看以下中文注释

// The main command describes the service and
// defaults to printing the help message.
var mainCmd = &cobra.Command{
    
    Use: "peer"}

func main() {
    
    
	// For environment variables.
	//通过viper设置前缀为 common.CmdRoot = "core"环境变量前缀名
	viper.SetEnvPrefix(common.CmdRoot)
	//通过viper获取所有的环境变量,如果设置过了前缀则会自动补全前缀名
	viper.AutomaticEnv()
	replacer := strings.NewReplacer(".", "_")
	viper.SetEnvKeyReplacer(replacer)

	// Define command-line flags that are valid for all peer commands and
	// subcommands.
	mainFlags := mainCmd.PersistentFlags()

	mainFlags.String("logging-level", "", "Legacy logging level flag")
	viper.BindPFlag("logging_level", mainFlags.Lookup("logging-level"))
	mainFlags.MarkHidden("logging-level")

	cryptoProvider := factory.GetDefault()

	mainCmd.AddCommand(version.Cmd())
	mainCmd.AddCommand(node.Cmd())
	mainCmd.AddCommand(chaincode.Cmd(nil, cryptoProvider))
	mainCmd.AddCommand(channel.Cmd(nil))
	mainCmd.AddCommand(lifecycle.Cmd(cryptoProvider))

	// On failure Cobra prints the usage message and error string, so we only
	// need to exit with a non-0 status
	if mainCmd.Execute() != nil {
    
    
		os.Exit(1)
	}
}

3.3 orderer命令如何搜索、读取配置文件

同样的,如果看/fabric/cmd/orderer/main.go文件,具体查看以下中文注释

// Main is the entry point of orderer process
func Main() {
    
    
	fullCmd := kingpin.MustParse(app.Parse(os.Args[1:]))

	// "version" command
	if fullCmd == version.FullCommand() {
    
    
		fmt.Println(metadata.GetVersionInfo())
		return
	}
	//配置文件加载入口
	conf, err := localconfig.Load()
	if err != nil {
    
    
		logger.Error("failed to parse config: ", err)
		os.Exit(1)
	}
	initializeLogging()

	prettyPrintStruct(conf)

/fabric/orderer/common/loadconfig/config.go,具体查看以下中文注释

// Load parses the orderer YAML file and environment, producing
// a struct suitable for config use, returning error on failure.
func Load() (*TopLevel, error) {
    
    
	//新建独立的viper
	config := viper.New()
	//调用 InitViper函数,configName 为配置文件名`orderer`
	coreconfig.InitViper(config, "orderer")  //`coreconfig`包就是`/fabric/core/config`包
	//通过viper设置前缀为 Prefix = "ORDERER"环境变量前缀名
	config.SetEnvPrefix(Prefix)
	//通过viper获取所有的环境变量,如果设置过了前缀则会自动补全前缀名
	config.AutomaticEnv()
	replacer := strings.NewReplacer(".", "_")
	config.SetEnvKeyReplacer(replacer)
	//读取配置文件
	if err := config.ReadInConfig(); err != nil {
    
    
		return nil, fmt.Errorf("Error reading configuration: %s", err)
	}

	var uconf TopLevel
	if err := viperutil.EnhancedExactUnmarshal(config, &uconf); err != nil {
    
    
		return nil, fmt.Errorf("Error unmarshaling config into struct: %s", err)
	}

	uconf.completeInitialization(filepath.Dir(config.ConfigFileUsed()))
	return &uconf, nil
}

4. 继续从源码分析其它模块

请见下文
Hyperledger Fabric(3) - 源码分析之Peer启动流程

猜你喜欢

转载自blog.csdn.net/u010159567/article/details/105548285
今日推荐