etcd 启动分析_etcd启动流程源码分析笔记(-)

本文详细分析了etcd的启动流程,包括参数校验、监听器初始化、ServerConfig构建以及raft节点的生成。重点讨论了etcdServer的创建,如Store、Transport、raft节点的启动,并提及了不同情况下raft节点生成的逻辑。文章还提到,启动过程中会为每个client和peer URL启动goroutine以提供监听服务。
摘要由CSDN通过智能技术生成

etcd启动流程源码分析笔记(-)

1. 初始化 etcdServer 流程:

代码路径为: github.com\coreos\etcd\embed\etcd.go

StartEtcd(inCfg *Config) (e *Etcd, err error)

流程如下:

1.1: 参数校验: inCfg.Validate()

校验关注点 1:

checkBindURLs(cfg.LPUrls): 校验 peer-urls schem 相关信息, 且在 3.1 版本之后不允许使用域名作为 url 来进行绑定操作.

checkBindURLs(cfg.LCUrls) : 校验 client-urls schem 相关信息, 且在 3.1 版本之后不允许使用域名作为 url 来进行绑定操作.

使用域名对于性能上是有一定的影响, 但是在实际生产环境中, 是存在使用域名的场景, 需要修改如下代码进行适配:

func checkBindURLs(urls []url.URL) error {//...

ifnet.ParseIP(host)==nil{

// 取消 err 的 return, 改为打印告警信息, 同 3.1 之前版本.

returnfmt.Errorf("expected IP in URL for binding (%s)",url.String())

}

}

校验关注点 2: 由于实际现网的网络延迟各不相同, 选举及心跳超时时间可作为调优适配的考虑范畴.

5*cfg.TickMs> cfg.ElectionMs : 选举超时时间必须大于五倍于心跳超时时间.

cfg.ElectionMs> maxElectionMs: 选举超时时间必须小于 5000ms

1.2: 初始化 PeerListeners,ClientListeners, 用于监听 peers 间及 client 端发送的 http 请求

PeerListeners: 作为 etcd member 之间进行通信使用的 listeners, 为了性能考量, 建议内部试用 schema:http, 由 flag "listen-peer-urls" 确定,

ClientListeners: 作为接受外部请求的 listerners, 一般为了安全性考量, 一般使用 schema:https, 由 flag "listen-client-urls" 确定,

具体方法实现为:

transport.NewTimeoutListener(u.Host, u.Scheme, tlsinfo, ConnReadTimeout, ConnWriteTimeout)

默认的读写超时均为 5s:ConnReadTimeout=5*time.Second

ConnWriteTimeout=5*time.Second

1.3: 获取 PeerURLsMap 以及 cluster token

1.4: 生成 new etcdServer 所需的的 ServerConfig 结构体:// ServerConfig holds the configuration of etcd as taken from the command line or discovery.

