认识分布式系统etcd

一 etcd的作用

     etcd是一个高可用的键值存储系统,主要用于共享配置和服务发现。etcd是由CoreOS开发并维护的,灵感来自于 ZooKeeper 和 Doozer,它使用Go语言编写,并通过Raft一致性算法处理日志复制以保证强一致性。Raft是一个来自Stanford的新的一致性算法,适用于分布式系统的日志复制,Raft通过选举的方式来实现一致性,在Raft中,任何一个节点都可能成为Leader。Google的容器集群管理系统Kubernetes、开源PaaS平台Cloud Foundry和CoreOS的Fleet都广泛使用了etcd。

     etcd 集群的工作原理基于 raft 共识算法 (The Raft Consensus Algorithm)。etcd 在 0.5.0 版本中重新实现了 raft 算法,而非像之前那样依赖于第三方库go-raft。raft 共识算法的优点在于可以在高效的解决分布式系统中各个节点日志内容一致性问题的同时,也使得集群具备一定的容错能力。即使集群中出现部分节点故障、网络故障等问题,仍可保证其余大多数节点正确的步进。甚至当更多的节点(一般来说超过集群节点总数的一半)出现故障而导致集群不可用时,依然可以保证节点中的数据不会出现错误的结果。

     Raft是一个新的一致性算法,适用于分布式系统的日志复制,Raft通过选举的方式来实现一致性。Google的容器集群管理系统Kubernetes、开源PaaS平台Cloud Foundry和CoreOS的Fleet都广泛使用了etcd。在分布式系统中,如何管理节点间的状态一直是一个难题,etcd像是专门为集群环境的服务发现和注册而设计,它提供了数据TTL失效、数据改变监视、多值、目录监听、分布式锁原子操作等功能,可以方便的跟踪并管理集群节点的状态。

二 etc和zookeeper的区别

  1 一致性协议: ETCD使用[Raft]协议, ZK使用ZAB(类PAXOS协议),前者容易理解,方便工程实现;

  2 运维方面:ETCD方便运维,ZK难以运维;

  3 项目活跃度:ETCD社区与开发活跃,ZK已经快死了;

  4 API:ETCD提供HTTP+JSON, gRPC接口,跨平台跨语言,ZK需要使用其客户端;

  5 访问安全方面:ETCD支持HTTPS访问,ZK在这方面缺失;

  更多更详细的区别:

    1  Zookeeper和Etcd的那些事儿

    2  常用的服务发现对比(Consul、zookeeper、etcd、eureka)

    3 Zookeeper和etcd比较

三 etcd的使用场景

 1 服务注册于发现(Service Discovery)
     服务发现要解决的也是分布式系统中最常见的问题之一,即在同一个分布式集群中的进程或服务,要如何才能找到对方并建立连接。本质上来说,服务发现就是想要了解集群中是否有进程在监听 udp 或 tcp 端口,并且通过名字就可以查找和连接。。

 2 消息发布与订阅

    在分布式系统中,最适用的一种组件间通信方式就是消息发布与订阅。即构建一个配置共享中心,数据提供者在这个配置中心发布消息,而消息使用者则订阅他们关心的主题,一旦主题有消息发布,就会实时通知订阅者。通过这种方式可以做到分布式系统配置的集中式管理与动态更新。类似于redis的pub sub但是其实现上有很大的不同。

3 负载均衡

     本文所指的负载均衡均为软负载均衡。分布式系统中,为了保证服务的高可用以及数据的一致性,通常都会把数据和服务部署多份,以此达到对等服务,即使其中的某一个服务失效了,也不影响使用。由此带来的坏处是数据写入性能下降,而好处则是数据访问时的负载均衡。因为每个对等服务节点上都存有完整的数据,所以用户的访问流量就可以分流到不同的机器上。

4 分布式通知与协调

   这里说到的分布式通知与协调,与消息发布和订阅有些相似。都用到了 etcd 中的 Watcher 机制,通过注册与异步通知机制,实现分布式环境下不同系统之间的通知与协调,从而对数据变更做到实时处理。实现方式通常是这样:不同系统都在 etcd 上对同一个目录进行注册,同时设置 Watcher 观测该目录的变化(如果对子目录的变化也有需要,可以设置递归模式),当某个系统更新了 etcd 的目录,那么设置了 Watcher 的系统就会收到通知,并作出相应处理。

5 分布式锁

    因为 etcd 使用 Raft 算法保持了数据的强一致性,某次操作存储到集群中的值必然是全局一致的,所以很容易实现分布式锁。锁服务有两种使用方式,一是保持独占,二是控制时序。

6 分布式队列

    分布式队列的常规用法与场景五中所描述的分布式锁的控制时序用法类似,即创建一个先进先出的队列,保证顺序。

更详细的应用信息见文章:etcd:从应用场景到实现原理的全方位解读

四 实现原理

1 raft算法

    ETCD使用Raft协议来维护集群内各个节点状态的一致性。简单说,ETCD集群是一个分布式系统,由多个节点相互通信构成整体对外服务,每个节点都存储了完整的数据,并且通过Raft协议保证每个节点维护的数据是一致的,

  Raft协议主要分为三个部分:选主,日志复制,安全性。了解raft算法:共识算法:Raft

2 etcd 整体架构
   ETCD-SERVER 大体上可以分为网络层,Raft模块,复制状态机,存储模块,架构图如图所示。

  

 

