ETCD系列之三:网络层实现

ETCD系列之三:网络层实现

1. 概述

在理清ETCD的各个模块的实现细节后,方便线上运维,理解各种参数组合的意义。本文先从网络层入手,后续文章会依次介绍各个模块的实现。

本文将着重介绍ETCD服务的网络层实现细节。在目前的实现中,ETCD通过HTTP协议对外提供服务,同样通过HTTP协议实现集群节点间数据交互。

网络层的主要功能是实现了服务器与客户端(能发出HTTP请求的各种程序)消息交互,以及集群内部各节点之间的消息交互。

2. ETCD-SERVER整体架构

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

  • 网络层:提供网络数据读写功能,监听服务端口,完成集群节点之间数据通信,收发客户端数据;
  • Raft模块:完整实现了Raft协议;
  • 存储模块:KV存储,WAL文件,SNAPSHOT管理
  • 复制状态机:这个是一个抽象的模块,状态机的数据维护在内存中,定期持久化到磁盘,每次写请求会持久化到WAL文件,并根据写请求的内容修改状态机数据。

3. 节点之间网络拓扑结构

ETCD集群的各个节点之间需要通过HTTP协议来传递数据,表现在:

  • Leader 向Follower发送心跳包, Follower向Leader回复消息;
  • Leader向Follower发送日志追加信息;
  • Leader向Follower发送Snapshot数据;
  • Candidate节点发起选举,向其他节点发起投票请求;
  • Follower将收的写操作转发给Leader;

各个节点在任何时候都有可能变成Leader, Follower, Candidate等角色,同时为了减少创建链接开销,ETCD节点在启动之初就创建了和集群其他节点之间的链接。

因此,ETCD集群节点之间的网络拓扑是一个任意2个节点之间均有长链接相互连接的网状结构。如图2所示。
thread_local_impl
图2 ETCD集群节点网络拓扑图

需要注意的是,每一个节点都会创建到其他各个节点之间的长链接。每个节点会向其他节点宣告自己监听的端口,该端口只接受来自其他节点创建链接的请求。

4. 节点之间消息交互

在ETCD实现中,根据不同用途,定义了各种不同的消息类型。各种不同的消息,最终都通过google protocol buffer协议进行封装。这些消息携带的数据大小可能不尽相同。例如 传输SNAPSHOT数据的消息数据量就比较大,甚至超过1GB, 而leader到follower节点之间的心跳消息可能只有几十个字节。

因此,网络层必须能够高效地处理不同数据量的消息。ETCD在实现中,对这些消息采取了分类处理,抽象出了2种类型消息传输通道:Stream类型通道和Pipeline类型通道。这两种消息传输通道都使用HTTP协议传输数据。
thread_local_impl_1_
图3 节点之间建立消息传输通道

集群启动之初,就创建了这两种传输通道,各自特点:

  • Stream类型通道:点到点之间维护HTTP长链接,主要用于传输数据量较小的消息,例如追加日志,心跳等;
  • Pipeline类型通道:点到点之间不维护HTTP长链接,短链接传输数据,用完即关闭。用于传输数据量大的消息,例如snapshot数据。

如果非要做做一个类别的话,Stream就向点与点之间维护了双向传输带,消息打包后,放到传输带上,传到对方,对方将回复消息打包放到反向传输带上;而Pipeline就像拥有N辆汽车,大消息打包放到汽车上,开到对端,然后在回来,最多可以同时发送N个消息。

Stream类型通道
Stream类型通道处理数据量少的消息,例如心跳,日志追加消息。点到点之间只维护1个HTTP长链接,交替向链接中写入数据,读取数据。

Stream 类型通道是节点启动后主动与其他每一个节点建立。Stream类型通道通过Channel 与Raft模块传递消息。每一个Stream类型通道关联2个Goroutines, 其中一个用于建立HTTP链接,并从链接上读取数据, decode成message, 通过Channel传给Raft模块中,另外一个通过Channel 从Raft模块中收取消息,然后写入通道。

具体点,ETCD使用golang的http包实现Stream类型通道:

  • 1)被动发起方监听端口, 并在对应的url上挂载相应的handler(当前请求来领时,handler的ServeHTTP方法会被调用)
  • 2)主动发起方发送HTTP GET请求;
  • 3)监听方的Handler的ServeHTTP访问被调用(框架层传入http.ResponseWriter和http.Request对象),其中http.ResponseWriter对象作为参数传入Writter-Goroutine(就这么称呼吧),该Goroutine的主循环就是将Raft模块传出的message写入到这个responseWriter对象里;http.Request的成员变量Body传入到Reader-Gorouting(就这么称呼吧),该Gorutine的主循环就是不断读取Body上的数据,decode成message 通过Channel传给Raft模块。

Pipeline类型通道
Pipeline类型通道处理数量大消息,例如SNAPSHOT消息。这种类型消息需要和心跳等消息分开处理,否则会阻塞心跳。

Pipeline类型通道也可以传输小数据量的消息,当且仅当Stream类型链接不可用时。

Pipeline类型通道可用并行发出多个消息,维护一组Goroutines, 每一个Goroutines都可向对端发出POST请求(携带数据),收到回复后,链接关闭。

具体地,ETCD使用golang的http包实现的:

  • 1)根据参数配置,启动N个Goroutines;
  • 2)每一个Goroutines的主循环阻塞在消息Channel上,当收到消息后,通过POST请求发出数据,并等待回复。

5. 网络层与Raft模块之间的交互

在ETCD中,Raft协议被抽象为Raft模块。按照Raft协议,节点之间需要交互数据。在ETCD中,通过Raft模块中抽象的RaftNode拥有一个message box, RaftNode将各种类型消息放入到messagebox中,有专门Goroutine将box里的消息写入管道,而管道的另外一端就链接在网络层的不同类型的传输通道上,有专门的Goroutine在等待(select)。

而网络层收到的消息,也通过管道传给RaftNode。RaftNode中有专门的Goroutine在等待消息。

也就是说,网络层与Raft模块之间通过Golang Channel完成数据通信。这个比较容易理解。

6 ETCD-SERVER处理请求(与客户端的信息交互)

在ETCD-SERVER启动之初,会监听服务端口,当服务端口收到请求后,解析出message后,通过管道传入给Raft模块,当Raft模块按照Raft协议完成操作后,回复该请求(或者请求超时关闭了)。

7 主要数据结构

网络层抽象为Transport类,该类完成网络数据收发。对Raft模块提供Send/SendSnapshot接口,提供数据读写的Channel,对外监听指定端口。

thread_local_impl_3_

8. 结束

本文整理了ETCD节点网络层的实现,为分析其他模块打下基础。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值