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 可以进行范围删除。
- 删除
foo1到foo9:
etcdctl --endpoints=$ENDPOINTS del foo foo9
如果有键在范围foo到foo9内,那么该键值对就会被删除。
- 返回被删除的键值对的值:
etcdctl --endpoints=$ENDPOINTS del foo --prev-kv
# 结果
foo
Hello World!
- 通过键的前缀值来删除键值对:
etcdctl --endpoints=$ENDPOINTS del foo --prefix
该命令会将所有键的前缀为foo的键值对删除。
- 删除 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监听foo到foo9范围内的所有键值对--prev-kv在变更的同时,会返回修改之前的键值对和修改之后的键值对--progress-notifyrev=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分布式锁原理和使用
分布式锁的原理以及实现。
分布式锁要求
- 互斥性:在任意时刻,对于同一个锁,只有一个客户端能持有,从而保证一个共享资源同一时间只能被一个客户端操作;
- 安全性:即不会形成死锁,当一个客户端在持有锁的期间崩溃而没有主动解锁的情况下,其持有的锁也能够被正确释放,并保证后续其它客户端能加锁
- 可用性:当提供锁服务的节点发生宕机等不可恢复性故障时,“热备” 节点能够接替故障的节点继续提供服务,并保证自身持有的数据与故障节点一致。
- 对称性:对于任意一个锁,其加锁和解锁必须是同一个客户端,即客户端 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 中只能存在一个节点。