从 etcd 的架构图中我们可以看到,etcd 主要分为四个部分。

(1)HTTP Server: 用于处理用户发送的 API 请求以及其它 etcd 节点的同步与心跳信息请求。

(2)Store:用于处理 etcd 支持的各类功能的事务,包括数据索引、节点状态变更、监控与反馈、事件处理与执行等等,是 etcd 对用户提供的大多数 API 功能的具体实现。

(3)Raft:Raft 强一致性算法的具体实现,是 etcd 的核心。

(4)WAL:Write Ahead Log(预写式日志),是 etcd 的数据存储方式。除了在内存中存有所有数据的状态以及节点的索引以外,etcd 就通过 WAL 进行持久化存储。WAL 中,所有的数据提交前都会事先记录日志。Snapshot 是为了防止数据过多而进行的状态快照;Entry 表示存储的具体日志内容。

   通常,一个用户的请求发送过来,会经由 HTTP Server 转发给 Store 进行具体的事务处理,如果涉及到节点的修改,则交给 Raft 模块进行状态的变更、日志的记录,然后再同步给别的 etcd 节点以确认数据提交,最后进行数据的提交,再次同步。各个节点在任何时候都有可能变成Leader, Follower, Candidate等角色,同时为了减少创建链接开销,ETCD节点在启动之初就创建了和集群其他节点之间的链接。

3 代码实现

   etcd深层的代码架构图

 

3.1 消息入口

    一个etcd节点运行以后,有3个通道接收外界消息,以kv数据的增删改查请求处理为例,介绍这3个通道的工作机制。

    1. client的http调用:会通过注册到http模块的keysHandler的ServeHTTP方法处理。解析好的消息调用EtcdServer的Do()方法处理。(图中2)

     2. client的grpc调用:启动时会向grpc server注册quotaKVServer对象,quotaKVServer是以组合的方式增强了kvServer这个数据结构。grpc消息解析完以后会调用kvServer的Range、Put、DeleteRange、Txn、Compact等方法。kvServer中包含有一个RaftKV的接口,由EtcdServer这个结构实现。所以最后就是调用到EtcdServer的Range、Put、DeleteRange、Txn、Compact等方法。

    3. 节点之间的grpc消息:每个EtcdServer中包含有Transport结构,Transport中会有一个peers的map,每个peer封装了节点到其他某个节点的通信方式。包括streamReader、streamWriter等,用于消息的发送和接收。streamReader中有recvc和propc队列,streamReader处理完接收到的消息会将消息推到这连个队列中。由peer去处理,peer调用raftNode的Process方法处理消息。

3.2 EtcdServer消息处理

     对于客户端消息,调用到EtcdServer处理时,一般都是先注册一个等待队列,调用node的Propose方法,然后用等待队列阻塞等待消息处理完成。Propose方法会往propc队列中推送一条MsgProp消息。 对于节点间的消息,raftNode的Process是直接调用node的step方法,将消息推送到node的recvc或者propc队列中。 可以看到,外界所有消息这时候都到了node结构中的recvc队列或者propc队列中。(图中5)

3.3 node处理消息

   node启动时会启动一个协程,处理node的各个队列中的消息,当然也包括recvc和propc队列。从propc和recvc队列中拿到消息,会调用raft对象的Step方法,raft对象封装了raft的协议数据和操作,其对外的Step方法是真正raft协议状态机的步进方法。当接收到消息以后,根据协议类型、Term字段做相应的状态改变处理,或者对选举请求做相应处理。对于一般的kv增删改查数据请求消息,会调用内部的step方法。内部的step方法是一个可动态改变的方法,将随状态机的状态变化而变化。当状态机处于leader状态时,该方法就是stepLeader;当状态机处于follower状态时,该方法就是stepFollower;当状态机处于Candidate状态时,该方法就是stepCandidate。leader状态会直接处理MsgProp消息。将消息中的日志条目存入本地缓存。follower则会直接将MsgProp消息转发给leader,转发的过程是将先将消息推送到raft的msgs数组中。 node处理完消息以后,要么生成了缓存中的日志条目,要么生成了将要发送出去的消息。缓存中的日志条目需要进一步处理(比如同步和持久化),而消息需要进一步处理发送出去。处理过程还是在node的这个协程中,在循环开始会调用newReady,将需要进一步处理的日志和需要发送出去的消息,以及状态改变信息,都封装在一个Ready消息中。Ready消息会推行到readyc队列中。(图中5)

3.4 raftNode的处理

    raftNode的start()方法另外启动了一个协程,处理readyc队列(图中6)。取出需要发送的message,调用transport的Send方法并将其发送出去(图中4)。调用storage的Save方法持久化存储日志条目或者快照(图中9、10),更新kv缓存。 另外需要将已经同步好的日志应用到状态机中,让状态机更新状态和kv存储,通知等待请求完成的客户端。因此需要将已经确定同步好的日志、快照等信息封装在一个apply消息中推送到applyc队列。

   3.5 EtcdServer的apply处理

EtcdServer会处理这个applyc队列,会将snapshot和entries都apply到kv存储中去(图中8)。最后调用applyWait的Trigger,唤醒客户端请求的等待线程,返回客户端的请求。

参考文章:

  etcd官网

  Etcd维基百科

  etcd:从应用场景到实现原理的全方位解读

  etcd github

  etcd documentation

  服务发现系统etcd介绍

  Etcd官方文档中文版(1)

  Etcd官方文档中文版(2)

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值