etcd API V2
獲取etcd版本
curl -L http://127.0.0.1:2379/version
設(shè)置etcd的key的value
V2版本的key在etcd是按照目錄格式來存儲(chǔ)的:
[root@compile-server bin]# curl http://127.0.0.1:2379/v2/keys/message -XPUT -d value="Hello world"
{"action":"set","node":{"key":"/message","value":"Hello world","modifiedIndex":4,"createdIndex":4}}
返回屬性:
1.action:就是剛才我們執(zhí)行的請(qǐng)求操作,XPUT對(duì)應(yīng)的是set
2.node.key :對(duì)應(yīng)的是我們?cè)O(shè)置的key,以“/”作為開頭
3.value:代表的是如果命令執(zhí)行成功,key的值
4.node.createdIndex : 這個(gè)其實(shí)是raft內(nèi)部的一個(gè)單調(diào)遞增的值,對(duì)于etcd數(shù)據(jù)的更改操作都會(huì)分配一個(gè)index,所以可以看到披粟,一開始etcd啟動(dòng)的時(shí)候,不是從0而是從4開始的,這是因?yàn)閑tcd內(nèi)部的一些消息交互(時(shí)鐘之類)造成的窖杀。
5.node.modifiedIndex:set, delete, update, create, compareAndSwap and compareAndDelete 這些操作都會(huì)造成這個(gè)值的變動(dòng)。
對(duì)于createdIndex 和 modifiedIndex兩個(gè)值裙士,基本上是一直相等的入客,當(dāng)你-XPUT的時(shí)候,會(huì)同步更新這兩個(gè)值腿椎,但delete和update的時(shí)候這兩個(gè)值就會(huì)不同了桌硫,只有modifiedIndex會(huì)增加。
消息頭
etcd的回復(fù)里面啃炸,包含了簡(jiǎn)單的消息頭铆隘,里面的數(shù)據(jù)也比較重要。
[root@compile-server bin]# curl http://127.0.0.1:2379/v2/keys/message -vv
* About to connect() to 127.0.0.1 port 2379 (#0)
* Trying 127.0.0.1...
* Connected to 127.0.0.1 (127.0.0.1) port 2379 (#0)
> GET /v2/keys/message HTTP/1.1
> User-Agent: curl/7.29.0
> Host: 127.0.0.1:2379
> Accept: */*
>
< HTTP/1.1 200 OK
< Content-Type: application/json
< X-Etcd-Cluster-Id: cdf818194e3a8c32
< X-Etcd-Index: 31
< X-Raft-Index: 33
< X-Raft-Term: 2
< Date: Fri, 10 May 2019 08:43:37 GMT
< Content-Length: 102
<
{"action":"get","node":{"key":"/message","value":"Hello world","modifiedIndex":31,"createdIndex":31}}
* Connection #0 to host 127.0.0.1 left intact
對(duì)于etcd的header:
1.X-Etcd-Index 代表的是上面說的"modifiedIndex"南用。watch的時(shí)候默認(rèn)就是從這個(gè)index后面開始的膀钠。
2.X-Raft-Index 代表的是底層的raft的協(xié)議
3.X-Raft-Term 代表的是raft的任期的概念。在raft里面這個(gè)和logindex是很重要的兩個(gè)標(biāo)志位裹虫,對(duì)于選舉有重大意義(raft協(xié)議規(guī)定選主必須滿足數(shù)據(jù)最新+term最大)
讀取數(shù)據(jù)
數(shù)據(jù)讀取比較簡(jiǎn)單肿嘲,直接讀取某個(gè)key就可以。
curl http://127.0.0.1:2379/v2/keys/message
[root@compile-server bin]# curl http://127.0.0.1:2379/v2/keys/message
{"action":"get","node":{"key":"/message","value":"Hello world","modifiedIndex":33,"createdIndex":33}}
更改key的值
和設(shè)置一樣筑公,更改的時(shí)候用也是PUT操作
curl http://127.0.0.1:2379/v2/keys/message -XPUT -d value="Hello etcd"
{"action":"set","node":{"key":"/message","value":"Hello etcd","modifiedIndex":34,"createdIndex":34},"
prevNode":{"key":"/message","value":"Hello world","modifiedIndex":33,"createdIndex":33}}
更改key的value的時(shí)候雳窟,可以看到有個(gè)preNode,代表的是我們?cè)趫?zhí)行該操作之前,node的狀態(tài)匣屡。如果之前不存在這個(gè)key封救,那么就不存在preNode.
刪除key
[root@compile-server bin]# curl http://127.0.0.1:2379/v2/keys/message -XDELETE
{"action":"delete","node":{"key":"/message","modifiedIndex":35,"createdIndex":34},"prevNode":{"key":"/message","value":"Hello etcd","modifiedIndex":34,"createdIndex":34}}
刪除操作和更改操作基本一致拇涤,不再講解。
使用key的TTL
TTL就是給key設(shè)置一個(gè)timeout兴泥,時(shí)間到了之后工育,key會(huì)被自動(dòng)刪除,當(dāng)然你可以在超時(shí)之前刪除timeout搓彻,這樣key就不會(huì)有超期了如绸。同樣的你也可以不斷的去刷新key的timeout,這樣可以起到看門狗的作用(可以用來探測(cè)etcd的存活)旭贬。
curl http://127.0.0.1:2379/v2/keys/foo -XPUT -d value=bar -d ttl=5
//ttl代表的是超時(shí)時(shí)間怔接,這里是5秒。
curl http://127.0.0.1:2379/v2/keys/foo //這里是讀取key
{"action":"get","node":{"key":"/foo","value":"bar","expiration":"2019-05-12T03:09:23.606325102Z","ttl":1,"modifiedIndex":39,"createdIndex":39}}
//在這里可以到有個(gè)expiration稀轨,代表的是從什么時(shí)間點(diǎn)開始的扼脐,ttl是代表剩余的時(shí)間。當(dāng)5s超時(shí)之后再用get指令奋刽。
{"errorCode":100,"message":"Key not found","cause":"/foo","index":40}
//返回的是100瓦侮,key not found.
[root@compile-server bin]# curl http://127.0.0.1:2379/v2/keys/foo -XPUT -d value=bar -d ttl= -d prevExist=true
//這里是刪除ttl的命令,這樣foo這個(gè)key就不會(huì)有超時(shí)機(jī)制了佣谐。
{"action":"update","node":{"key":"/foo","value":"bar","modifiedIndex":53,"createdIndex":52},"prevNode":{"key":"/foo","value":"bar","exon":"2019-05-12T03:12:14.138970921Z","ttl":1,"modifiedIndex":52,"createdIndex":52}}
注意這里的action是udate蓉冈,createdIndex是不會(huì)增加的乖杠,modifiedIndex是增加的典鸡。
需要注意的一點(diǎn)是潮针,timeout只會(huì)由leader來觸發(fā),如果一個(gè)instance(不是leader)因?yàn)槟撤N原因退出集群了雌澄,那么該instance的超時(shí)key就不會(huì)刪除了斋泄,除非它重新加入集群,leader會(huì)幫它刪掉(個(gè)人認(rèn)為這時(shí)候不再是timeout刪除镐牺,而是raft底層的數(shù)據(jù)同步炫掐,會(huì)把刪除操作append到節(jié)點(diǎn),節(jié)點(diǎn)會(huì)刪除數(shù)據(jù).如果leader退出了睬涧,那么新的leader會(huì)執(zhí)行timeout操作卒废。
更新key的TTL
上面說過,在超時(shí)之前可以用命令重新更新key的ttl宙地。
curl http://127.0.0.1:2379/v2/keys/foo -XPUT -d value=bar -d ttl=5 //設(shè)置key
curl http://127.0.0.1:2379/v2/keys/foo -XPUT -d ttl=5 -d refresh=true -d prevExist=true //更新key的ttl摔认,這里有個(gè)問題refresh和preExist分別代表什么?作用是什么宅粥?
//首先設(shè)置了一個(gè)key
[root@compile-server bin]# curl http://127.0.0.1:2379/v2/keys/foo -XPUT -d ttl=30
{"action":"set","node":{"key":"/foo","value":"","expiration":"2019-05-12T03:40:12.864653611Z","ttl":30,"modifiedIndex":104,"createdIndex":104}}
//加了refresh之后参袱,返回的action是“set”,這個(gè)地方應(yīng)該會(huì)有點(diǎn)疑惑,因?yàn)槲覀冇玫膔efresh抹蚀,理論上應(yīng)該是update呀??
[root@compile-server bin]# curl http://127.0.0.1:2379/v2/keys/foo -XPUT -d ttl=30 -d refresh=true
{"action":"set","node":{"key":"/foo","value":"","expiration":"2019-05-12T03:40:33.419839799Z","ttl":30,"modifiedIndex":105,"createdIndex":105},"prevNode":{"key":"/foo","value":"","expiration":"2019-05-12T03:40:12.864653611Z","ttl":10,"modifiedIndex":104,"createdIndex":104}}
//這個(gè)地方用是prevExist剿牺,檢查是否有這個(gè)key,等于true可以理解為強(qiáng)制寫入环壤,默認(rèn)的是true晒来,如果為false,二次寫入會(huì)報(bào)錯(cuò)郑现。
[root@compile-server bin]# curl http://127.0.0.1:2379/v2/keys/foo -XPUT -d ttl=30 -d prevExist=true
{"action":"update","node":{"key":"/foo","value":"","expiration":"2019-05-12T03:40:45.027170827Z","ttl":30,"modifiedIndex":106,"createdIndex":105},"prevNode":{"key":"/foo","value":"","expiration":"2019-05-12T03:40:33.419839799Z","ttl":19,"modifiedIndex":105,"createdIndex":105}}
[root@compile-server bin]# curl http://127.0.0.1:2379/v2/keys/foo -XPUT -d ttl=30 -d refresh=true -d prevExist=true
{"action":"update","node":{"key":"/foo","value":"","expiration":"2019-05-12T03:40:54.905998913Z","ttl":30,"modifiedIndex":107,"createdIndex":105},"prevNode":{"key":"/foo","value":"","expiration":"2019-05-12T03:40:45.027170827Z","ttl":21,"modifiedIndex":106,"createdIndex":105}}
watch功能
watch功能是etcd里面一個(gè)很重要的功能湃崩,現(xiàn)在有v2和v3兩個(gè)版本。分別對(duì)應(yīng)了不同的設(shè)計(jì)接箫,可以參照下面的鏈接:
https://blog.csdn.net/zl1zl2zl3/article/details/79627412
我們可以通過常輪訓(xùn)來監(jiān)聽key的變化攒读,同時(shí)可以使用recursive=true參數(shù)來對(duì)子key(目錄)監(jiān)聽。
//啟動(dòng)監(jiān)聽辛友,會(huì)一直等待薄扁,直到收到事件
curl http://127.0.0.1:2379/v2/keys/foo?wait=true
//設(shè)置key,上面會(huì)收到該事件消息
curl http://127.0.0.1:2379/v2/keys/foo -XPUT -d value=bar
//可以看到收到事件信息,第一次設(shè)置該key只會(huì)顯示本次的操作废累,如果再監(jiān)聽一次可以看到上次的操作邓梅。
{"action":"set","node":{"key":"/foo","value":"bar","modifiedIndex":22,"createdIndex":22}}
//因?yàn)閑tcd會(huì)保存最近的1000條記錄,如果直接加上waitIndex則直接返回上次的數(shù)據(jù)邑滨,這里選擇上面的modifiedIndex震放,是不會(huì)阻塞等待的
curl 'http://127.0.0.1:2379/v2/keys/foo?wait=true&waitIndex=22'
//如果選擇一個(gè)比目前小的waitIndex(比如選擇10),則會(huì)返回9之后最早發(fā)生的一次。
curl 'http://127.0.0.1:2379/v2/keys/foo?wait=true&waitIndex=10'
//17是最早的一次改動(dòng)驼修,所以就會(huì)返回最早的一次
{"action":"set","node":{"key":"/foo","value":"","modifiedIndex":17,"createdIndex":17}}
//recursive參數(shù)
curl 'http://127.0.0.1:2379/v2/keys/dir/?wait=true&recursive=true'
curl 'http://127.0.0.1:2379/v2/keys/dir/?wait=true'
兩個(gè)watch命令都是watch同一個(gè)目錄,但第一個(gè)用了recursive诈铛,也就是會(huì)監(jiān)聽目錄下面所有的key的變化乙各。下面的命令,如果操作目錄下的key幢竹,不會(huì)觸發(fā)事件耳峦。
注意:
etcd的watch智能保持100條記錄(V2版本,v3沒這個(gè)限制)焕毫,所以收到通知之后應(yīng)該將response交給其它線程去處理蹲坷,不要在watch線程里處理,不然容易阻塞R仂循签!
從已經(jīng)清空的Index監(jiān)聽
前面已經(jīng)說過etcd的v2版本只能保存1000個(gè)事件,如果已經(jīng)事件丟失疙咸,我們需要先得到目前的狀態(tài)县匠,然后再開始監(jiān)聽。比如我們對(duì)一個(gè)key設(shè)置了2000次,那么etcd里面保存的直郵1000-2000的事件乞旦,如果這時(shí)候我們用下面命令:
curl 'http://127.0.0.1:2379/v2/keys/foo?wait=true&waitIndex=8'
//我們將會(huì)收到下面的錯(cuò)誤
{"errorCode":401,"message":"The event in requested index is outdated and cleared","cause":"the requested history has been cleared [1008/8]","index":2007}
那現(xiàn)在我們?cè)趺撮_始監(jiān)聽這個(gè)key那贼穆,因?yàn)槲覀円膊恢纄tcd里面保存的事件是從哪里開始的。所以我們可以使用下面命令得到etcd現(xiàn)在的狀態(tài)兰粉。
curl 'http://127.0.0.1:2379/v2/keys/foo' -vv
< HTTP/1.1 200 OK
< Content-Type: application/json
< X-Etcd-Cluster-Id: 7e27652122e8b2ae
< X-Etcd-Index: 2007
< X-Raft-Index: 2615
< X-Raft-Term: 2
< Date: Mon, 05 Jan 2015 18:54:43 GMT
< Transfer-Encoding: chunked
<
{ "action":"get","node":{"key":"/foo","value":"bar","modifiedIndex":7,"createdIndex":7}}
前面已經(jīng)解釋過返回的信息的標(biāo)志位的意思了故痊。X-Etcd-Index代表的是etcd的狀態(tài),modifiedIndex代表的是上次修改這個(gè)key的index玖姑,X-Etcd-Index是大于等于modifiedIndex的愕秫,理論上來講用兩個(gè)index + 1是相同效果,但是用modifiedIndex可能會(huì)導(dǎo)致上面說的401error,所以我們一般都用X-Etcd-Index客峭。
連接關(guān)閉
連接關(guān)閉指的是可能server還沒來的及發(fā)送event豫领,但由于超時(shí)或者server發(fā)生了關(guān)機(jī),因?yàn)閔ttp的header是一旦接收到連接就會(huì)返回頭部的舔琅,所以返回值會(huì)是200:ok和空的消息體等恐。client需要自己處理這種情況。
創(chuàng)建自動(dòng)增長的順序keys
在一個(gè)目錄下使用POST可以創(chuàng)建出順序的key备蚓,有時(shí)候這個(gè)功能很有用课蔬,比如需要嚴(yán)格按照順序處理的事件〗汲ⅲ可以保證cleint可以公平的訪問mutex鎖(這個(gè)點(diǎn)沒太搞懂)
創(chuàng)建一個(gè)順序的key很簡(jiǎn)單:
curl http://127.0.0.1:2379/v2/keys/queue -XPOST -d value=Job1
//返回值
{
"action": "create",
"node": {
"createdIndex": 6,
"key": "/queue/00000000000000000006",
"modifiedIndex": 6,
"value": "Job1"
}
}
//獲取目錄下所有的順序key的命令
curl -s 'http://127.0.0.1:2379/v2/keys/queue?recursive=true&sorted=true'
這里的key的后綴是全局的etcd的index二跋。
目錄的TTL
etcd的存儲(chǔ)是以目錄樹的結(jié)構(gòu)來的,所以我們可以對(duì)某個(gè)目錄設(shè)置一個(gè)TTL流昏。
//設(shè)置一個(gè)目錄
curl http://127.0.0.1:2379/v2/keys/dir -XPUT -d ttl=30 -d dir=true
//刷新目錄的ttl
curl http://127.0.0.1:2379/v2/keys/dir -XPUT -d ttl=30 -d dir=true -d prevExist=true
目錄的ttl超時(shí)的時(shí)候扎即,如果有監(jiān)聽線程正在監(jiān)聽它下面的key,那么watch會(huì)收到一個(gè)expire的事件况凉。
etcd的CAS操作
etcd的 Compare-and-Swap
etcd主要通過三個(gè)標(biāo)志位來對(duì)etcd的數(shù)據(jù)進(jìn)行原子操作谚鄙,注意etcd的原子操作只能針對(duì)key,不能針對(duì)目錄刁绒,否則會(huì)返回102“Not a file” error闷营。
1.prevValue - 檢查key的上一個(gè)狀態(tài).
2.prevIndex -檢查key的modifiedIndex.(保證沒別的線程修改過)
3.prevExist - 如果是true,代表"update"請(qǐng)求知市,否則是create請(qǐng)求傻盟。
下面看幾個(gè)例子:
curl http://127.0.0.1:2379/v2/keys/foo -XPUT -d value=one
curl http://127.0.0.1:2379/v2/keys/foo?prevExist=false -XPUT -d value=three
返回值:
{
"cause": "/foo",
"errorCode": 105,
"index": 39776,
"message": "Key already exists"
}
curl http://127.0.0.1:2379/v2/keys/foo?prevValue=two -XPUT -d value=three
//返回值:
{
"cause": "[two != one]",
"errorCode": 101,
"index": 8,
"message": "Compare failed"
}
curl http://127.0.0.1:2379/v2/keys/foo?prevValue=one -XPUT -d value=two
//返回值
{
"action": "compareAndSwap",
"node": {
"createdIndex": 8,
"key": "/foo",
"modifiedIndex": 9,
"value": "two"
},
"prevNode": {
"createdIndex": 8,
"key": "/foo",
"modifiedIndex": 8,
"value": "one"
}
}
etcd的Compare-and-Delete
etcd的刪除操作也支持原子操作,標(biāo)志位和上面的比少了prevExist嫂丙。其它的是一樣的娘赴。看例子:
curl http://127.0.0.1:2379/v2/keys/foo -XPUT -d value=one
//因?yàn)関alue不一致跟啤,所以不能刪除筝闹。
curl http://127.0.0.1:2379/v2/keys/foo?prevValue=two -XDELETE
//返回值
{
"errorCode": 101,
"message": "Compare failed",
"cause": "[two != one]",
"index": 8
}
//prevIndex不一致媳叨,所以不能刪除。
curl http://127.0.0.1:2379/v2/keys/foo?prevIndex=1 -XDELETE
//返回值
{
"errorCode": 101,
"message": "Compare failed",
"cause": "[1 != 8]",
"index": 8
}
curl http://127.0.0.1:2379/v2/keys/foo?prevValue=one -XDELETE
{
"action": "compareAndDelete",
"node": {
"key": "/foo",
"modifiedIndex": 9,
"createdIndex": 8
},
"prevNode": {
"key": "/foo",
"value": "one",
"modifiedIndex": 8,
"createdIndex": 8
}
}
創(chuàng)建目錄
在大多數(shù)情況下关顷,目錄都會(huì)在創(chuàng)建key的時(shí)候自動(dòng)創(chuàng)建糊秆,但有時(shí)候我們需要自己去創(chuàng)建一個(gè)目錄或者刪除一個(gè)目錄。用參數(shù)dir=true就可以了议双。
curl http://127.0.0.1:2379/v2/keys/dir -XPUT -d dir=true
{
"action": "set",
"node": {
"createdIndex": 30,
"dir": true,
"key": "/dir",
"modifiedIndex": 30
}
}
列出目錄
在etcd中痘番,如果想獲得目錄下所有的key,可以用下面的命令:
curl http://127.0.0.1:2379/v2/keys/?recursive=true
如果沒有recursive平痰,那么只會(huì)顯示目錄汞舱,不會(huì)現(xiàn)在目錄下面的key或者子目錄。
刪除目錄
刪除etcd的目錄的時(shí)候宗雇,如果目錄下面有key或者別的目錄存在昂芜,你刪除的時(shí)候會(huì)返回下面的錯(cuò)誤:
{"errorCode":108,"message":"Directory not empty","cause":"/dir2","index":156}
所以如果想直接刪除整個(gè)目錄,可以用下面的命令(recursive=true):
curl 'http://127.0.0.1:2379/v2/keys/dir2?recursive=true' -XDELETE
創(chuàng)建隱藏的node
etcd可以通過添加_前綴創(chuàng)建隱藏的key或者目錄赔蒲,這時(shí)候你通過GET指令是看不到這個(gè)node的泌神。
etcd隱藏的node主要作用是為了安全性,特地去github上看了一下作者的解釋舞虱。
The hidden was designed for security through obscurity. So you cannot find the hidden key from API which is by design. You can find it if you can access the local disk though.
https://github.com/etcd-io/etcd/issues/6375
就是說隱藏key你通過etcd的api是看不到的欢际,除非你能登陸本地的disk,然后查看本地的存儲(chǔ)文件矾兜。保密性高损趋,我們可以將我們一些key放在里面。創(chuàng)建的指令如下:
curl http://127.0.0.1:2379/v2/keys/_message -XPUT -d value="Hello hidden world"
通過文件設(shè)置key
我們可以用etcd存儲(chǔ)一些小的配置文件椅寺。
echo "Hello\nWorld" > afile.txt
curl http://127.0.0.1:2379/v2/keys/afile -XPUT --data-urlencode value@afile.txt
線性化讀取
大家都知道etcd的寫操作是都要經(jīng)過raft協(xié)議同步的浑槽,線性化讀取也是同樣的道理。如果 Get的時(shí)候使用參數(shù)quorum=true返帕,那么GET操作也要走raft協(xié)議桐玻,不是本地直接返回。
統(tǒng)計(jì)
etcd的狀態(tài)統(tǒng)計(jì)
我們可以查看etcd的一些內(nèi)部統(tǒng)計(jì)信息來觀察etcd的狀態(tài)溉旋。主要就分為etcd的狀態(tài)和存儲(chǔ)狀態(tài)。
//獲取etcd的leader狀態(tài)
curl http://127.0.0.1:2379/v2/stats/leader
//獲取自己的狀態(tài)
curl http://127.0.0.1:2379/v2/stats/self
每個(gè)標(biāo)志位的含義:
id: the unique identifier for the member
leaderInfo.leader: id of the current leader member
leaderInfo.uptime: amount of time the leader has been leader
name: this member's name
recvAppendRequestCnt: number of append requests this node has processed
recvBandwidthRate: number of bytes per second this node is receiving (follower only)
recvPkgRate: number of requests per second this node is receiving (follower only)
sendAppendRequestCnt: number of requests that this node has sent
sendBandwidthRate: number of bytes per second this node is sending (leader only). This value is undefined on single member clusters.
sendPkgRate: number of requests per second this node is sending (leader only). This value is undefined on single member clusters.
state: either leader or follower
startTime: the time when this node was started
etcd的存儲(chǔ)統(tǒng)計(jì)
curl http://127.0.0.1:2379/v2/stats/store
{
"compareAndSwapFail": 0,
"compareAndSwapSuccess": 0,
"createFail": 0,
"createSuccess": 2,
"deleteFail": 0,
"deleteSuccess": 0,
"expireCount": 0,
"getsFail": 4,
"getsSuccess": 75,
"setsFail": 2,
"setsSuccess": 4,
"updateFail": 0,
"updateSuccess": 0,
"watchers": 0
}
etcd的存儲(chǔ)的狀態(tài)是存儲(chǔ)在內(nèi)存中的嫉髓,所以重啟之后是會(huì)重置各個(gè)參數(shù)观腊。
結(jié)束語
掌握etcd的API是使用etcd的前提條件,所以把v2版本的按照個(gè)人理解和操作整理出來算行。后面會(huì)整理v3版本的API梧油。