typeServerConfigstruct{

Namestring// etcdserver 名称, 对应 flag "name"

DiscoveryURLstring// etcd 用于服务发现, 无需知道具体 etcd 节点 ip 即可访问 etcd 服务, 对应 flag "discovery"

DiscoveryProxystring// 供服务发现 url 的代理地址, 对应 flag "discovery-proxy"

ClientURLstypes.URLs// 由 ip+port 组成, 默认 DefaultListenClientURLs = "http://localhost:2379"; 实际情况使用 https schema, 供 etcd member 通信, 对应 flag "listen-client-urls"

PeerURLstypes.URLs// 由 ip+port 组成, 默认 DefaultListenPeerURLs = "http://localhost:2380"; 实际生产环境使用 http schema, 用以外部 etcd client 访问, 对应 flag "listen-client-urls"

DataDirstring// 数据目录地址, 为全路径, 对应 flag "data-dir"

// DedicatedWALDir config will make the etcd to write the WAL to the WALDir

// rather than the dataDir/member/wal.

DedicatedWALDir string

SnapCount uint64 // 默认是 10000 次事件做一次快照: DefaultSnapCount = 100000 可以作为调优参数进行参考, 对应 flag "snapshot-count",

MaxSnapFiles uint // 默认是 5, 这是 v2 的参数, v3 内只有一个 db 文件, DefaultMaxSnapshots = 5, 对应 flag "max-snapshots"

MaxWALFiles uint // 默认是 5,DefaultMaxWALs = 5, 表示最大存储 wal 文件的个数, 对应 flag "max-wals", 保留的文件可以作为 etcd-dump-logs 工具进行 debug 使用.InitialPeerURLsMaptypes.URLsMap// peerUrl 与 etcd name 对应的 map, 由方法 cfg.PeerURLsMapAndToken("etcd") 生成.

InitialClusterTokenstring// etcd 集群 token, 对应 flang "initial-cluster-token"

NewCluster bool // 确定是否为新建集群, 对应 flag "initial-cluster-state", 由方法 func (cfg Config) IsNewCluster() bool { return cfg.ClusterState == ClusterStateFlagNew } 确定;

ForceNewCluster bool // 对应 flag "force-new-cluster", 默认为 false, 若为 true, 在生产环境内, 一般用于含 v2 数据的集群恢复, 效果为以现有数据或者空数据新建一个单节点的 etcd 集群, 如果存在数据, 则会清楚数据内的元数据信息, 并重建只包含该 etcd 的元数据信息.

PeerTLSInfo transport.TLSInfo // member 间通信使用的证书信息, 若 peerURL 为 https 时使用, 对应 flag "peer-ca-file","peer-cert-file", "peer-key-file"

TickMs uint // raft node 发送心跳信息的超时时间. "heartbeat-interval"

ElectionTicks int // raft node 发起选举的超时时间, 最大为 5000ms maxElectionMs = 50000, 对应 flag "election-timeout", 选举时间与心跳时间在最佳实践内建议是 10 倍关系.BootstrapTimeouttime.Duration// etcd server 启动的超时时间, 默认为 1s, 由方法 func (c *ServerConfig) bootstrapTimeout() time.Duration 确定;

AutoCompactionRetentionint// 默认为 0, 单位为小时, 主要为了方便用户快速查询, 定时对 key 进行合并处理, 对应 flag "auto-compaction-retention", 由方法 func NewPeriodic(h int, rg RevGetter, c Compactable) *Periodic 确定,

// 具体 compact 的实现方法为: func (s *kvServer) Compact(ctx context.Context, r *pb.CompactionRequest) (*pb.CompactionResponse, error)

QuotaBackendBytesint64// etcd 后端数据文件的大小, 默认为 2GB, 最大为 8GB, v3 的参数, 对应 flag "quota-backend-bytes" , 具体定义: etcd\etcdserver\quota.go

StrictReconfigCheckbool

// ClientCertAuthEnabled is true when cert has been signed by the client CA.

ClientCertAuthEnabledbool

AuthTokenstring

}

1.5, 调用方法

func NewServer(cfg *ServerConfig) (srv *EtcdServer, err error)

初始化 etcdServer:// NewServer creates a new EtcdServer from the supplied configuration. The

// configuration is considered static for the lifetime of the EtcdServer.

funcNewServer(cfg*ServerConfig)(srv*EtcdServer,err error){

}

1.5.1: 分配内存空间

st := store.New(StoreClusterPrefix, StoreKeysPrefix)

1.5.2: 检测并生成数据目录, 生成向远端 raft node peer listeners 发送请求的 Transport

其中的超时时间计算方法为:time.Second+time.Duration(c.ElectionTicks)*time.Duration(c.TickMs)*time.Millisecond/5

1.5.3:

根据日志目录是否存在, 对应生成 raft node 实体.

1.5.3.1: 若日志目录不存在且 flag "initial-cluster-state" 为'existing':

case !haveWAL && !cfg.NewCluster:

使用方法

func startNode(cfg *ServerConfig, cl *membership.RaftCluster, ids []types.ID) (id types.ID, n raft.Node, s *raft.MemoryStorage, w *wal.WAL)

