etcdmain/main.go
func Main() {
checkSupportArch()
if len(os.Args) > 1 {
cmd := os.Args[1]
if covArgs := os.Getenv("ETCDCOV_ARGS"); len(covArgs) > 0 {
args := strings.Split(os.Getenv("ETCDCOV_ARGS"), "\xe7\xcd")[1:]
rootCmd.SetArgs(args)
cmd = "grpc-proxy"
}
switch cmd {
case "gateway", "grpc-proxy":
if err := rootCmd.Execute(); err != nil {
fmt.Fprint(os.Stderr, err)
os.Exit(1)
}
return
}
}
startEtcdOrProxyV2()
}
为理解这段代码,我们先看一下etcd启动的参数
./bin/etcd --help
Usage:
etcd [flags]
Start an etcd server.
etcd --version
Show the version of etcd.
etcd -h | --help
Show the help information about etcd.
etcd --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.
etcd gateway
Run the stateless pass-through etcd TCP connection forwarding proxy.
etcd grpc-proxy
Run the stateless etcd v3 gRPC L7 reverse proxy.
而官方推荐的启动命令:
On each etcd node, specify the cluster members:
TOKEN=token-01
CLUSTER_STATE=new
NAME_1=machine-1
NAME_2=machine-2
NAME_3=machine-3
HOST_1=10.240.0.17
HOST_2=10.240.0.18
HOST_3=10.240.0.19
CLUSTER=${NAME_1}=http://${HOST_1}:2380,${NAME_2}=http://${HOST_2}:2380,${NAME_3}=http://${HOST_3}:2380
Run this on each machine:
# For machine 1
THIS_NAME=${NAME_1}
THIS_IP=${HOST_1}
etcd --data-dir=data.etcd --name ${THIS_NAME} \
--initial-advertise-peer-urls http://${THIS_IP}:2380 --listen-peer-urls http://${THIS_IP}:2380 \
--advertise-client-urls http://${THIS_IP}:2379 --listen-client-urls http://${THIS_IP}:2379 \
--initial-cluster ${CLUSTER} \
--initial-cluster-state ${CLUSTER_STATE} --initial-cluster-token ${TOKEN}
# For machine 2
THIS_NAME=${NAME_2}
THIS_IP=${HOST_2}
etcd --data-dir=data.etcd --name ${THIS_NAME} \
--initial-advertise-peer-urls http://${THIS_IP}:2380 --listen-peer-urls http://${THIS_IP}:2380 \
--advertise-client-urls http://${THIS_IP}:2379 --listen-client-urls http://${THIS_IP}:2379 \
--initial-cluster ${CLUSTER} \
--initial-cluster-state ${CLUSTER_STATE} --initial-cluster-token ${TOKEN}
# For machine 3
THIS_NAME=${NAME_3}
THIS_IP=${HOST_3}
etcd --data-dir=data.etcd --name ${THIS_NAME} \
--initial-advertise-peer-urls http://${THIS_IP}:2380 --listen-peer-urls http://${THIS_IP}:2380 \
--advertise-client-urls http://${THIS_IP}:2379 --listen-client-urls http://${THIS_IP}:2379 \
--initial-cluster ${CLUSTER} \
--initial-cluster-state ${CLUSTER_STATE} --initial-cluster-token ${TOKEN}
我们没有定义ETCDCOV_ARGS,也没有grpc-gateway或者grpc-proxy,实际上测试环境直接就是
etcd
啥参数也没有,所以接下来的流程就到了startEtcdOrProxyV2()
etcdmain/etcd.go
#56: startEtcdOrProxyV2()
这个函数太长了,源码就不贴了
- 先生成一个默认的配置
// config holds the config for a command line invocation of etcd
type config struct {
ec embed.Config
cp configProxy
cf configFlags
configFile string
printVersion bool
ignored []string
}
这里embed.Config就是etcdserver的配置,同时etcd也可以当作主要程序的一部分运行而不用单独启动一个进程。
- 检查data-dir里面的数据,如果已经有数据但是与当前数据不符,比如这个dir之前用来保存proxy启动的etcd,现在又要求以member形式启动,那么就不行,panic完事
- 检查完毕,开启etcd server
startEtcd
这是以member形式启动etcd
func startEtcd(cfg *embed.Config) (<-chan struct{}, <-chan error, error) {
e, err := embed.StartEtcd(cfg)
if err != nil {
return nil, nil, err
}
osutil.RegisterInterruptHandler(e.Close)
select {
case <-e.Server.ReadyNotify(): // wait for e.Server to join the cluster
case <-e.Server.StopNotify(): // publish aborted from 'ErrStopped'
}
return e.Server.StopNotify(), e.Err(), nil
}
这个函数显示了etcd的主要逻辑,以embed的形式启动etcd,其实etcd作为单独进程启动跟作为embed启动是一个流程。单独启动就是启动一个进程运行embed
等待退出
select {
case lerr := <-errc:
// fatal out on listener errors
if lg != nil {
lg.Fatal("listener failed", zap.Error(lerr))
} else {
plog.Fatal(lerr)
}
case <-stopped:
}
etcdmain文件夹
总的来说etcdmain目录作为main函数的下一个目的地,主要实现的逻辑有:
- 命令检查,确定是启动member模式还是proxy模式
- 参数检查,根据服务模式获取需要运行的参数
- 调用具体的服务实现函数,交由接下来的embed
- 等待信号,关闭资源,退出
etcdmain/
├── config.go
├── config_test.go
├── doc.go
├── etcd.go
├── gateway.go
├── grpc_proxy.go
├── help.go
├── main.go
└── util.go
config.go:定义配置以及解析
etcd.go:启动embed etcd前后的准备和收尾
gateway.go, grpc_proxy.go: proxy模式的进程
help.go: --help的输出
main.go:确定是proxy还是member模式运行etcd
util.go:根据dns名字获取ip