etcd 简单介绍
etcd 是一个高可用的分布式 key-value 数据存储系统,内部采用 Raft
协议作为一致性算法,基于 Go 语言实现。
主要特点:
- 简单:提供明确的定义、面向用户的 API(gRPC)
- 安全:支持 TLS客户端证书认证
- 快速:基准测试每秒 10000 写入
- 可靠:使用 Raft 算法保证一致性
etcd 应用场景
服务发现
服务发现要解决的也是分布式系统中最常见的问题之一,即在同一个分布式集群中的进程或服务,要如何才能找到对方并建立连接。本质上来说,服务发现就是想要了解集群中是否有进程在监听 udp
或 tcp
端口,并且通过名字就可以查找和连接。
![服务发现](/static/etcd/01.png)
配置中心
将配置进行集中管理。一般应用在启动时会主动从 etcd 中获取配置信息,同时在节点上注册一个 Watcher
监控,每当配置有更新时,etcd 都会实时通知订阅者,以此获取最新的配置。
分布式锁
使用 Raft
算法保持数据的一致性,每当数据储存到集群中的值必然是全局一致的,所以很容易实现分布式锁。所有获取锁的用户最终只有一个可以得到,为此提供了分布式锁原子操作 CAS(CompareAndSwap)的 API。通过设置prevExist值,可以保证在多个节点同时去创建某个目录时,只有一个成功。而创建成功的用户就可以认为是获得了锁。
Raft 简单介绍
Raft
是一种协议,集群节点可以使用该协议维护一个复制的状态机,状态机与复制的日志保存同步。具体详情可看Raft.pdf。Raft
被广泛地使用在许多产品中,其中包括 etcd, Kubernetes, Docker Swarm, Cloud Foundry Diego, CockroachDB, TiDB, Project Calico, Flannel, Hyperledger 等。
相关特性:
- 领导人选举
- 日志复制
- 日志压缩
- 会员变更
- 领导转移扩展
- ····
etcd 与其他常见服务发现框架对比
名称 | 优点 | 缺点 | 接口 | 一致性算法 |
---|---|---|---|---|
zookeeper | 1.功能强大,不仅仅只是服务发现 2.提供 watcher 机制能实时获取服务提供者的状态 3.dubbo 等框架支持 |
1.没有健康检查 2.需在服务中集成 sdk,复杂度高 3.不支持多数据中心 |
sdk | Paxos |
consul | 1.简单易用,不需要集成 sdk 2.自带健康检查 3.支持多数据中心 4.提供 web 管理界面 |
不能实时获取服务信息的变化通知 | http/dns | Raft |
etcd | 1.简单易用,不需要集成 sdk 可配置性强 |
1.没有健康检查 2.需配合第三方工具一起完成服务发现 3.不支持多数据中心 |
http | Raft |
etcd 单机部署
部署环境:Ubuntu-16.04
etcd Server 端
下载:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16ETCD_VER=v3.4.14
# choose either URL
GOOGLE_URL=https://storage.googleapis.com/etcd
GITHUB_URL=https://github.com/etcd-io/etcd/releases/download
DOWNLOAD_URL=${GOOGLE_URL}
rm -f /tmp/etcd-${ETCD_VER}-linux-amd64.tar.gz
rm -rf /tmp/etcd-download-test && mkdir -p /tmp/etcd-download-test
curl -L ${DOWNLOAD_URL}/${ETCD_VER}/etcd-${ETCD_VER}-linux-amd64.tar.gz -o /tmp/etcd-${ETCD_VER}-linux-amd64.tar.gz
tar xzvf /tmp/etcd-${ETCD_VER}-linux-amd64.tar.gz -C /tmp/etcd-download-test --strip-components=1
rm -f /tmp/etcd-${ETCD_VER}-linux-amd64.tar.gz
/tmp/etcd-download-test/etcd --version
/tmp/etcd-download-test/etcdctl version
启动:1
2# start a local etcd server
/tmp/etcd-download-test/etcd
启动信息: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[WARNING] Deprecated '--logger=capnslog' flag is set; use '--logger=zap' flag instead
2020-12-10 17:07:49.921382 I | etcdmain: etcd Version: 3.4.14
2020-12-10 17:07:49.924906 I | etcdmain: Git SHA: 8a03d2e96
2020-12-10 17:07:49.925297 I | etcdmain: Go Version: go1.12.17
2020-12-10 17:07:49.927355 I | etcdmain: Go OS/Arch: linux/amd64
2020-12-10 17:07:49.928312 I | etcdmain: setting maximum number of CPUs to 2, total number of available CPUs is 2
2020-12-10 17:07:49.928478 W | etcdmain: no data-dir provided, using default data-dir ./default.etcd
[WARNING] Deprecated '--logger=capnslog' flag is set; use '--logger=zap' flag instead
2020-12-10 17:07:49.930599 I | embed: name = default
2020-12-10 17:07:49.930678 I | embed: data dir = default.etcd
2020-12-10 17:07:49.930739 I | embed: member dir = default.etcd/member
2020-12-10 17:07:49.930810 I | embed: heartbeat = 100ms
2020-12-10 17:07:49.930885 I | embed: election = 1000ms
2020-12-10 17:07:49.930953 I | embed: snapshot count = 100000
2020-12-10 17:07:49.931005 I | embed: advertise client URLs = http://localhost:2379
2020-12-10 17:07:49.974825 I | etcdserver: starting member 8e9e05c52164694d in cluster cdf818194e3a8c32
Raft2020/12/10 17:07:49 INFO: 8e9e05c52164694d switched to configuration voters=()
Raft2020/12/10 17:07:49 INFO: 8e9e05c52164694d became follower at term 0
Raft2020/12/10 17:07:49 INFO: newRaft 8e9e05c52164694d [peers: [], term: 0, commit: 0, applied: 0, lastindex: 0, lastterm: 0]
Raft2020/12/10 17:07:49 INFO: 8e9e05c52164694d became follower at term 1
Raft2020/12/10 17:07:49 INFO: 8e9e05c52164694d switched to configuration voters=(10276657743932975437)
2020-12-10 17:07:49.997394 W | auth: simple token is not cryptographically signed
2020-12-10 17:07:50.011174 I | etcdserver: starting server... [version: 3.4.14, cluster version: to_be_decided]
2020-12-10 17:07:50.014616 I | etcdserver: 8e9e05c52164694d as single-node; fast-forwarding 9 ticks (election ticks 10)
Raft2020/12/10 17:07:50 INFO: 8e9e05c52164694d switched to configuration voters=(10276657743932975437)
2020-12-10 17:07:50.018429 I | etcdserver/membership: added member 8e9e05c52164694d [http://localhost:2380] to cluster cdf818194e3a8c32
2020-12-10 17:07:50.020840 I | embed: listening for peers on 127.0.0.1:2380
Raft2020/12/10 17:07:50 INFO: 8e9e05c52164694d is starting a new election at term 1
Raft2020/12/10 17:07:50 INFO: 8e9e05c52164694d became candidate at term 2
Raft2020/12/10 17:07:50 INFO: 8e9e05c52164694d received MsgVoteResp from 8e9e05c52164694d at term 2
Raft2020/12/10 17:07:50 INFO: 8e9e05c52164694d became leader at term 2
Raft2020/12/10 17:07:50 INFO: Raft.node: 8e9e05c52164694d elected leader 8e9e05c52164694d at term 2
2020-12-10 17:07:50.788838 I | etcdserver: setting up the initial cluster version to 3.4
2020-12-10 17:07:50.794910 N | etcdserver/membership: set the initial cluster version to 3.4
2020-12-10 17:07:50.795922 I | etcdserver/api: enabled capabilities for version 3.4
2020-12-10 17:07:50.796055 I | etcdserver: published {Name:default ClientURLs:[http://localhost:2379]} to cluster cdf818194e3a8c32
2020-12-10 17:07:50.797360 I | embed: ready to serve client requests
2020-12-10 17:07:50.803625 N | embed: serving insecure client requests on 127.0.0.1:2379, this is strongly discouraged!
相关信息:
- name 表示节点名称,默认为 default。
- data-dir 保存日志和快照的目录,默认为当前工作目录default.etcd/目录下。
- 在 http://localhost:2380 和集群中其他节点通信。
- 在 http://localhost:2379 提供 HTTP API 服务,供客户端交互。
- heartbeat 为 100ms,该参数的作用是 leader 多久发送一次心跳到
- followers,默认值是100ms。
- election 为 1000ms,该参数的作用是重新投票的超时时间,如果 follow 在该 + 时间间隔没有收到心跳包,会触发重新投票,默认为 1000ms。
- snapshot count 为 10000,该参数的作用是指定有多少事务被提交时,触发 + 截取快照保存到磁盘。
- 集群和每个节点都会生成一个 uuid。
- 启动的时候会运行 Raft,选举出 leader。
etcd Client 端
命令行客户端
etcd 提供一个命令行客户端,这里简单看一下
1 | # write,read to etcd |
1 | ray@ray-virtual-machine:~$ /tmp/etcd-download-test/etcdctl -h |
客户端包
安装
1 | go get go.etcd.io/etcd/clientv3 |
put、get
1 | package main |
watch
1 | package main |
lease 租约
1 | package main |
keepAlive
1 | package main |
分布式锁
1 | package main |
输出:1
2
3acquired lock for s1
released lock for s1
acquired lock for s2
参考文献