yinxin
作者yinxin·2019-01-16 14:16
项目经理·某金融机构

Etcd与Zookeeper等的对比

字数 6537阅读 1977评论 0赞 2

Etcd:

一个键值存储仓库,主要用于配置共享和服务发现。

优点:

简单:支持 curl 方式的用户 API (HTTP+JSON)
安全:可选 SSL 客户端证书认证
快速:单实例可达每秒 1000 次写操作
可靠:使用 Raft 实现分布式

raft算法

百战将军问题

拜占庭位于如今的土耳其的伊斯坦布尔,是东罗马帝国的首都。由于当时拜占庭罗马帝国国土辽阔,为了防御目的,因此每个军队都分隔很远,将军与将军之间只能靠信差传消息。在战争的时候,拜占庭军队内所有将军必需达成 一致的共识,决定是否有赢的机会才去攻打敌人的阵营。但是,在军队内有可能存有叛徒和敌军的间谍,左右将军们的决定又扰乱整体军队的秩序,在进行共识时,结果并不代表大多数人的意见。这时候,在已知有成员不可靠的情况下,其余忠诚的将军在不受叛徒或间谍的影响下如何达成一致的协议,拜占庭问题就此形成。拜占庭假设是对现实世界的模型化,由于硬件错误、网络拥塞或断开以及遭到恶意攻击,计算机和网络可能出现不可预料的行为。

在一个由 Raft 协议组织的集群中有三类角色:

Leader(领袖) Follower(群众) Candidate(候选人) 就像一个民主社会,领袖由民众投票选出。刚开始没有领袖,所有集群中的参与者都是群众,那么首先开启一轮大选,在大选期间所有群众都能参与竞选,这时所有群众的角色就变成了候选人,民主投票选出领袖后就开始了这届领袖的任期,然后选举结束,所有除领袖的候选人又变回群众角色服从领袖领导。这里提到一个概念「任期」,用术语 Term 表达。关于 Raft 协议的核心概念和术语就这么多而且和现实民主制度非常匹配,所以很容易理解。

Leader 选举过程

在极简的思维下,一个最小的 Raft 民主集群需要三个参与者(如下图:A、B、C),这样才可能投出多数票。初始状态 ABC 都是 Follower,然后发起选举这时有三种可能情形发生。下图中前二种都能选出 Leader,第三种则表明本轮投票无效(Split Votes),每方都投给了自己,结果没有任何一方获得多数票。之后每个参与方随机休息一阵(Election Timeout)重新发起投票直到一方获得多数票。这里的关键就是随机 timeout,最先从 timeout 中恢复发起投票的一方向还在 timeout 中的另外两方请求投票,这时它们就只能投给对方了,很快达成一致。选出 Leader 后,Leader 通过定期向所有 Follower 发送心跳信息维持其统治。若 Follower 一段时间未收到 Leader 的心跳则认为 Leader 可能已经挂了再次发起选主过程。

Leader 节点对一致性的影响

Raft 协议强依赖 Leader 节点的可用性来确保集群数据的一致性。数据的流向只能从 Leader 节点向 Follower 节点转移。当 Client 向集群 Leader 节点提交数据后,Leader 节点接收到的数据处于未提交状态(Uncommitted),接着 Leader 节点会并发向所有 Follower 节点复制数据并等待接收响应,确保至少集群中超过半数节点已接收到数据后再向 Client 确认数据已接收。一旦向 Client 发出数据接收 Ack 响应后,表明此时数据状态进入已提交(Committed),Leader 节点再向 Follower 节点发通知告知该数据状态已提交。在这个过程中,主节点可能在任意阶段挂掉,看下 Raft 协议如何针对不同阶段保障数据一致性的。

数据到达 Leader 节点前 这个阶段 Leader 挂掉不影响一致性

