未完待续

ETCD-实战

环境准备

首先介绍一个好用的进程管理工具 goreman,基于它,我们可快速创建、停止本地的多节点 etcd 集群。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#goreman 安装 
go get github.com/mattn/goreman

#etcd 安装 version: 3.5.1

#下载etcd/Procfile
https://github.com/etcd-io/etcd/blob/main/Procfile

#通过 goreman 启动etcd集群
goreman -f Procfile start

ETCD 集群启动如下图

image-20220104223359645

1
2
3
#列出member

etcdctl member list --write-out=table  --endpoints=localhost:2379

image-20220105221516281

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# 操作key 

etcdctl --endpoints=localhost:2379 put /key val1
etcdctl --endpoints=localhost:2379 put /key val2
etcdctl --endpoints=localhost:2379 put /key val3
etcdctl --endpoints=localhost:2379 put /key val4
etcdctl --endpoints=localhost:2379 get /key -wjson


# 从不同的 reversion 版本来watch key
etcdctl --endpoints=localhost:2379 watch --prefix / --rev 0
etcdctl --endpoints=localhost:2379 watch --prefix / --rev 1
etcdctl --endpoints=localhost:2379 watch --prefix / --rev 2

image-20220105222218575

-wjosn 可以查看 key 的详情信息

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
{
    "header": {
        "cluster_id": 17237436991929493444,
        "member_id": 9372538179322589801,
        "revision": 7,
        "raft_term": 5
    },
    "kvs": [
        {
            "key": "L2tleQ==",
            "create_revision": 3,
            "mod_revision": 7,
            "version": 5,
            "value": "dmFsNQ=="
        }
    ],
    "count": 1
}

一个读请求是如何执行的

一个读请求从 client 通过 Round-robin 负载均衡算法,选择一个etcd server 节点,发出 gRPC 请求,经过 etcd server 的 KVServer 模块、线性读模块、MVCC 的 treeIndex 和 boltdb 模块紧密协作,完成了一个读请求。

简要基础架构图

image-20220106220618666

  1. 在 etcd v3.4.9 版本中,etcdctl 是通过 clientv3 库来访问 etcd server 的,clientv3 库基于 gRPC client API 封装了操作 etcd KVServer、Cluster、Auth、Lease、Watch 等模块的 API,同时还包含了负载均衡、健康探测和故障切换等特性。

  2. 在解析完请求中的参数后,etcdctl 会创建一个 clientv3 库对象,使用 KVServer 模块的API 来访问 etcd server。

  3. 接下来,就需要为这个 请求选择一个合适的 etcd server 节点了,这里得用到负载均衡算法。在 etcd 3.4 中,clientv3 库采用的负载均衡算法为 Round-robin。针对每一个请求,Round-robin 算法通过轮询的方式依次从 endpoint 列表中选择一个 endpoint 访问 (长连接),使 etcd server 负载尽量均衡。

    关于负载均衡算法,你需要特别注意以下两点。

    1. 如果你的 client 版本 <= 3.3,那么当你配置多个 endpoint 时,负载均衡算法仅会从中选择一个 IP 并创建一个连接(Pinned endpoint),这样可以节省服务器总连接数。但在这我要给你一个小提醒,在 heavy usage 场景,这可能会造成 server 负载不均衡。

    2. 在 client 3.4 之前的版本中,负载均衡算法有一个严重的 Bug:如果第一个节点异常了,可能会导致你的 client 访问 etcd server 异常,特别是在 Kubernetes 场景中会导致 APIServer 不可用。不过,该 Bug 已在 Kubernetes 1.16 版本后被修复。

  4. 为请求选择好 etcd server 节点,client 就可调用 etcd server 的 KVServer 模块的 Range RPC 方法,把请求发送给 etcd server。

    这里我说明一点,client 和 server 之间的通信,使用的是基于 HTTP/2 的 gRPC 协议。相比 etcd v2 的 HTTP/1.x,HTTP/2 是基于二进制而不是文本、支持多路复用而不再有序且阻塞、支持数据压缩以减少包大小、支持 server push 等特性。因此,基于 HTTP/2 的

    gRPC 协议具有低延迟、高性能的特点,有效解决了我们在上一讲中提到的 etcd v2 中HTTP/1.x 性能问题。

  5. client 发送 Range RPC 请求到了 server 后,就开始进入我们架构图中的流程二,也就是KVServer 模块了。进入 KVServer 模块后,我们就进入核心的读流程了 KVServer 模块收到线性读请求后,通过架构图中流程三向 Raft 模块发起 ReadIndex 请求,Raft 模块将 Leader 最新的已提交日志索引封装在流程四的 ReadState 结构体,通过 channel 层层返回给线性读模块,线性读模块等待本节点状态机追赶上 Leader 进度,追赶完成后,就通知 KVServer 模块,进行架构图中流程五,与状态机中的MVCC 模块进行进行交互了。