二、ETCD的使用

265 阅读4分钟

ETCD 是一个开源的分布式键值存储,用于保存和管理分布式系统保持运行所需的关键信息。

写入数据

写入数据的命令是put

etcdctl --endpoints=$ENDPOINTS put foo "Hello World!"

运行成功后,会向 ETCD 中插入一条数据,键为foo,值为Hello World!

查询数据

从 ETCD 中读取数据:

etcdctl --endpoints=$ENDPOINTS get foo

# 结果
foo
Hello World!

使用该命令可以将ETCD中将键为foo的值读取出来。

如果需要更加细节的数据,那么可以使用write-out="json"命令,这将输出json格式的细节内容:

etcdctl --endpoints=$ENDPOINTS --write-out="json" get foo
{"header":{"cluster_id":289318470931837780,"member_id":14947050114012957595,"revision":3,"raft_term":4,
"kvs":[{"key":"Zm9v","create_revision":2,"mod_revision":3,"version":2,"value":"SGVsbG8gV29ybGQh"}]}}

kvs中包含键值对信息,均是经过base64编码之后的数据。

通过键前缀获取数据

ETCD 可以使用键的前缀值来获取键值对。

首先向 ETCD 插入三条数据:

etcdctl --endpoints=$ENDPOINTS put web1 value1
etcdctl --endpoints=$ENDPOINTS put web2 value2
etcdctl --endpoints=$ENDPOINTS put web3 value3

如果我希望获取键前缀为web的所有键值对,那么上面三个数据均会被查询到:

etcdctl --endpoints=$ENDPOINTS get web --prefix

通过--prefix来实现键前缀的查询。

删除数据

删除某一个键:

etcdctl --endpoints=$ENDPOINTS del foo 

删除键值为foo的键值对。

范围删除,ETCD 可以进行范围删除。

  1. 删除foo1foo9
etcdctl --endpoints=$ENDPOINTS del foo foo9

如果有键在范围foofoo9内,那么该键值对就会被删除。

  1. 返回被删除的键值对的值:
etcdctl --endpoints=$ENDPOINTS del foo --prev-kv

# 结果
foo
Hello World!
  1. 通过键的前缀值来删除键值对:
etcdctl --endpoints=$ENDPOINTS del foo --prefix

该命令会将所有键的前缀为foo的键值对删除。

  1. 删除 byte 值大于等于该键的键值对:
etcdctl --endpoints=$ENDPOINTS del b --from-key

如果 ETCD 中存在键为z、t的,那么他们的 byte 值均大于b,因此都会被删除。

Txn方法可以在单个事务中处理多个请求

client.Txn(ctx).If(cmp1, cmp2, ...).Then(op1, op2, ...,).Else(op1, op2, …)

事务的API由 if 、then、else组成,在if语句中可以添加一系列条件表达式, 如果全部成立,那么就执行then中的 get/put/delete 等操作,否则执行 else 中的 get/put/delete操作。

etcdctl --endpoints=$ENDPOINTS put user1 bad
etcdctl --endpoints=$ENDPOINTS txn --interactive

compares:
value("user1") = "bad"

success requests (get, put, delete):
del user1

failure requests (get, put, delete):
put user1 good

使用txn --interactive命令来使用事务API。

compares中写条件表达式,表示 if 逻辑,success requests (get, put, delete):表示执行then逻辑,failure requests (get, put, delete):表示执行else逻辑。

监听键的值的变化

当A进程监听某一个键的时候,一旦在其他进程中修改了该键的值,那么A进程就会获取到变化,并将最新的键值对返回。

etcdctl --endpoints=$ENDPOINTS watch foo

# 在其他进程修改该键的值: etcdctl --endpoints=$ENDPOINTS put foo newOne

PUT
foo
newOne

监听也可以加许多参数:

  • web --prefix监听所有前缀为web的键值
  • foo foo9监听foofoo9范围内的所有键值对
  • --prev-kv在变更的同时,会返回修改之前的键值对和修改之后的键值对
  • --progress-notify
  • rev=0从修订版本0开始观察键值对的历史变动

给键授予租约

应用可以为 ETCD 集群里的键授予租约。当某一个键被授予租约后,他的存活时间就被绑定到了租约的存活时间,租约的存活时间会被TTL管理。一旦租约到期,租约就会过期并且所有附带的键都会被删除。

授予租约:

etcdctl --endpoints=$ENDPOINTS lease grant 300

