概述:本文将介绍etcd特性、使用场景、基本原理以及Linux环境下的实战操作
入门
什么是etcd?
etcd是一个分布式键值存储数据库
关键字解析:
- 键值存储 :存储协议是 key---value 的形式,类似于redis
- 分布式 :具有分布式特性、每个etcd实例作为集群中的一个节点,通过分布式锁, leader选举保障可靠的分布式协同
- 数据库 :持久化存储、与redis的主内存存储不同,etcd主持久化到磁盘,同MySQL
etcd的特性
1.强一致性,通过Raft共识算法保证集群中各节点数据的一致性,这是etcd最重要特点
2.全量复制,集群中每个节点拥有全量数据,这也是强一致性的体现
3.数据结构仅支持字符串格式
4.性能,单节点支持1000次/秒 写操作,2000次/秒 读操作(远低于redis的10w+8w)
使用场景
从etcd的性能指标发现,单从性能考虑,etcd远不如redis,那么etcd有什么过人之处呢?
1.服务注册与服务发现
在一些由多服务模块组成的大型项目中,例如即时通讯项目,一般有login_server,msg_server,route_server,file_server等各个分工不同的server,这些server之间有互相连接通信的需求,而连接就需要获知对方的ip+port,如何获知呢?
a.第一种方式是把所有server的地址写在配置文件中,也就是写死在代码中的方式
b.将所有server的地址保存在另一个server中,也就是加一层注册中心,支持动态删减
而etcd正适合作为服务的注册和发现中心 ,etcd的各个节点保证的强一致性可以确保在访问不同节点时,得到的同一份数据是完全相同的,这对服务间的协同是必要的
2.分布式选主
etcd通过部署多个节点组成集群,集群中分为一个leader和剩下的follower。来自客户端的写请求全部由leader节点处理,读请求全部由follower节点处理。不管是leader还是follower节点都会有失效的情况,也就是服务挂掉了。
-
如何获知节点挂掉了?
leader节点会定时向所有follower节点发生心跳,表明自己还在工作,而follower节点则会响应心跳,表明自己也尚存活,任有一方长时间没有得到对方的相应,则认为对方失效
Leader 节点失效时的行为
如果集群的 Leader 节点失效,etcd 的行为如下:
3.1 Leader 检测失效
- 当集群中的 Follower 节点未能在规定时间(称为
election timeout
)内收到 Leader 的心跳时,认为 Leader 失效,开始进入 选举阶段。
3.2 选举新 Leader
- 在选举阶段,所有 Follower 节点会随机等待一段时间(称为随机延迟),之后尝试成为新 Leader,这一过程称为 候选人状态。
- 候选人会向其他节点发送投票请求(
RequestVote
),询问它们是否同意将自己选为新 Leader。 - 当某个候选人得到集群中大多数节点(即 Quorum,N/2 + 1)的投票支持后,它会成为新的 Leader。
- 成为 Leader 后,它会开始向其他节点发送心跳,并继续接管客户端的写请求。
3.3 数据一致性保障
- 选举过程中,etcd 使用 Raft 的一致性协议确保所有节点的日志保持一致。只有当候选人确认自己拥有最新的日志记录时,才能成为新的 Leader。
- 这避免了一个拥有落后数据的节点被选为 Leader,从而导致数据不一致的问题。
3.4 Leader 重新恢复
- 如果失效的 Leader 节点重新上线,它会以 Follower 的身份重新加入集群,不会自动恢复成 Leader。新 Leader 会通过日志复制的方式将其与集群的最新数据同步。
Follower 节点失效时的行为
如果 Follower 节点失效,etcd 集群的行为如下:
4.1 失效 Follower 检测
- Leader 通过心跳和日志同步来检测 Follower 是否失效。如果 Leader 发现某个 Follower 不响应,它会将该节点标记为不健康,并停止向该节点发送日志更新。
4.2 失效 Follower 不影响集群可用性
- etcd 集群依赖 Quorum(大多数派)来保持正常运行,因此只要集群中有足够多的节点(N/2 + 1)在线,失效的 Follower 节点不会影响整个系统的可用性。
- 举例来说,如果集群有 5 个节点,允许最多 2 个节点失效,集群依然能正常工作。
4.3 失效 Follower 恢复后
-
一旦失效的 Follower 恢复,它会重新加入集群,并通过与 Leader 进行数据同步,获取最新的日志条目。Leader 会将它在失效期间未接收到的日志记录发送给它,确保其数据与其他节点一致。
-
这个过程通常是透明的,客户端不会感知到。
3.读多写少的场景
etcd不同于redis的高性能,大量写操作会导致响应速度降低
Linux环境下 etcd实战
为了演示etcd的集群环境,本文使用docker部署etcd集群
环境:Ubuntu22.04
1.拉取etcd镜像
shell
docker pull quay.io/coreos/etcd:v3.5.7
2.编写配置文件
存放路径:/home/w/conf/etcd
第一个节点 etcd0.yaml
shell
# 节点名称
name: "etcdnode0"
# 数据存储目录
data-dir: "/etcd-data/data"
# 预写式日志存储目录
wal-dir: "/etcd-data/wal"
# 集群成员之间通讯使用URL
listen-peer-urls: "http://0.0.0.0:2380"
# 集群提供给外部客户端访问的URL,即外部客户端必须通过指定的IP加端口访问etcd
listen-client-urls: "http://0.0.0.0:2379"
#集群配置
initial-advertise-peer-urls: "http://192.168.62.128:2380"
# 集群初始成员配置,是etcd静态部署的核心初始化配置,它说明了当前集群由哪些URLs组成,此处default为节点名称
initial-cluster: "etcdnode0=http://192.168.62.128:2380,etcdnode1=http://192.168.62.128:12380,etcdnode2=http://192.168.62.128:22380"
# 初始化集群状态(new 或 existing)
initial-cluster-state: "new"
# 引导期间etcd集群的初始集群令牌,防止不同集群之间产生交互
initial-cluster-token: "etcd-cluster"
# 向客户端发布的服务端点
advertise-client-urls: "http://192.168.62.128:2379"
logger: "zap"
# 配置日志级别,仅支持 debug, info, warn, error, panic, or fatal
log-level: "warn"
log-outputs:
- "stderr"
第二个节点 etcd1.yaml
shell
# 节点名称
name: "etcdnode1"
# 数据存储目录
data-dir: "/etcd-data/data"
# 预写式日志存储目录
wal-dir: "/etcd-data/wal"
# 集群成员之间通讯使用URL
listen-peer-urls: "http://0.0.0.0:12380"
# 集群提供给外部客户端访问的URL,即外部客户端必须通过指定的IP加端口访问etcd
listen-client-urls: "http://0.0.0.0:12379"
#集群配置
initial-advertise-peer-urls: "http://192.168.62.128:12380"
# 集群初始成员配置,是etcd静态部署的核心初始化配置,它说明了当前集群由哪些URLs组成,此处default为节点名称
initial-cluster: "etcdnode0=http://192.168.62.128:2380,etcdnode1=http://192.168.62.128:12380,etcdnode2=http://192.168.62.128:22380"
# 初始化集群状态(new 或 existing)
initial-cluster-state: "new"
# 引导期间etcd集群的初始集群令牌,防止不同集群之间产生交互
initial-cluster-token: "etcd-cluster"
# 向客户端发布的服务端点
advertise-client-urls: "http://192.168.62.128:12379"
logger: "zap"
# 配置日志级别,仅支持 debug, info, warn, error, panic, or fatal
log-level: "warn"
log-outputs:
- "stderr"
第三个节点 etcd2.yaml
shell
# 节点名称
name: "etcdnode2"
# 数据存储目录
data-dir: "/etcd-data/data"
# 预写式日志存储目录
wal-dir: "/etcd-data/wal"
# 集群成员之间通讯使用URL
listen-peer-urls: "http://0.0.0.0:22380"
# 集群提供给外部客户端访问的URL,即外部客户端必须通过指定的IP加端口访问etcd
listen-client-urls: "http://0.0.0.0:22379"
#集群配置
initial-advertise-peer-urls: "http://192.168.62.128:22380"
# 集群初始成员配置,是etcd静态部署的核心初始化配置,它说明了当前集群由哪些URLs组成,此处default为节点名称
initial-cluster: "etcdnode0=http://192.168.62.128:2380,etcdnode1=http://192.168.62.128:12380,etcdnode2=http://192.168.62.128:22380"
# 初始化集群状态(new 或 existing)
initial-cluster-state: "new"
# 引导期间etcd集群的初始集群令牌,防止不同集群之间产生交互
initial-cluster-token: "etcd-cluster"
# 向客户端发布的服务端点
advertise-client-urls: "http://192.168.62.128:22379"
logger: "zap"
# 配置日志级别,仅支持 debug, info, warn, error, panic, or fatal
log-level: "warn"
log-outputs:
- "stderr"
3.创建并运行容器
!先关闭防火墙
shell
sudo ufw disable
分别执行以下三条命令
shell
#etcd0:
docker run -d -p 2379:2379 -p 2380:2380 \
-v /home/w/conf/etcd:/etcd-conf \
-v /tmp/etcd0-data:/etcd-data \
--name etcd0 \
quay.io/coreos/etcd:v3.5.7 \
/usr/local/bin/etcd --config-file=/etcd-conf/etcd0.yaml
#etcd1:
docker run -d -p 12379:12379 -p 12380:12380 \
-v /home/w/conf/etcd:/etcd-conf \
-v /tmp/etcd1-data:/etcd-data \
--name etcd1 \
quay.io/coreos/etcd:v3.5.7 \
/usr/local/bin/etcd --config-file=/etcd-conf/etcd1.yaml
#etcd2
docker run -d -p 22379:22379 -p 22380:22380 \
-v /home/w/conf/etcd:/etcd-conf \
-v /tmp/etcd2-data:/etcd-data \
--name etcd2 \
quay.io/coreos/etcd:v3.5.7 \
/usr/local/bin/etcd --config-file=/etcd-conf/etcd2.yaml
检查节点运行状态
shell
docker exec etcd0 /usr/local/bin/etcdctl endpoint health --cluster -w table
检查集群运行状态
shell
docker exec etcd0 /usr/local/bin/etcdctl endpoint status --cluster -w table
4.操作etcd
1.添加一个key
shell
docker exec etcd0 /usr/local/bin/etcdctl put key1 value1
2.获取一个key-value
shell
docker exec etcd0 /usr/local/bin/etcdctl get key1
3.获取所有key-value
shell
docker exec etcd0 /usr/local/bin/etcdctl get "" --prefix
4.删除一个key
shell
docker exec etcd0 /usr/local/bin/etcdctl del key1
由于etcd集群各节点间数据完全同步,因此,在一个节点上的put操作可以在另一个节点上看到结果,此外,由于leader节点负责写操作,所以在follower节点上的写操作会被重定向到leader节点进行操作