此篇是和上一篇的区别是上一篇的 EtcdServer.Start供外部调用,表示启动 EtcdServer。而这篇的EtcdServer.start函数是一个私有(private 或包内可见)方法,执行具体的服务器启动逻辑。
【ETCD】【源码阅读】深入解析 etcd 的 EtcdServer.Start 函数
在 etcd 的代码中,EtcdServer.start
是一个关键的初始化方法,它准备服务器的运行环境并启动主循环(s.run
),为 etcd 的高效运行奠定了基础。本文将从源码层面逐步分析该函数的实现细节。
函数签名及作用
func (s *EtcdServer) start() {...
}
- 核心作用:初始化服务器的必要状态,为后续服务运行提供基础。
- 运行模式:函数本身为单线程运行,但最终通过
go s.run()
启动并发主循环。 - 安全性:在调用
start
后,不应再直接修改服务器的字段。
源码分步解析
1. 日志记录器初始化
lg := s.Logger()
- 作用:获取当前服务器的日志记录器(
zap.Logger
)。 - 目的:后续步骤的日志输出统一由该记录器处理。
2. 快照相关配置初始化
if s.Cfg.SnapshotCount == 0 {lg.Info("updating snapshot-count to default",zap.Uint64("given-snapshot-count", s.Cfg.SnapshotCount),zap.Uint64("updated-snapshot-count", DefaultSnapshotCount),)s.Cfg.SnapshotCount = DefaultSnapshotCount
}
if s.Cfg.SnapshotCatchUpEntries == 0 {lg.Info("updating snapshot catch-up entries to default",zap.Uint64("given-snapshot-catchup-entries", s.Cfg.SnapshotCatchUpEntries),zap.Uint64("updated-snapshot-catchup-entries", DefaultSnapshotCatchUpEntries),)s.Cfg.SnapshotCatchUpEntries = DefaultSnapshotCatchUpEntries
}
- 功能:
- 如果未设置
SnapshotCount
和SnapshotCatchUpEntries
,使用默认值。
- 如果未设置
- 背景:
SnapshotCount
:触发快照生成的日志条目数。SnapshotCatchUpEntries
:快照之后保留的日志条目数,用于提高容错能力。
3. 等待队列和通知器初始化
s.w = wait.New()
s.applyWait = wait.NewTimeList()
- 作用:
- 初始化等待队列(
wait.New
)和时间通知器(wait.NewTimeList
)。
- 初始化等待队列(
- 用途:
- 等待队列用于控制异步操作。
- 时间通知器用于管理带有超时的操作。
4. 服务器上下文控制初始化
s.done = make(chan struct{})
s.stop = make(chan struct{})
s.stopping = make(chan struct{}, 1)
s.ctx, s.cancel = context.WithCancel(context.Background())
- 功能:
- 创建多个用于控制服务器状态的
channel
和上下文。
- 创建多个用于控制服务器状态的
- 详细分析:
done
:表示服务器完成的信号。stop
:触发服务器停止的信号。stopping
:非阻塞信号,用于标识服务器是否正在停止。ctx
和cancel
:用于管理整个服务器生命周期的上下文控制。
5. 读写相关组件初始化
s.readwaitc = make(chan struct{}, 1)
s.readNotifier = newNotifier()
s.leaderChanged = notify.NewNotifier()
- 作用:
- 初始化与读操作和领导者状态变化相关的组件。
- 详细分析:
readwaitc
:通知等待中的读操作。readNotifier
:通知机制,用于触发阻塞等待。leaderChanged
:监听领导者变更事件。
6. 集群版本日志记录
if s.ClusterVersion() != nil {lg.Info("starting etcd server",zap.String("local-member-id", s.MemberID().String()),zap.String("local-server-version", version.Version),zap.String("cluster-id", s.Cluster().ID().String()),zap.String("cluster-version", version.Cluster(s.ClusterVersion().String())),)membership.ClusterVersionMetrics.With(prometheus.Labels{"cluster_version": version.Cluster(s.ClusterVersion().String())}).Set(1)
} else {lg.Info("starting etcd server",zap.String("local-member-id", s.MemberID().String()),zap.String("local-server-version", version.Version),zap.String("cluster-version", "to_be_decided"),)
}
- 功能:
- 日志记录本地成员 ID、服务器版本和集群版本。
- 如果集群版本未知,标记为
to_be_decided
。
- 用途:
- 为调试和监控提供关键信息。
- 更新 Prometheus 的集群版本指标(
membership.ClusterVersionMetrics
)。
7. 启动主循环
go s.run()
- 功能:
- 启动
s.run
方法,将服务器的主循环放到一个单独的 goroutine 中。
- 启动
- 详解:
- 主循环的核心职责包括处理 Raft 协议、客户端请求和定期任务。
- 更多细节请参见 etcd 主循环(
run
)源码分析。
总结
EtcdServer.start
方法的设计充分体现了 etcd 的高性能和高可靠性:
-
初始化阶段:
- 使用默认值和配置确保服务器稳定启动。
- 各种信号和上下文管理机制为服务器的生命周期提供了精确控制。
-
日志与监控:
- 详细的日志记录和 Prometheus 指标更新使得调试和性能分析更加方便。
-
并发架构:
- 通过 goroutine 启动主循环,实现高效非阻塞的服务处理。
此方法是 etcd 服务器启动的重要一环,它为服务器提供了一个可靠的启动基础,同时也为后续操作铺平了道路。