启动流程的图如下:
1、主函数入口
ETCD 启动的入口在 etcd/server/main.go
文件中。
package main
import (
"os"
"go.etcd.io/etcd/server/v3/etcdmain"
)
func main() {
etcdmain.Main(os.Args)
}
这里调用了 etcdmain.Main()
,这是 ETCD 的实际启动逻辑。
2、etcdmain.Main(os.Args)
详解
代码源文件:etcd/server/v3/etcdmain.main.go
func Main(args []string) {
// 1. 检查系统架构支持
checkSupportArch()
if len(args) > 1 {
// // 2. 检查命令行参数
cmd := args[1]
switch cmd {
case "gateway", "grpc-proxy": // // 3. 判断是否运行网关或 gRPC 代理模式
if err := rootCmd.Execute(); err != nil {
fmt.Fprint(os.Stderr, err)
os.Exit(1)
}
return
}
}
startEtcdOrProxyV2(args) // // 4. 启动普通 ETCD 节点或代理节点
}
2.1.checkSupportArch()
- 功能:检查当前运行的系统架构是否被 ETCD 支持。
- 目的:确保程序在不支持的架构上不会运行,例如特定 ARM 版本可能不被支持。
2.2. 检查命令行参数:
args
是启动时传递的命令行参数,args[0]
通常是可执行文件名,args[1]
是实际命令。
2.3. 启动不同模式:
- 如果命令为
"gateway"
或"grpc-proxy"
,表示要运行 ETCD 的网关或 gRPC 代理模式。 - 执行
rootCmd.Execute()
,启动对应的命令逻辑。 rootCmd
是 Cobra 框架定义的根命令,包含所有子命令和配置。
2.4. 启动普通节点或代理节点:
-
如果没有匹配到特殊命令,调用
startEtcdOrProxyV2(args)
。 -
功能:根据配置和参数,决定是启动标准 ETCD 节点还是运行代理模式。
接下来可以深入分析
startEtcdOrProxyV2
函数,该函数负责实际启动 ETCD 实例或代理模式。它会加载配置、初始化组件,并启动 Raft、MVCC 等核心模块。
3、startEtcdOrProxyV2(args)
详解
func startEtcdOrProxyV2(args []string) {
// 禁用 gRPC 跟踪,优化性能
grpc.EnableTracing = false
// 创建一个新的配置对象
cfg := newConfig()
// 保存初始集群配置,方便后续处理
defaultInitialCluster := cfg.ec.InitialCluster
// 解析传入的命令行参数,将配置赋值给 cfg
err := cfg.parse(args[1:])
// 初始化日志记录器,确保所有输出都写入日志。
lg := cfg.ec.GetLogger()
// If we failed to parse the whole configuration, print the error using
// preferably the resolved logger from the config,
// but if does not exists, create a new temporary logger.
if lg == nil {
var zapError error
// use this logger
lg, zapError = logutil.CreateDefaultZapLogger(zap.InfoLevel)
if zapError != nil {
fmt.Printf("error creating zap logger %v", zapError)
os.Exit(1)
}
}
lg.Info("Running: ", zap.Strings("args", args))
if err != nil {
lg.Warn("failed to verify flags", zap.Error(err))
switch {
case errorspkg.Is(err, embed.ErrUnsetAdvertiseClientURLsFlag):
lg.Warn("advertise client URLs are not set", zap.Error(err))
}
os.Exit(1)
}
cfg.ec.SetupGlobalLoggers()
defer func() {
logger := cfg.ec.GetLogger()
if logger != nil {
logger.Sync()
}
}()
// 解析默认集群地址
defaultHost, dhErr := (&cfg.ec).UpdateDefaultClusterFromName(defaultInitialCluster)
if defaultHost != "" {
lg.Info(
"detected default host for advertise",
zap.String("host", defaultHost),
)
}
if dhE