数据到达 Leader 节点,但未复制到 Follower 节点 这个阶段 Leader 挂掉,数据属于未提交状态,Client 不会收到 Ack 会认为超时失败可安全发起重试。Follower 节点上没有该数据,重新选主后 Client 重试重新提交可成功。原来的 Leader 节点恢复后作为 Follower 加入集群重新从当前任期的新 Leader 处同步数据,强制保持和 Leader 数据一致。

数据到达 Leader 节点,成功复制到 Follower 所有节点,但还未向 Leader 响应接收 这个阶段 Leader 挂掉,虽然数据在 Follower 节点处于未提交状态(Uncommitted)但保持一致,重新选出 Leader 后可完成数据提交,此时 Client 由于不知到底提交成功没有,可重试提交。针对这种情况 Raft 要求 RPC 请求实现幂等性,也就是要实现内部去重机制。

数据到达 Leader 节点,成功复制到 Follower 部分节点,但还未向 Leader 响应接收

这个阶段 Leader 挂掉,数据在 Follower 节点处于未提交状态(Uncommitted)且不一致,Raft 协议要求投票只能投给拥有最新数据的节点。所以拥有最新数据的节点会被选为 Leader 再强制同步数据到 Follower,数据不会丢失并最终一致。

数据到达 Leader 节点,成功复制到 Follower 所有或多数节点,数据在 Leader 处于已提 交状态,但在 Follower 处于未提交状态

这个阶段 Leader 挂掉,重新选出新 Leader 后的处理流程和阶段 3 一样。

数据到达 Leader 节点,成功复制到 Follower 所有或多数节点,数据在所有节点都处于已提交状态,但还未响应 Client

这个阶段 Leader 挂掉,Cluster 内部数据其实已经是一致的,Client 重复重试基于幂等策略对一致性无影响。

网络分区导致的脑裂情况,出现双 Leader

网络分区将原先的 Leader 节点和 Follower 节点分隔开,Follower 收不到 Leader 的心跳将发起选举产生新的 Leader。这时就产生了双 Leader,原先的 Leader 独自在一个区,向它提交数据不可能复制到多数节点所以永远提交不成功。向新的 Leader 提交数据可以提交成功,网络恢复后旧的 Leader 发现集群中有更新任期(Term)的新 Leader 则自动降级为 Follower 并从新 Leader 处同步数据达成集群数据一致。

服务发现

Zookeeper

Zookeeper是这种类型的项目中历史最悠久的之一,它起源于Hadoop,帮助在Hadoop集群中维护各种组件。它非常成熟、可靠,被许多大公司(YouTube、eBay、雅虎等)使用。其数据存储的格式类似于文件系统,如果运行在一个服务器集群中,Zookeper将跨所有节点共享配置状态,每个集群选举一个领袖,客户端可以连接到任何一台服务器获取数据。

Zookeeper的主要优势是其成熟、健壮以及丰富的特性,然而,它也有自己的缺点,其中采用Java开发以及复杂性是罪魁祸首。尽管Java在许多方面非常伟大,然后对于这种类型的工作还是太沉重了,Zookeeper使用Java以及相当数量的依赖使其对于资源竞争非常饥渴。因为上述的这些问题,Zookeeper变得非常复杂,维护它需要比我们期望从这种类型的应用程序中获得的收益更多的知识。这部分地是由于丰富的特性反而将其从优势转变为累赘。应用程序的特性功能越多,就会有越大的可能性不需要这些特性,因此,我们最终将会为这些不需要的特性付出复杂度方面的代价。

Zookeeper为其他项目相当大的改进铺平了道路,“大数据玩家“在使用它,因为没有更好的选择。今天,Zookeeper已经老态龙钟了,我们有了更好的选择。

Etcd

etcd是一个采用HTTP协议的健/值对存储系统,它是一个分布式和功能层次配置系统,可用于构建服务发现系统。其很容易部署、安装和使用,提供了可靠的数据持久化特性。它是安全的并且文档也十分齐全。

etcd比Zookeeper是比更好的选择,因为它很简单,然而,它需要搭配一些第三方工具才可以提供服务发现功能。

