1. etcd 简介
etcd
官网定义:
A highly-available key value store for shared configuration and service discovery.
即一个用于配置共享和服务发现的键值存储系统。
etcd
是一款分布式存储中间件,使用 Go
语言编写,并通过 Raft
一致性算法处理和确保分布式一致性,解决了分布式系统中数据一致性的问题。
而且作为一款分布式、可靠的键值存储组件,etcd
常用于微服务架构中的服务注册与发现中心,相较于 ZooKeeper
部署更简单,而且具有数据持久化、支持 SSL
客户端安全认证的独特优势。
etcd
作为一个可信赖的分布式键值存储服务,它能够为整个分布式集群存储一些关键数据,协助分布式集群的正常运转。
etcd
可集中管理配置信息。服务端将配置信息存储于 etcd
,客户端通过 etcd
得到服务配置信息,etcd
监听配置信息的改变,发现改变通知客户端。
而 etcd
满足 CAP
理论中的 CP
(一致性和分区容错性) 指标,由此我们知道,etcd
解决了分布式系统中一致性存储的问题。
2. etcd 特点
etcd
可以用来构建高可用的分布式键值数据库,总结来说有如下特点。
- 简单:安装简单,且为用户提供了
HTTP API
,使用起来也很简单; - 存储:数据分层存储在文件目录中,类似于我们日常使用的文件系统;
- Watch 机制:
Watch
指定的键、前缀目录的更改,并对更改时间进行通知; - 安全通信:支持
SSL
证书验证; - 高性能:
etcd
单实例可以支持 2K/s 读操作,官方也有提供基准测试脚本; - 一致可靠:基于
Raft
共识算法,实现分布式系统内部数据存储、服务调用的一致性和高可用性; Revision
机制:每个Key
带有一个Revision
号,每进行一次事务便加一,因此它是全局唯一的,如初始值为 0,进行一次Put
操作,Key
的Revision
变为 1,同样的操作,再进行一次,Revision
变为 2;换成Key1
进行Put
操作,Revision
将变为 3。这种机制有一个作用,即通过Revision
的大小就可知道写操作的顺序,这对于实现公平锁,队列十分有益;Lease
机制:即租约机制(TTL
,Time To Live
),Etcd
可以为存储的Key-Value
对设置租约,当租约到期,Key-Value
将失效删除;同时也支持续约,通过客户端可以在租约到期之前续约,以避免Key-Value
对过期失效;此外,还支持解约,一旦解约,与该租约绑定的Key-Value
将失效删除;
etcd
是一个实现了分布式一致性键值对存储的中间件,支持跨平台,etcd
集群中的节点基于 Raft
算法进行通信,Raft
算法保证了微服务实例或机器集群所访问的数据的可靠一致性。
在分布式系统或者 Kubernetes
集群中,etcd
可以作为服务注册与发现和键值对存储组件。
3. 应用场景
etcd
在稳定性、可靠性和可伸缩性上表现极佳,同时也为云原生应用系统提供了协调机制。etcd
经常用于服务注册与发现的场景,此外还有键值对存储、消息发布与订阅、分布式锁等场景。
3.1 键值对存储
etcd
是一个用于键值存储的组件,存储是 etcd
最基本的功能,其他应用场景都建立在 etcd
的可靠存储上。比如 Kubernetes
将一些元数据存储在 etcd
中,将存储状态数据的复杂工作交给 etcd
,Kubernetes
自身的功能和架构就能更加稳定。
3.2 服务注册与发现
etcd
基于 Raft
算法,能够有力地保证分布式场景中的一致性。各个服务启动时注册到 etcd
上,同时为这些服务配置键的 TTL
时间。注册到 etcd
上面的各个服务实例通过心跳的方式定期续租,实现服务实例的状态监控。
服务发现(Service Discovery
)要解决的是分布式系统中最常见的问题之一,即在同一个分布式集群中的进程或服务如何才能找到对方并建立连接。服务发现的实现原理如下。
-
存在一个高可靠、高可用的中心配置节点:基于
Ralf
算法的etcd
天然支持。 -
服务提供方会持续的向配置节点注册服务:用户可以在
etcd
中注册服务,并且对注册的服务配置租约,定时续约以达到维持服务的目的(一旦停止续约,对应的服务就会失效)。 -
服务的调用方会持续地读取中心配置节点的配置并修改本机配置,然后
Reload
服务:服务提供方在etcd
指定的目录(前缀机制支持)下注册服务,服务调用方在对应的目录下查询服务。通过watch
机制,服务调用方还可以监测服务的变化。
3.3 消息发布与订阅
在分布式系统中,服务之间还可以通过消息通信,即消息的发布与订阅,如下图所示:
通过构建 etcd
消息中间件,服务提供者发布对应主题的消息,消费者则订阅他们关心的主题,一旦对应的主题有消息发布,就会产生订阅事件,消息中间件就会通知该主题所有的订阅者。
在分布式系统中,组件间通信常用的方式是消息发布-订阅机制。具体而言,即配置一个配置共享中心,数据提供者在这个配置中心发布消息,而消息使用者则订阅它们关心的主题,一旦有关主题有消息发布,就会实时通知订阅者。通过这种方式可以实现分布式系统配置的集中式管理和实时动态更新。显然,通过 Watch
机制可以实现。
应用在启动时,主动从 etcd
获取一次配置信息,同时,在 etcd
节点上注册一个 watcher
并等待,以后每次配置有更新,etcd
都会实时通知订阅者,以此达到获取最新配置信息的目的。
3.4 分布式锁
分布式系统中涉及多个服务实例,存在跨进程之间资源调用,对于资源的协调分配,单体架构中的锁已经无法满足需要,需要引入分布式锁的概念。etcd
基于 Raft
算法,实现分布式集群的一致性,存储到 etcd
集群中的值必然是全局一致的,因此基于 etcd
很容易实现分布式锁。
Etcd
支持 Revision
机制,那么对于同一个 Lock
,即便有多个客户端争夺(本质上就是 put(lockName, value)
操作),Revision
机制可以保证它们的 Revision
编号有序且唯一,那么,客户端只要根据 Revision
的大小顺序就可以确定获得锁的先后顺序,从而很容易实现“公平锁”。
3.5 集群监控与 Leader 竞选
-
集群监控:通过
etcd
的watch
机制,当某个key
消失或变动时,watcher
会第一时间发现并告知用户。节点可以为key
设置租约(TTL
),比如每隔 30 s 向etcd
发送一次心跳续约,使代表该节点的key
保持存活,一旦节点故障,续约停止,对应的key
将失效删除。如此,通过watch
机制就可以第一时间检测到各节点的健康状态,以完成集群的监控要求。 -
Leader
竞选:使用分布式锁,可以很好地实现Leader
竞选(抢锁成功的成为Leader
)。Leader
应用的经典场景是在搜索系统中建立全量索引。如果每个机器分别进行索引建立,不仅耗时,而且不能保证索引的一致性。通过在etcd
实现的锁机制竞选Leader
,由Leader
进行索引计算,再将计算结果分发到其它节点。
4. etcd 的核心架构![架构图](https://i-blog.csdnimg.cn/blog_migrate/a239f63304062373f5f3e12b86f5f61c.png#pic_center)
从上图可知,etcd
有 etcd Server
、gRPC Server
、存储相关的 MVCC
、Snapshot
、WAL
,以及 Raft
模块。
其中:
etcd Server
用于对外接收和处理客户端的请求;gRPC Server
则是etcd
与其他etcd
节点之间的通信和信息同步;MVCC
即多版本控制,etcd
的存储模块,键值对的每一次操作行为都会被记录存储,这些数据底层存储在BoltDB
数据库中;WAL
预写式日志,etcd
中的数据提交前都会记录到日志;Snapshot
快照,以防WAL
日志过多,用于存储某一时刻etcd
的所有数据;Snapshot
和WAL
相结合,etcd
可以有效地进行数据存储和节点故障恢复等操作;
虽然 etcd
内部实现机制复杂,但对外提供了简单的 API
接口,方便客户端调用。我们可以通过 etcdctl
客户端命令行操作和访问 etcd
中的数据,或者通过HTTP API
接口直接访问 etcd
。
etcd
中的数据结构很简单,它的数据存储其实就是键值对的有序映射。etcd
还提供了一种键值对监测机制,即 Watch
机制,客户端通过订阅相关的键值对,获取其更改的事件信息。Watch
机制实时获取 etcd
中的增量数据更新,使数据与 etcd
同步。
etcd
目前有 V2.x
和 V3.x
两个大版本。etcd V2
和 V3
是在底层使用同一套 Raft
算法的两个独立应用,但相互之间实现原理和使用方法上差别很大,接口不一样、存储不一样,两个版本的数据互相隔离。
至于由 etcd V2
升级到 etcd V3
的情况,原有数据只能通过 etcd V2
接口访问,V3
接口创建的数据只能通过新的 V3
的接口访问。
etcd
架构图:
通常,
etcd
会监听两个端口,默认是 2379
端口和 2380
端口。其中,2380
端口用于集群内部通信,主要涉及集群间数据同步、心跳、选举等。2379
端口用于与客户端通信,比如接收客户端发起的读/写数据请求。
etcd
节点在部署的时候有两种运行模式:集群模式和代理模式。
当 etcd
节点以集群模式运行时,它会加入已有集群中,作为集群的一部分。也就是说,后续的心跳、数据同步、选举等它都会参与。
集群模式中的节点数一般采用奇数个。为什么呢?因为假如同时有两个 Candidate
发起选举,如果是偶数节点的话,可能存在两个 Candidate
获得相同票数。
这会导致什么问题?如果两个 Candidate
票数一样,就需要再次发起选举,而再次发起选举还是有一定概率出现票数一样,这会导致选举耗时较多,影响稳定性。所以,采用奇数个节点,能有效降低票数一样的概率,提升选举的效率。另外,使用奇数节点来部署,也能让 etcd
很好地处理分区容错问题。
当某个 etcd
节点以代理模式运行时,该节点负责将接收到的请求转发给 etcd
集群节点。目前 etcd
接口有 v2
和 v3
两个版本,其中 v2
是 HTTP
接口,v3
是 gRPC
接口。需要注意的是,代理模式只支持转发 v2
版本的请求,也就是只支持转发 HTTP
请求。
不过,由于
etcd v3
接口在性能、安全、稳定性等方面要比 v2
接口优秀很多,新项目倾向于使用 v3
接口,老项目也逐渐从 v2
接口迁移到 v3
接口。也就是说,代理模式以后可能逐渐被淘汰掉。