【ETCD】【源码解读】Etcd启动阶段配置解析跟踪

一、newConfig()

newConfig() 是 ETCD 启动过程中的一个重要函数,它用于初始化和配置 ETCD 的各种配置项。该函数构造了一个 config 类型的实例,并为其配置了多个启动参数、标志和默认值。接下来,我们将逐行解析该函数的具体实现。


1. 创建 config 实例

go 复制代码
cfg := &config{
    ec:      *embed.NewConfig(),
    ignored: ignored,
}
  • cfg := &config{...} :创建一个新的 config 结构体实例,并为其各个字段赋予默认值。
  • ec: *embed.NewConfig() :初始化 embed.Config,这是 ETCD 服务的配置,包含了网络监听、存储路径等基础配置。
  • ignored: ignoredignored 是一个包含需要忽略的标志名称的字符串切片。它用于存储那些不会被解析和使用的标志。

2. 初始化 configFlags 结构体

go 复制代码
cfg.cf = configFlags{
    flagSet: flag.NewFlagSet("etcd", flag.ContinueOnError),
    clusterState: flags.NewSelectiveStringValue(
        embed.ClusterStateFlagNew,
        embed.ClusterStateFlagExisting,
    ),
    fallback: flags.NewSelectiveStringValue(
        fallbackFlagExit,
        fallbackFlagProxy,
    ),
    v2deprecation: flags.NewSelectiveStringsValue(
        string(cconfig.V2Depr1WriteOnly),
        string(cconfig.V2Depr1WriteOnlyDrop),
        string(cconfig.V2Depr2Gone)),
}
  • flagSet: flag.NewFlagSet("etcd", flag.ContinueOnError) :创建一个新的 FlagSet 实例,该实例用于解析命令行参数。"etcd" 是标志集的名称,flag.ContinueOnError 表示在遇到错误时继续执行(不退出)。

  • clusterState: flags.NewSelectiveStringValue(...)clusterState 是一个标志,表示集群的状态。它使用 NewSelectiveStringValue 初始化,支持选择值 "new"(新集群)或 "existing"(已有集群)。这个标志告诉 ETCD 是启动一个新集群还是加入现有集群。

  • fallback: flags.NewSelectiveStringValue(...)fallback 是一个标志,控制发现失败时的处理方式。它的可选值为 "exit""proxy",决定在发现失败时退出或继续作为代理。

  • v2deprecation: flags.NewSelectiveStringsValue(...)v2deprecation 是一个包含多个选择值的标志,用于控制 ETCD v2 API 的弃用阶段。它使用 NewSelectiveStringsValue 初始化,支持不同的弃用阶段(如 "write-only""write-only-drop""gone")。


3. 定义 Usage 函数

go 复制代码
fs := cfg.cf.flagSet
fs.Usage = func() {
    fmt.Fprintln(os.Stderr, usageline)
}
  • fs := cfg.cf.flagSet :获取 flagSet,该实例用于解析命令行标志。
  • fs.Usage = func() :定义 Usage 函数,当用户输入 -h 或无效的标志时,Usage 会输出帮助信息。此处的 usageline 是预定义的帮助文本。

4. 添加标志和参数

go 复制代码
cfg.ec.AddFlags(fs)
  • cfg.ec.AddFlags(fs) :将 embed.Config 的标志添加到 flagSet 中。AddFlags 函数会将 ETCD 服务相关的配置项(如端口、数据目录等)添加到 flagSet,使得它们可以通过命令行传递。

5. 配置命令行标志

go 复制代码
fs.StringVar(&cfg.configFile, "config-file", "", "Path to the server configuration file. Note that if a configuration file is provided, other command line flags and environment variables will be ignored.")
  • fs.StringVar(&cfg.configFile, "config-file", "") :添加 --config-file 标志,指定配置文件路径。该文件的内容会覆盖命令行参数和环境变量的设置。