现在,我们有一个地方来存储服务相关信息,我们还需要一个工具可以自动发送信息给etcd。但在这之后,为什么我们还需要手动把数据发送给etcd呢?即使我们希望手动将信息发送给etcd,我们通常情况下也不会知道是什么信息。记住这一点,服务可能会被部署到一台运行最少数量容器的服务器上,并且随机分配一个端口。理想情况下,这个工具应该监视所有节点上的Docker容器,并且每当有新容器运行或者现有的一个容器停止的时候更新etcd,其中的一个可以帮助我们达成目标的工具就是Registrator。

Registrator

Registrator通过检查容器在线或者停止运行状态自动注册和去注册服务,它目前支持etcd、Consul和SkyDNS 2。

Registrator与etcd是一个简单但是功能强大的组合,可以运行很多先进的技术。每当我们打开一个容器,所有数据将被存储在etcd并传播到集群中的所有节点。我们将决定什么信息是我们的。

Confd

Confd是一个轻量级的配置管理工具,常见的用法是通过使用存储在etcd、consul和其他一些数据登记处的数据保持配置文件的最新状态,它也可以用来在配置文件改变时重新加载应用程序。换句话说,我们可以用存储在etcd(或者其他注册中心)的信息来重新配置所有服务。

etcd和zookeeper区别

1. etcd、Registrator和Confd是一个非常简单但非常强大的组合,可以解决大部分问题,如果不是全部满足服务发现需要的话。它还展示了我们可以通过组合非常简单和特定的工具来获得强大的服务发现能力,它们中的每一个都执行一个非常具体的任务,通过精心设计的API进行通讯,具备相对自治工作的能力,从架构和功能途径方面都是微服务方式。
2. Zookeeper是其中最老态龙钟的一个,使用年限显示出了其复杂性、资源利用和尽力达成的目标,它是为了与我们评估的其他工具所处的不同时代而设计的(即使它不是老得太多)。

消息队列:

memccacheQ:

memcacheQ, mcq性能和稳定性毋庸置疑,开发维护的图片存储和视频存储系统都可依靠mcq进行消息同步,但是在可靠性和易用性上有一定不足

优点以及缺点

可靠性 目前mcq是单点的,一旦一台mcq服务器故障,所有队列消息都将丢失。mcq本身又是非常稳定的。所以基本不会这样的事故,但还是有这种风险存在。

多条消费者队列 在mcq中,一个消息入队之后,消费者只能从队列里取出来一次,在某些场景下,一个消息可能要被多个消费者系统消费,比如 微博图片上传之后,需要被压缩系统拿来进行压缩,同时还要被分类系统拿去进行分类,还会被审核系统拿去审查,等等,但目前,只能是一个消费者先从队列里拿消息,处理完之后再重新入队,这样其他系统就可以继续拿出来消费,弊端就是将一个并行的任务生生做成里串行,而且严重的是,一旦某个消费者系统拿了消息之后发生故障,未能将消息塞回去,那么后续的消费者就无法处理这个消息。

消息确认和重新入队 消息确认也是保障消息可靠性的一个重要方面,目前mcq并不支持消息确认机制。试想一个消息被某个消费者拿走之后,还没来得及处理,这个消费者系统就挂了,那么这个消息就丢失了,任何其他消费者都无法对它进行处理。当然可以从mcq里拿到消息之后如果长时间没有处理好,就重新写入mcq中,其实并不方便。 必须支持消息确认机制,未确认的消息一段时间之后将会自动重新进入队列中,无需使用者操心。这样才比较合理。

解决方式: 1. 基于reft协议的可靠性消息队列express 解决第一个问题,可以选择在消息队列系统中引入raft一致性协议进行消息同步,简单的说,系统一次性起n个实例,客户端可以连到任意一个实例上进行入队和出队操作,只要集群中有超过一半的节点存活且确认拿到消息,即可认为消息处理成功。即使有少部分实例挂掉,系统依然可以对外提供服务,当挂掉的这些实例重启或者新加入几个实例之后,它们也可以自动同步到最新的状态,然后继续对外服务。

