etcd 可行性分析报告

Etcd可行性分析报告

1 Watch 可行性

  1. 目前测试etcd提供的api及命令行etcdctl watch接口都比较正常。
  2. 测试https://github.com/nokia/etcd-cpp-apiv3 该开源项目对etcd api接口进行了C++ 层面上的封装。在该api中watch接口还存在一些bug,需要更改源码。
  3. 对该api中的接口进行的测试,改完源码后的watch接口正常。Watch 接口主要是靠回调来实现

2 可用性,性能评估,读写

Etcd 每次存储 request 最多支持1M的数据
存储的大小限制最多为2G,可配置到8G(官方文档)

官方给的写入性能测试

  1. Set 操作10000次花费6秒,同一个链接,1核1G内存(写操作)
  2. Set 操作10000次花15秒,每次新建一个链接,1核1G内存(写操作)

官方给的读性能测试

  1. get 操作10000次花费4秒,同一个链接,1核1G内存(读操作)
  2. get 操作10000次花费11秒,每次新建一个链接,1核1G内存(读操作)

按照官网给出的[Benchmark], 在2CPU,1.8G内存,SSD磁盘这样的配置下,单节点的写性能可以达到16K QPS, 而先写后读也能达到12K QPS。

测试这些接口,正常

3 etcd目录层级和深度测试

etcd目录层级和深度测试(100层目录的key添加正常)

etcdctl put /test/0/1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/21/22/23/24/25/26/27/28/29/30/31/32/33/34/35/36/37/38/39/40/41/42/43/44/45/46/47/48/49/50/51/52/53/54/55/56/57/58/59/60/61/62/63/64/65/66/67/68/69/70/71/72/73/74/75/76/77/78/79/80/81/82/83/84/85/86/87/88/89/90/91/92/93/94/95/96/97/98/99/100 key {“action”:”get”,”node”:{“key”:”/test/0/1/2/3/4/5/6/7/8/9/10/11/12/13/14/15/16/17/18/19/20/21/22/23/24/25/26/27/28/29/30/31/32/33/34/35/36/37/38/39/40/41/42/43/44/45/46/47/48/49/50/51/52/53/54/55/56/57/58/59/60/61/62/63/64/65/66/67/68/69/70/71/72/73/74/75/76/77/78/79/80/81/82/83/84/85/86/87/88/89/90/91/92/93/94/95/96/97/98/99/100”,”value”:”hello etcd”,”modifiedIndex”:502079,”createdIndex”:502079}}

OK

4 容灾,异常恢复

提供了集群抗失败,但是集群中有一半失败就会持续失败,不过v3版本提供了快照。
并且提供备份功能。
对集群进行了搭建,集群的选举,配置同步功能正常。快照的功能暂时没有尝试。

5 原子读写

在程序配置中,有时会有多个服务需要并发地修改同一个键。在这种情况下,就可能会出现前一个服务读取了数值还未写回时,后一个服务读到了相同的值,那么后写入的服务则会错误地覆盖前一个服务已经修改过的记录。

为了解决写入冲突的问题,Etcd API 提供了一种先对比再写入(Compare-and-Swap)的 “原子读写” 方法:

prevValue:写入前将指定值与待更新键的内容进行比较,两个值相同时才会写入。
prevIndex:写入前将指定 Index 与待更新键的 modifiedIndex 值进行比较,两个值下同才会写入。
prevExist:写入前待更新键的存在性与指定一致时,才会写入。存在性可以是 true 或 false。
由于这个比较和写入是通过同一个请求发送到 Etcd 服务的,Etcd 能够确保在这一次的比较和写入之间,指定的键不会被任何途径修改。即如果比较时结果是相同的,那么此次写入一定是安全的,不用担心意外覆写其它服务更新过的内容。

6 表结构

  key value
基础配置 /node/$ip:$port {id:$id,role:$role,group:$group,parnetid:$parnetid}
例子 /node/ip1:9201 {id:99,role:unit,group:bl,parnetid:101}
活跃服务 /run/$id $ip:$port
例子 /run/99 ip1:9201
注册服务 /service/$role/$group/$parentid/$id $ip:$port
例子 /service/unit/bl/101/99 ip1:9201
/service/frontend/50 ip1:9204

7 流程解析

  1. 启动注册流程:
    节点启动后,向monitor节点发送ip+port进行注册,monitor对etcd的”/node”字段进行该ip+port的查询,若无则为非法注册;若有取回id、role、group、parentid等信息。
  2. 服务发现流程:
    节点获得id等相关信息后,发送role、group、parentid信息给monitor获取可用服务,monitor对etcd的”/service”字段进行查询相关注册的服务,并返回给节点;
  3. 服务注册流程:
    
    节点正常初始化启动后,发送心跳给monitor,monitor对etcd的”/run”字段进行查询,若存在,则更新该key的ttl;若无,先插入”/run”,再对”/service”字段进行查询,若存在,则判定此为网络抖动导致”/run”下该key消失;若无,则判定为新服务的注册,并进行插入。
  4. 新添加服务发现流程:
    
    根据(3)的规则,新添加服务会更新etcd的”/service”,而monitor启动时会订阅etcd的”/service”字段,一旦变化,etcd返回变更结果,monitor根据变化结果通知相关节点做添加/删除服务。

8 场景演示

启动注册:

启动时服务发现:

保活:

服务注册:

运行时服务发现:

现阶段存在问题:

  1. etcd v3的c++ client实现使用的是https://github.com/nokia/etcd-cpp-apiv3上的开源代码,其实现的功能有限且不稳定,增删改查、订阅功能都有,更新租约(保活)、事务操作等都没实现。
  2. 开源client代码中订阅功能会在grpc通信write阶段发生coredump,参考了https://github.com/nokia/etcd-cpp-apiv3/issues/5进行解决,但没解决根本原因,需要进一步研究grpc调用过程。
  3. 现阶段自己实现了client端保活功能,初步测试无误,稳定性需进一步验证。