《云计算技术系列丛书 云原生分布式存储基石: etcd深入解析》—2.2etcd架构简介
2.2 etcd架构简介
etcd在设计的时候重点考虑了如下的四个要素。
1.简单
支持RESTful风格的HTTP+JSON的API。
从性能角度考虑,etcd v3增加了对gRPC的支持,同时也提供rest gateway进行转化。
使用Go语言编写,跨平台,部署和维护简单。
使用Raft算法保证强一致性,Raft算法可理解性好。
2.安全
支持TLS客户端安全认证。
3.性能
单实例支持每秒一千次以上的写操作(v2),极限写性能可达10K+Qps(v3)。
4.可靠
使用Raft算法充分保证了分布式系统数据的强一致性。etcd集群是一个分布式系统,由多个节点相互通信构成整体的对外服务,每个节点都存储了完整的数据,并且通过Raft协议保证了每个节点维护的数据都是一致的。Raft协议的工作原理这里不再赘述,详见1.4节即可了解。
简单地说,etcd可以扮演两大角色,具体如下。
持久化的键值存储系统。
分布式系统数据一致性服务提供者。
在分布式系统中,如何管理节点间的状态一直是一个难题,etcd像是专门为集群环境的服务发现和注册而设计的,它提供了数据TTL失效、数据改变监视、多值、目录、分布式锁原子操作等功能,可以方便地跟踪并管理集群节点的状态。
etcd(server)大体上可以分为网络层(http(s) server)、Raft模块、复制状态机和存储模块。etcd的架构如图2-1所示。
图2-1 etcd server模块组成
网络层:提供网络数据读写功能,监听服务端口,完成集群节点之间数据通信,收发客户端数据。
Raft模块:Raft强一致性算法的具体实现。
存储模块:涉及KV存储、WAL文件、Snapshot管理等,用于处理etcd支持的各类功能的事务,包括数据索引、节点状态变更、监控与反馈、事件处理与执行等,是etcd对用户提供的大多数API功能的具体实现。
复制状态机:这是一个抽象的模块,状态机的数据维护在内存中,定期持久化到磁盘,每次写请求都会持久化到WAL文件,并根据写请求的内容修改状态机数据。除了在内存中存有所有数据的状态以及节点的索引之外,etcd还通过WAL进行持久化存储。基于WAL的存储系统其特点就是所有的数据在提交之前都会事先记录日志。Snapshot是为了防止数据过多而进行的状态快照。复制状态机的工作原理在这里也不多做赘述,详见1.2.3节。
通常,一个用户的请求发送过来,会经由HTTP(S)Server转发给存储模块进行具体的事务处理,如果涉及节点状态的更新,则交给Raft模块进行仲裁和日志的记录,然后再同步给别的etcd节点,只有当半数以上的节点确认了该节点状态的修改之后,才会进行数据的持久化。
各个节点在任何时候都有可能变成Leader、Follower、Candidate等角色,同时为了减少创建链接开销,etcd节点在启动之初就会创建并维持与集群其他节点之间的链接。
etcd集群的各个节点之间需要通过网络来传递数据,具体表现为如下几个方面。
1)Leader向Follower发送心跳包,Follower向Leader回复消息。
2)Leader向Follower发送日志追加信息。
3)Leader向Follower发送Snapshot数据。
4)Candidate节点发起选举,向其他节点发起投票请求。
5)Follower将收到的写操作转发给Leader。
因此,etcd集群节点之间的网络拓扑是一个任意2个节点之间均有长链接相互连接的网状结构,如图2-2所示。
图2-2 etcd集群拓扑关系
2.2.1 etcd数据通道
在etcd的实现中,etcd根据不同的用途,定义了各种不同的消息类型。这些不同的消息,最终都将通过protocol buffer格式进行编码。这些消息携带的数据大小可能不尽相同。例如传输Snapshot的数据量就比较大,甚至会超过1GB,而Leader到Follower节点之间的心跳消息可能只有几十KB。因此,网络层必须要能够高效地处理不同数据量的消息。etcd在实现中,对这些消息采取了分类处理的方式,它抽象出了2种类型的消息传输通道,即Stream类型通道和Pipeline类型通道。这2种消息传输通道都使用HTTP传输数据。打个比方,Stream就像是在点与点之间维护的双向传输带,消息打包后,放到传输带上,传给对方,对方将回复消息打包好并放到反向传输带上;而Pipeline就如同拥有N辆汽车,将大消息打包放到汽车上,开到对端,然后再开回来,最多可以同时发送N个消息。下面将分别阐述这两种不同类型通道的数据流。
1. Stream类型通道
Stream类型通道用于处理数据量较少的消息,例如,心跳、日志追加消息等。点到点之间只维护1个HTTP长链接,交替向链接中写入数据和读取数据。
Stream类型通道是节点启动后主动与其他每一个节点建立链接,它通过Channel 与Raft模块传递消息。每一个Stream类型通道关联2个go routine(Go语言的协程),其中一个用于建立HTTP链接,并从链接上读取数据并解码成消息,再通过Channel传给Raft模块,另外一个通过Go语言的Channel 从Raft模块中收取消息,然后写入Stream类型通道。
如果深入研究代码细节的话,则是etcd使用Golang的HTTP包实现Stream类型通道,具体过程如下所示。
1)Server端监听端口,并在对应的url上挂载相应的Handler(当前请求到达时,Handler的ServeHTTP方法会被调用)。
2)客户端发送HTTP GET请求。
3)调用Server端的Handler的ServeHTTP访问(框架层传入http.Response-Writer和http.Request对象),其中http.ResponseWriter对象将作为参数 传入Writter-Goroutine,该go routine的主循环就是将Raft模块传出的消息写入这个responseWriter对象里。http.Request的成员变量Body传入Reader-Gorouting(就这么称呼吧)中,该go routine的主循环就是不断读取Body上的数据,并解码成消息,然后通过Go语言的Channel传给Raft模块。
2. Pipeline类型通道
Pipeline类型通道用于处理数据量大的消息,例如,Snapshot。这种类型的消息需要与心跳等消息分开处理,否则会阻塞心跳包的传输,进而影响集群的稳定性。使用Pipeline类型通道进行通信时,点到点之间不维护HTTP长链接,它只通过短链接传输数据,用完即关闭。
Pipeline类型通道也可以传输小数据量的消息,不过,是在当且仅当Stream类型链接不可用时,它才会这样做。
此外,Pipeline类型通道还可用来并行发出多个消息,它维护着一组go routine,每一个go routine都可向对端发出POST请求(携带数据),收到回复后,链接关闭。
etcd使用Golang的HTTP包实现Pipeline类型通道的具体过程如下所示。
1)根据参数配置,启动N个go routine。
2)每一个go routine的主循环都阻塞在消息Channel上,待收到消息之后,通过POST请求发出数据,并等待回复。
- 点赞
- 收藏
- 关注作者
评论(0)