消息队列的应用场景和数据库是完全不同的,引入raft一个最大的问题就是处理能力严重不足。

MQ这种东西,本身就是解决生产者和消费者速度不匹配的问题而诞生的,那么MQ系统一个最基本的要求就是写入速度必须要快,哪怕出队速度慢点也无所谓,因为业务高峰期持续时间是有限的,高峰结束之后有的是时间让消费者慢慢消化,更别说简单粗暴 多加几台消费者就好了。而一旦引入raft一致性协议,每个消息都要等半数以上的express实例确认之后才能返回成功,延时非常高!虽然延时可以通过heartbeat来降低,但同时也会提高系统负载,效果不明显。

在加上,MQ系统在队列堵塞的时候,短时间内积累的消息数量非常之多,以峰值10亿条,每条消息200字节算,如果存在内存里,需要200GB的内存,这太奢侈了,所以必须落地存储,而raft协议同步的基础是增量日志(WAL),这就导致每一个消息会带来两次磁盘写入,磁盘I/O是非常慢的操作,更进一步降低系统入队速度。 集群入队一个消息需要50ms的话,实例之间通过raft库相互同步和确认带来的延时不可避免。 etcd处理能力1000qps 所谓日志复制,是指主节点将每次操作形成日志条目,并持久化到本地磁盘,然后通过网络IO发送给其他节点。
etcd VS zookeeper
etcd在项目实现,一致性协议易理解性,运维,安全等多个维度上,都占据优势。

一致性协议:etcd使用raft协议,zk使用zab(类paxos协议),前者易于理解,方便工程实现。ZooKeeper的部署、维护、使用比较复杂,需要安装客户端,官方只提供了Java和C两种语言的接口。(paxos算法复杂)
api:etcd提供http+json,grpc接口,跨平台语言,zk则需要使用其客户端。
访问安全方面:etcd支持https访问,zk在这方面缺失。
应用场景:配置管理,服务注册发现,选主,应用调度,分布式队列,分布式锁。
etcd读写性能:每个实例每秒支持一千次写操作。这个性能还是相当可观的。 单实例节点支持每秒1000次数据写入。节点越多,由于数据同步涉及到网络延迟,会根据实际情况越来越慢,而读性能会随之变强,因为每个节点都能处理用户请求。
数据持久化。etcd默认数据一更新就进行持久化
数据存储 etcd的存储分为内存存储和持久化(硬盘)存储两部分,内存中的存储除了顺序化的记录下所有用户对节点数据变更的记录外,还会对用户数据进行索引、建堆等方便查询的操作。而持久化则使用预写式日志(WAL:Write Ahead Log)进行记录存储。

etcd做etcd集群代理

etcd --proxy on 静态的配置一个etcd代理 服务注册发现 每个服务器上都部署一个proxy模式的etcd, 这样确保能访问etcd集群的服务都能互相连接。
kafka是个分布式的,基于发布/订阅的消息系统,使用scala编写,可水平扩展和高吞吐率。
etcd做任务队列

k8s就是用etcd做任务队列的,但是这种任务的数量不能太大,否则会遇到etcd的瓶颈。

confd(http://www.thinksaas.cn/topics/0/397/397712.html

统一管理各种配置文件,配置文件模板和数据是分离的,可各自单独管理
动态修改配置属性,然后重新生成相关的配置文件
提供cli方式修改数据

Features(特点)

基础解析:配置文件使用toml格式,模板数据分离
多种存储支持:local file、redis、zookeeper
两种运行模式:Debug或Daemon
cli工具:修改store,批量更新配置
更友好的交互方式,比如通过web界面
分离server和client,提供客户端cli或API供拉取指定配置
加密支持:store中存储的配置可加密,防止泄露

如果觉得我的文章对您有用,请点赞。您的支持将鼓励我继续创作!

2

添加新评论0 条评论

Ctrl+Enter 发表

作者其他文章

相关文章

相关问题

相关资料

X社区推广