etcd-v3.5.9源代码分析

Github上下载etcdv3.5.9源代码,包含server和client两部分。
先放ETCDv3 读写流程图镇楼

读流程

写流程

Etcd Server

server->etcdmain->main.go:Main():startEtcdOrProxyV2() ->etcd.go:startEtcd()->StartEtcd()->etcdserver目录下server.go:NewServer() 初始化该节点为follow,创建管理lease(小顶堆)的lessor,WAL,MVCC,authStore,KV等等.

raft协议主要体现在etcd/raft/raft.go文件中,每个节点初始化为follower, 随机事件进入参选状态candidate并给自己投票,当获得多数票数后成为leader,并定时发送心跳。当其他follower收不到心跳时,该follower再次进入candidate.那这个leader发现了新的 Leader 任期号,那么它就需要转换到 Follower。etcd3.4引入PreVote预投票机制,防止原leader A Crash时,选出新leader,而当老leader A 恢复后再次触发新一轮 Leader 选举,影响服务的可用性。在这种情况下,因 A 节点数据落后较多,预投票请求无法获得多数节点认可,因此它就不会进入 Candidate 状态,导致集群重新选举。

MVCC 版本中,每一次操作行为都将被单独记录下来,每个 key 都包含多个版本的 value ,那么用户 value 是怎么存储的呢?就是保存到 BoltDB 中。在 BoltDB 中,每个revision都将作为key,即将序列化(revision.main + revision.sub)作为key 。但客户端都是通过key来查询value的,所以etcdv3在内存中还维护了一个kvindex,保存的就是key与reversion之间的映射关系,其可用来加速查询。
revision的数据定义在etcd/server/mvcc/revision.go中;在内存索引中,每个用户的原始key都会关联一个key_index结构,里面维护了多版本信息,该数据结构定义在etcd/server/mvcc/key_index.go中。

存储数据库boltdb

etcd 数据存储在 boltdb, boltdb 基于 B+ tree、各类 page 实现查找、更新、事务提交. 核心数据结构包括page、node、bucket 等。
boltdb 文件指的是你 etcd 数据目录下的 member/snap/db 的文件, etcd 的 key-value、lease、meta、member、cluster、auth 等所有数据存储在其中。etcd 启动的时候,会通过 mmap 机制将 db 文件映射到内存,后续可从内存中快速读取文件中的数据。写请求通过 fwrite 和 fdatasync 来写入、持久化数据到磁盘。

boltdb 结构图
boltdb 提供了非常简单的 API 给上层业务使用,当我们执行一个 put hello 为 world 命令时,boltdb 实际写入的 key 是版本号,value 为 mvccpb.KeyValue 结构体(etcd/api/mvccpb/kv.pb.go)。

etcdv3的范围查询功能实现代码:etcd/server/mvcc/kvstore_txn.go rangeKeys函数。从这段代码中可以看到,rangeKeys()是对key进行范围查询,查询的时候指定一个版本号curRev, etcd则从底层的BoltDB中读取比curRev更新的数据,主要流程具体如下:
先判断curRev的数据是否都已经被删除了。etcd会定期将老版本的数据进行垃圾回收,因此如果curRev小于上一次垃圾回收的版本号,则直接返回错误。否则就从内存的索引中查询到该key大于curRev的版本号,再去BoltDB中读取数据,返回给客户端即可。

另外,如果我们持续更新同一个 key ,那么 generations[0].revs 就会一直变大,遇到这种情况该怎么办呢?在多版本中,一般采用compact来压缩历史版本,即当历史版本达到一定的数量时,会删除一些历史版本,只保存最近的一些版本。

Debug

  1. 在 etcd 3.4 中,logger 默认为 capnslog,trace 特性只有在当 logger 为 zap 时才开启,因此你需要设置 --logger=zap. 打开之后可以看到 range/put/txn 等操作的耗时。
  2. etcd 默认参数并不会采集各个接口的延时数据,我们可以通过设置 etcd 的启动参数 --metrics 为 extensive 来开启,获得每个 gRPC 接口的延时数据。同时可结合各个 gRPC 接口的请求数,获得 QPS。
posted @ 2024-06-05 11:03  JaneySJ  阅读(35)  评论(0编辑  收藏  举报