go 复制代码
fs.Var(cfg.cf.fallback, "discovery-fallback", fmt.Sprintf("Valid values include %q", cfg.cf.fallback.Valids()))
fs.Var(cfg.cf.clusterState, "initial-cluster-state", "Initial cluster state ('new' when bootstrapping a new cluster or 'existing' when adding new members to an existing cluster). After successful initialization (bootstrapping or adding), flag is ignored on restarts.")
fs.Var(cfg.cf.v2deprecation, "v2-deprecation", fmt.Sprintf("v2store deprecation stage: %q. ", cfg.cf.v2deprecation.Valids()))
  • fs.Var :为 fallbackclusterStatev2deprecation 配置标志。每个标志都绑定到 configFlags 结构体中的相应字段,并为用户提供合适的描述。
go 复制代码
fs.BoolVar(&cfg.printVersion, "version", false, "Print the version and exit.")
  • fs.BoolVar(&cfg.printVersion, "version", false) :添加 --version 标志,用于输出 ETCD 的版本信息并退出。

6. 处理 ignored 标志

go 复制代码
for _, f := range cfg.ignored {
    fs.Var(&flags.IgnoredFlag{Name: f}, f, "")
}
  • for _, f := range cfg.ignored :遍历 ignored 字段(一个包含不需要处理的标志的切片),并为每个标志创建一个忽略的 flag

7. 返回配置实例

go 复制代码
return cfg
  • return cfg :函数结束时返回配置实例 cfg。该实例包含了 ETCD 启动时的所有配置,包括命令行标志、配置文件、集群信息等。

二、parse()函数解析

parse 函数是 config 结构体的方法,负责解析传入的命令行参数,处理配置文件和环境变量,并根据解析结果返回相应的错误或配置。下面是该函数的详细逐行解析:


函数签名:

go 复制代码
func (cfg *config) parse(arguments []string) error
  • cfg *config :接收一个指向 config 结构体的指针,parse 方法修改该配置实例。
  • arguments []string :命令行参数的切片,通常是 os.Args[1:]

1. 解析命令行参数:

go 复制代码
perr := cfg.cf.flagSet.Parse(arguments)
  • cfg.cf.flagSet.Parse(arguments) :解析传入的命令行参数 argumentsflagSet 是配置中的 FlagSet,负责命令行标志的解析。
  • 返回值 perr :解析过程中返回的错误,如果没有错误,perrnil

2. 错误处理:

go 复制代码
switch {
case perr == nil:
case errors.Is(perr, flag.ErrHelp):
    fmt.Println(flagsline)
    os.Exit(0)
default:
    os.Exit(2)
}
  • perr == nil:如果解析成功,继续执行。
  • errors.Is(perr, flag.ErrHelp) :如果错误是 flag.ErrHelp(用户请求帮助信息),则打印帮助文本 flagsline,并正常退出。
  • default:如果发生其他错误,程序退出并返回状态码 2。

3. 检查无效的标志:

go 复制代码
if len(cfg.cf.flagSet.Args()) != 0 {
    return fmt.Errorf("%q is not a valid flag", cfg.cf.flagSet.Arg(0))
}
  • cfg.cf.flagSet.Args():获取所有未被解析的命令行参数。
  • 如果还有未解析的参数(即无效的标志),则返回一个错误,指明该标志无效。

4. 打印版本信息:

go 复制代码
if cfg.printVersion {
    fmt.Printf("etcd Version: %s\n", version.Version)
    fmt.Printf("Git SHA: %s\n", version.GitSHA)
    fmt.Printf("Go Version: %s\n", runtime.Version())
    fmt.Printf("Go OS/Arch: %s/%s\n", runtime.GOOS, runtime.GOARCH)
    os.Exit(0)
}
  • cfg.printVersion :如果 --version 标志被设置为 true,则打印 ETCD 的版本信息、Git 哈希值、Go 版本以及操作系统架构信息。
  • os.Exit(0):打印完版本信息后正常退出程序。

5. 解析配置文件:

go 复制代码
if cfg.configFile == "" {
    cfg.configFile = os.Getenv(flags.FlagToEnv("ETCD", "config-file"))
}
  • cfg.configFile :检查是否提供了配置文件路径(--config-file)。
  • 如果没有提供配置文件路径,则尝试从环境变量中获取 ETCD_CONFIG_FILE 值。

6. 从配置文件加载配置:

go 复制代码
if cfg.configFile != "" {
    err = cfg.configFromFile(cfg.configFile)
    if lg := cfg.ec.GetLogger(); lg != nil {
        lg.Info(
            "loaded server configuration, other configuration command line flags and environment variables will be ignored if provided",
            zap.String("path", cfg.configFile),
        )
    }
} else {
    err = cfg.configFromCmdLine()
}
  • cfg.configFile != "" :如果指定了配置文件,调用 configFromFile 加载配置文件内容。
  • cfg.configFromFile(cfg.configFile):读取配置文件并应用到配置结构体。
  • 如果没有配置文件,使用命令行参数(configFromCmdLine)来加载配置。

7. 设置 ETCD v2 相关弃用配置:

go 复制代码
if cfg.ec.V2Deprecation == "" {
    cfg.ec.V2Deprecation = cconfig.V2DeprDefault
}
  • cfg.ec.V2Deprecation :如果未设置 v2 API 弃用配置,设置为默认值 cconfig.V2DeprDefault

8. 解析 WarningUnaryRequestDuration 标志:

go 复制代码
cfg.ec.WarningUnaryRequestDuration, perr = cfg.parseWarningUnaryRequestDuration()
if perr != nil {
    return perr
}
  • cfg.parseWarningUnaryRequestDuration() :解析 --warning-unary-request-duration 标志,用于设置警告阈值。
  • 如果解析出错,返回错误。

9. 处理弃用的标志:

go 复制代码
var warningsForDeprecatedFlags []string
cfg.cf.flagSet.Visit(func(f *flag.Flag) {
    if msg, ok := deprecatedFlags[f.Name]; ok {
        warningsForDeprecatedFlags = append(warningsForDeprecatedFlags, msg)
    }
})
if len(warningsForDeprecatedFlags) > 0 {
    if lg := cfg.ec.GetLogger(); lg != nil {
        for _, msg := range warningsForDeprecatedFlags {
            lg.Warn(msg)
        }
    }
}
  • cfg.cf.flagSet.Visit:遍历所有解析的标志,检查是否有弃用的标志。
  • deprecatedFlags:包含弃用标志的映射。
  • 日志警告:如果有弃用的标志,记录警告日志。

10. 返回配置解析错误:

go 复制代码
return err
  • 如果过程中没有错误,返回 nil,表示解析成功。如果出现任何错误,将返回错误。

三、总结:

newConfig() 函数的主要作用是创建并初始化一个包含 ETCD 启动配置的实例。它负责:

  • 创建并配置命令行标志。
  • 加载和验证配置。
  • 提供 ETCD 启动时所需的所有配置信息。

该函数通过 flagSet 处理所有命令行标志,提供给 ETCD 启动过程所需的配置信息。

  • 命令行参数解析parse 方法主要负责解析命令行参数、配置文件和环境变量。
  • 版本信息输出:如果用户请求,打印 ETCD 的版本信息并退出。
  • 配置文件加载:支持从配置文件和命令行参数加载配置。
  • 弃用标志处理:检查是否使用了弃用的命令行标志,并记录警告。
  • 错误处理:在遇到无效标志或其他错误时,及时返回错误并中止。

该函数的目的是确保配置的正确性并处理所有启动时的参数设置。

相关推荐
python机器学习建模3 小时前
科研论文必须要了解的25个学术网站
数据库
J.P.August4 小时前
Oracle DataGuard启动与关闭顺序
数据库·oracle
尚雷55804 小时前
Oracle 与 达梦 数据库 对比
数据库·oracle·达梦数据库
keenx5 小时前
PHP无法读取.env的配置变量原因
php·laravel
小猿姐5 小时前
Ape-DTS:开源 DTS 工具,助力自建 MySQL、PostgreSQL 迁移上云
数据库·mysql·postgresql·开源
百香果果ccc5 小时前
MySQL中的单行函数和聚合函数
数据库·mysql
摸摸陌陌5 小时前
Redis快速入门
数据库·redis·缓存
Elastic 中国社区官方博客6 小时前
Elasticsearch Serverless 中的数据流自动分片
大数据·数据库·elasticsearch·搜索引擎·serverless·时序数据库
Minyy116 小时前
牛客网刷题SQL--高级查询
数据库·sql
秋意钟6 小时前
MySQL基本架构
数据库·mysql·架构