生成 raft node 实体id,

n,

s,

w=startNode(cfg,cl,nil)

1.5.3.2: 若日志目录不存在且 flag "initial-cluster-state" 为'new':

case !haveWAL && cfg.NewCluster:

使用方法

func startNode(cfg *ServerConfig, cl *membership.RaftCluster, ids []types.ID) (id types.ID, n raft.Node, s *raft.MemoryStorage, w *wal.WAL)

生成 raft node 实体

id, n, s, w = startNode(cfg, cl, cl.MemberIDs())

1.5.3.3 若日志目录存在:

1.5.3.3.1 若 flag "force-new-cluster" 为 "false":

调用方法

func restartNode(cfg *ServerConfig, snapshot *raftpb.Snapshot) (types.ID, *membership.RaftCluster, raft.Node, *raft.MemoryStorage, *wal.WAL)

生成 raft node 实体id,

cl,

n,

s,

w=restartNode(cfg,snapshot)

1.5.3.3.2 若 flag "force-new-cluster" 为 "true":

调用方法

func restartAsStandaloneNode(cfg *ServerConfig, snapshot *raftpb.Snapshot) (types.ID, *membership.RaftCluster, raft.Node, *raft.MemoryStorage, *wal.WAL)

生成 raft node 实体id,

cl,

n,

s,

w=restartAsStandaloneNode(cfg,snapshot)

1.5.4 初始化 EtcdServer:srv=&EtcdServer{

readych:make(chanstruct{}),

Cfg:cfg,

snapCount:cfg.SnapCount,

errorc:make(chan error,1),

store:st,

snapshotter:ss,

r:*newRaftNode(raftNodeConfig{

isIDRemoved:func(id uint64)bool{

returncl.IsIDRemoved(types.ID(id))

},

Node:n,

heartbeat:heartbeat,

raftStorage:s,

storage:NewStorage(w,ss),

},

),

id:id,

attributes:membership.Attributes{

Name:cfg.Name,

ClientURLs:cfg.ClientURLs.StringSlice()

},

cluster:cl,

stats:sstats,

lstats:lstats,

SyncTicker:time.NewTicker(500*time.Millisecond),

peerRt:prt,

reqIDGen:idutil.NewGenerator(uint16(id),time.Now()),

forceVersionC:make(chanstruct{}),

....

}

在初始化 EtcdServer 过程中, 会启动用于 peer 间发送及接收 raft 消息的 rafthttp transport, 具体方法如下:func(t*Transport)Start()error{

varerr error

t.streamRt,err=newStreamRoundTripper(t.TLSInfo,t.DialTimeout)

iferr!=nil{

returnerr

}

t.pipelineRt,err=NewRoundTripper(t.TLSInfo,t.DialTimeout)

iferr!=nil{

returnerr

}

t.remotes=make(map[types.ID]*remote)

t.peers=make(map[types.ID]Peer)

t.prober=probing.NewProber(t.pipelineRt)

returnnil

2.1. 启动 etcdServer

3.1. 为每个 client url 及 peer url 启动一个 client server 的 goroutine, 以提供监听服务, 这个动作在 raft http transport 启动之后:peer server goroutine:

go func(l*peerListener){

e.errHandler(l.serve())

}(pl)

client server goroutine:

go func(s*serveCtx){

e.errHandler(s.serve(e.Server,ctlscfg,v2h,e.errHandler))

}(sctx)

若启动失败, 则停止 grpcServer:defer func(){

...

if!serving{

// errored before starting gRPC server for serveCtx.grpcServerC

for_,sctx:=range e.sctxs{

close(sctx.grpcServerC)

}

}

...

}()

暂时就启动流程进行粗略分享, 后续将进一步分析 etcdServer 启动具体机制, 及针对 NewServer 内针对生成 raft node 详细机制进行分析及基于 k8s 平台部署 etcd 集群备份恢复方案进行探讨.

来源: http://www.tuicool.com/articles/yAre63J

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值