lease 2be7547fbc6a5afa granted with TTL(300s)

# 将键 sample 附加到租约 2be7547fbc6a5afa 
etcdctl --endpoints=$ENDPOINTS put sample value --lease=2be7547fbc6a5afa
etcdctl --endpoints=$ENDPOINTS get sample

维持租约:

etcdctl --endpoints=$ENDPOINTS lease keep-alive 2be7547fbc6a5afa

刷新租约过期时间。

撤销租约:

etcdctl --endpoints=$ENDPOINTS lease revoke 2be7547fbc6a5afa
# or after 300 seconds
etcdctl --endpoints=$ENDPOINTS get sample

通过租约的id来撤销租约,一旦租约被撤销,绑定到相应租约的键值对也会被删除。



获取租约信息:

etcdctl --endpoints=$ENDPOINTS lease timetolive 2be7547fbc6a5afa
lease 2be7547fbc6a5afa granted with TTL(300s), remaining(258s)

该命令会获取租约的TTL(Time To Live)以及剩余有效时间。

获取租约信息及其附带的键的信息:

etcdctl --endpoints=$ENDPOINTS lease timetolive --keys 2be7547fbc6a5afa
lease 694d5765fc71500b granted with TTL(300s), remaining(132s), attached keys([zoo2 zoo1])

# 如果租约已经过期或者不存在,它将给出下面的应答:
Error:  etcdserver: requested lease not found

如何创建一个分布式锁

Etcd分布式锁原理和使用

juejin.cn/post/706290…

juejin.cn/post/727675…

分布式锁的原理以及实现。

分布式锁要求

  • 互斥性:在任意时刻,对于同一个锁,只有一个客户端能持有,从而保证一个共享资源同一时间只能被一个客户端操作;
  • 安全性:即不会形成死锁,当一个客户端在持有锁的期间崩溃而没有主动解锁的情况下,其持有的锁也能够被正确释放,并保证后续其它客户端能加锁
  • 可用性:当提供锁服务的节点发生宕机等不可恢复性故障时,“热备” 节点能够接替故障的节点继续提供服务,并保证自身持有的数据与故障节点一致。
  • 对称性:对于任意一个锁,其加锁和解锁必须是同一个客户端,即客户端 A 不能把客户端 B 加的锁给解了。
etcdctl --endpoints=$ENDPOINTS lock mutex1

# another client with the same name blocks
etcdctl --endpoints=$ENDPOINTS lock mutex1

这里创建分布式锁 mutex1,只有当第一个进程的锁释放后,第二个进程才能获得锁。具体的实现其实比较复杂,可以参考上面的三篇文章。

查看集群状态

etcdctl --write-out=table --endpoints=$ENDPOINTS endpoint status

+------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
|    ENDPOINT      |        ID        | VERSION | DB SIZE | IS LEADER | IS LEARNER | RAFT TERM | RAFT INDEX | RAFT APPLIED INDEX | ERRORS |
+------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------+
| 10.240.0.17:2379 | 4917a7ab173fabe7 |  3.5.0  |   45 kB |      true |      false |         4 |      16726 |              16726 |        |
| 10.240.0.18:2379 | 59796ba9cd1bcd72 |  3.5.0  |   45 kB |     false |      false |         4 |      16726 |              16726 |        |
| 10.240.0.19:2379 | 94df724b66343e6c |  3.5.0  |   45 kB |     false |      false |         4 |      16726 |              16726 |        |
+------------------+------------------+---------+---------+-----------+------------+-----------+------------+--------------------+--------|

使用endpoint status可以查看集群状态。

etcdctl --endpoints=$ENDPOINTS endpoint health

10.240.0.17:2379 is healthy: successfully committed proposal: took = 3.345431ms
10.240.0.19:2379 is healthy: successfully committed proposal: took = 3.767967ms
10.240.0.18:2379 is healthy: successfully committed proposal: took = 4.025451ms

数据持久化

etcdctl --endpoints=$ENDPOINTS snapshot save my.db

Snapshot saved at my.db
etcdutl --write-out=table snapshot status my.db

+---------+----------+------------+------------+
|  HASH   | REVISION | TOTAL KEYS | TOTAL SIZE |
+---------+----------+------------+------------+
| c55e8b8 |        9 |         13 | 25 kB      |
+---------+----------+------------+------------+

注意:只能从一个节点请求快照,因此 $ENDPOINTS 中只能存在一个节点。