etcd 服务

 在应用中ETCD做以下几个方面的功能: 服务发现,配置分发,core↔burner心跳, 多core选主(cluster实现)。

 

一、etcd 服务发现

iceberg 系统的 core 如何知道有哪些可用的 burner,以及 burner 如何连接到 core,向 core 发送magazine等注册信息?

1.1、设置 key 前缀 iceberg/etcd/burner,每个 burner 启动后连接到etcd集群,注册key,iceberg/etcd/burner/burner_sn,burner_sn 为每个 burner 硬件的 SN,可唯一识别 burner。core 获取以 iceberg/etcd/burner 为前缀的所有 key,就可以知道集群中有哪些可用的 burner。

1.2、core 启动后分别设置 key,iceberg/etcd/core/core_ip,iceberg/etcd/core/core_port,burner 获取这两个 key,就可以构造 http 请求,向 core 发送消息进行注册。

二、etcd 配置分发

对于 core、burner 有很多共同的配置项,如 keystone、s3 集群配置等,为了避免每个节点都需要设置一遍,或者方便配置更新。使用 etcd 来进行配置分发,在 etcd 设置 key 即可,比如 iceberg/etcd/KeyStoneHost,core、burner watch 该 key,当 key 有变动时,可以发送事件通知各个节点。可以很方便的实现配置分发以及动态更新。

三、etcd 实现 core、burner 心跳

实现机制:节点在 etcd 上设置的 key,可以设定 ttl 超时时间,当节点宕机,等 ttl 超时,其设置的节点会自动注销,watch 该 key 的节点将收到通知,告知该节点离线。存活的节点,在 ttl 超时时间前可以发送消息更新该 tll。比如设置超时时间为 60s,每隔 1s 重新设置 key ttl。这样集群就可以知道 core、burner 节点的状态是否发生改变。

四、etcd 进行多 core 选主

为避免coreserver单点失败,iceberg支持多个coreserver构成master/slave架构的集群(cluster)。其中主从节点之间的心跳和选主流程都是通过etcd的功能实现。

4.1、etcd 选主机制

当 core 在 iceberg/etcd/elect 注册key 时,etcd 提供有序键的实现,每个 core 注册 key 时,会获取一个递增的有序键,持有最小有序键,即最早创建 key 的节点将成为 master。当 master 节点宕机,等待超时时间后,其设置的 key 将自动注销,感知 master 离开的其他节点会开始下一轮选举,查看自己是否已成为 master。

etcd client v3 的 election.go 文件对于以上功能进行了封装。

CoreEtcd.Elect = concurrency.NewElection(CoreEtcd.Session, CoreEtcd.ElectKey)
CoreEtcd.Elect.Campaign(context.Background(), conf.CoreIp)

指定 ElectKey,并设置值 conf.CoreIp,调用 Campaign 接口即可进行 master 的选举。

a、如果选主成功,该接口将返回,并且 ElectKey 下新创建的节点值为 CoreIp,burner 可以获取该 CoreIp,也就知道当前集群中的 master。

b、如果存在更早创建的节点,该接口将会 block,直到旧主离开,当前节点选举成为新的 master。

 

4.2、core 选主状态机

4.2.1、core 的状态

a、ElectionContend:

处于 contend 状态,当前将进行 core 的选举,调用 Campaign 函数,选举成功后进行 etcd core 配置更新,然后进行 keep 状态,如果此时集群中有别的 master 存在,状态机将 block 在 Campaign 接口。

b、ElectionKeep:

处于该状态时,此时 core 每次检查自己是否仍是 master,防止新主产生后,旧主因为网络故障等原因未感知 master 已切换。

c、ElectionRelease:

处于该状态,master 调用 Resign 接口,释放当前 master 节点,重新开始下一轮的选举。当前 core 释放后,将进入 null 状态

CoreEtcd.Elect.Resign(context.TODO())

d、ElectionNull:

处于该状态的 coreserver 为 slave,且没有去竞选 master,可通过设置状态到 contend 后重新参加 master 竞选

4.3、core 手动切主命令

4.3.1、释放当前 master

只有 keep 状态的 core 可以释放 master,将状态机置为 release 状态,调用 Resign 接口后,释放 master,当前 core 进入 null 状态

4.3.2、core 参加竞选

将 null 状态的 core 设置为 contend,将调用 Campaign 函数,进行 master 的选举

4.3.3、master 并不能直接指定,因为 master 的选举是通过创建 key 节点的先后顺序,只有当之前创建的所有节点都失效后,新创建的 key 才能成为 master

 

4.4、core 脑裂场景处理

场景分析:假设超时时间为 60s,网络故障后,0-59s 期间,心跳无法更新 ttl,第 59s,旧主判断自己仍是主,继续执行任务,假定任务执行耗时 5s。第 60s,旧主超时,集群选举新主,新主开始执行任务,在时间 60-64s,集群中将有两个主同时发布任务,集群脑裂,有可能造成集群数据不一致。

 

解决方案:增加容错时间段,不等旧主心跳超时后才停止工作,而是如果 core 每隔 1s 去更新 ttl 时,如果失败就停止服务,这样新主旧主切换的时间就扩大到了 59s。只要旧主执行的任务在 59s 返回就不会有脑裂的场景产生。当网络故障时,master 心跳更新失败,置节点状态为 inactive,停止服务,当网络恢复,心跳更新成功后检查。

a、如果此时自己仍是 master,置为 active,继续提供服务。

b、网络故障期间,有新主产生,置选举状态为 null,成为 slave。

 

4.5、core网络中断场景处理

core 节点进行 master 选举,首先要创建

CoreEtcd.Client, err = etcd.NewClientEtcd(common.ClusterListEtcd)
CoreEtcd.Session, err = concurrency.NewSession(CoreEtcd.Client)
CoreEtcd.Elect = concurrency.NewElection(CoreEtcd.Session, CoreEtcd.ElectKey)

当网络中断时,Session 将会失效,此时旧主可以意识到自己已经不再是 master。

etcd v3 client 中提供 CoreEtcd.Session.Done() 接口,当 session 失效时将返回一个 channel 的信号。
go func() {
select {
case <-CoreEtcd.Session.Done():

       // 不再是主

}

调用此接口可以让旧主更快的感知自己 session 超时,不再是主。而且此时之前的 session、client 都需要关闭,网络恢复后,需要重新创建才能正常工作,否则调用无效的 session、client,将会 crash。

4.6、fencing tokens

在分布式系统中,为避免多主切换时,新旧master对底层存储的访问之间存在racing condition的问题,通常需要实现fencing tokens机制。ETCD提供了Election.Rev()函数可以获得当前master的Revision值,该值单调递增可以用来做fencing tokens。

在iceberg中,由于coreserver的处理比较轻量级,经过分析,4.4中的机制能保证通常数据的一致性,因此目前尚未实现fencing tokens机制。

你可能感兴趣的:(etcd 服务)