etcd API-V2

etcd API V2


獲取etcd版本

curl -L http://127.0.0.1:2379/version

設(shè)置etcd的key的value

V2版本的key在etcd是按照目錄格式來存儲的:

[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í)行的請求操作,XPUT對應(yīng)的是set
2.node.key :對應(yīng)的是我們設(shè)置的key,以“/”作為開頭
3.value:代表的是如果命令執(zhí)行成功,key的值
4.node.createdIndex : 這個其實是raft內(nèi)部的一個單調(diào)遞增的值,對于etcd數(shù)據(jù)的更改操作都會分配一個index,所以可以看到,一開始etcd啟動的時候,不是從0而是從4開始的,這是因為etcd內(nèi)部的一些消息交互(時鐘之類)造成的。
5.node.modifiedIndex:set, delete, update, create, compareAndSwap and compareAndDelete 這些操作都會造成這個值的變動。

對于createdIndex 和 modifiedIndex兩個值,基本上是一直相等的,當(dāng)你-XPUT的時候,會同步更新這兩個值,但delete和update的時候這兩個值就會不同了,只有modifiedIndex會增加。

消息頭

etcd的回復(fù)里面,包含了簡單的消息頭,里面的數(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

對于etcd的header:
    1.X-Etcd-Index  代表的是上面說的"modifiedIndex"。watch的時候默認(rèn)就是從這個index后面開始的。
    2.X-Raft-Index 代表的是底層的raft的協(xié)議
    3.X-Raft-Term 代表的是raft的任期的概念。在raft里面這個和logindex是很重要的兩個標(biāo)志位,對于選舉有重大意義(raft協(xié)議規(guī)定選主必須滿足數(shù)據(jù)最新+term最大)

讀取數(shù)據(jù)

數(shù)據(jù)讀取比較簡單,直接讀取某個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è)置一樣,更改的時候用也是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的時候,可以看到有個preNode,代表的是我們在執(zhí)行該操作之前,node的狀態(tài)。如果之前不存在這個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è)置一個timeout,時間到了之后,key會被自動刪除,當(dāng)然你可以在超時之前刪除timeout,這樣key就不會有超期了。同樣的你也可以不斷的去刷新key的timeout,這樣可以起到看門狗的作用(可以用來探測etcd的存活)。

curl http://127.0.0.1:2379/v2/keys/foo -XPUT -d value=bar -d ttl=5 

//ttl代表的是超時時間,這里是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}}

//在這里可以到有個expiration,代表的是從什么時間點開始的,ttl是代表剩余的時間。當(dāng)5s超時之后再用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這個key就不會有超時機制了。
{"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是不會增加的,modifiedIndex是增加的。

需要注意的一點是,timeout只會由leader來觸發(fā),如果一個instance(不是leader)因為某種原因退出集群了,那么該instance的超時key就不會刪除了,除非它重新加入集群,leader會幫它刪掉(個人認(rèn)為這時候不再是timeout刪除,而是raft底層的數(shù)據(jù)同步,會把刪除操作append到節(jié)點,節(jié)點會刪除數(shù)據(jù).如果leader退出了,那么新的leader會執(zhí)行timeout操作。

更新key的TTL

上面說過,在超時之前可以用命令重新更新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,這里有個問題refresh和preExist分別代表什么?作用是什么?

//首先設(shè)置了一個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”,這個地方應(yīng)該會有點疑惑,因為我們用的refresh,理論上應(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}}

//這個地方用是prevExist,檢查是否有這個key,等于true可以理解為強制寫入,默認(rèn)的是true,如果為false,二次寫入會報錯。
[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里面一個很重要的功能,現(xiàn)在有v2和v3兩個版本。分別對應(yīng)了不同的設(shè)計,可以參照下面的鏈接:
https://blog.csdn.net/zl1zl2zl3/article/details/79627412

我們可以通過常輪訓(xùn)來監(jiān)聽key的變化,同時可以使用recursive=true參數(shù)來對子key(目錄)監(jiān)聽。

//啟動監(jiān)聽,會一直等待,直到收到事件
curl http://127.0.0.1:2379/v2/keys/foo?wait=true
//設(shè)置key,上面會收到該事件消息
curl http://127.0.0.1:2379/v2/keys/foo -XPUT -d value=bar
//可以看到收到事件信息,第一次設(shè)置該key只會顯示本次的操作,如果再監(jiān)聽一次可以看到上次的操作。
{"action":"set","node":{"key":"/foo","value":"bar","modifiedIndex":22,"createdIndex":22}}
 //因為etcd會保存最近的1000條記錄,如果直接加上waitIndex則直接返回上次的數(shù)據(jù),這里選擇上面的modifiedIndex,是不會阻塞等待的
curl 'http://127.0.0.1:2379/v2/keys/foo?wait=true&waitIndex=22'
//如果選擇一個比目前小的waitIndex(比如選擇10),則會返回9之后最早發(fā)生的一次。
curl 'http://127.0.0.1:2379/v2/keys/foo?wait=true&waitIndex=10'
//17是最早的一次改動,所以就會返回最早的一次
{"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'

兩個watch命令都是watch同一個目錄,但第一個用了recursive,也就是會監(jiān)聽目錄下面所有的key的變化。下面的命令,如果操作目錄下的key,不會觸發(fā)事件。

注意:

etcd的watch智能保持100條記錄(V2版本,v3沒這個限制),所以收到通知之后應(yīng)該將response交給其它線程去處理,不要在watch線程里處理,不然容易阻塞??!

從已經(jīng)清空的Index監(jiān)聽

前面已經(jīng)說過etcd的v2版本只能保存1000個事件,如果已經(jīng)事件丟失,我們需要先得到目前的狀態(tài),然后再開始監(jiān)聽。比如我們對一個key設(shè)置了2000次,那么etcd里面保存的直郵1000-2000的事件,如果這時候我們用下面命令:

curl 'http://127.0.0.1:2379/v2/keys/foo?wait=true&waitIndex=8'
 //我們將會收到下面的錯誤
{"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)在我們怎么開始監(jiān)聽這個key那,因為我們也不知道etcd里面保存的事件是從哪里開始的。所以我們可以使用下面命令得到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代表的是上次修改這個key的index,X-Etcd-Index是大于等于modifiedIndex的,理論上來講用兩個index + 1是相同效果,但是用modifiedIndex可能會導(dǎo)致上面說的401error,所以我們一般都用X-Etcd-Index。

連接關(guān)閉

連接關(guān)閉指的是可能server還沒來的及發(fā)送event,但由于超時或者server發(fā)生了關(guān)機,因為http的header是一旦接收到連接就會返回頭部的,所以返回值會是200:ok和空的消息體。client需要自己處理這種情況。

創(chuàng)建自動增長的順序keys

在一個目錄下使用POST可以創(chuàng)建出順序的key,有時候這個功能很有用,比如需要嚴(yán)格按照順序處理的事件。可以保證cleint可以公平的訪問mutex鎖(這個點沒太搞懂)

創(chuàng)建一個順序的key很簡單:

    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的存儲是以目錄樹的結(jié)構(gòu)來的,所以我們可以對某個目錄設(shè)置一個TTL。

    //設(shè)置一個目錄
    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超時的時候,如果有監(jiān)聽線程正在監(jiān)聽它下面的key,那么watch會收到一個expire的事件。

etcd的CAS操作

etcd的 Compare-and-Swap

etcd主要通過三個標(biāo)志位來對etcd的數(shù)據(jù)進(jìn)行原子操作,注意etcd的原子操作只能針對key,不能針對目錄,否則會返回102“Not a file” error。

1.prevValue - 檢查key的上一個狀態(tài).

2.prevIndex -檢查key的modifiedIndex.(保證沒別的線程修改過)

3.prevExist - 如果是true,代表"update"請求,否則是create請求。

下面看幾個例子:

    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。其它的是一樣的??蠢樱?/p>

    curl http://127.0.0.1:2379/v2/keys/foo -XPUT -d value=one
    
    //因為value不一致,所以不能刪除。
    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ù)情況下,目錄都會在創(chuàng)建key的時候自動創(chuàng)建,但有時候我們需要自己去創(chuàng)建一個目錄或者刪除一個目錄。用參數(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,那么只會顯示目錄,不會現(xiàn)在目錄下面的key或者子目錄。

刪除目錄

刪除etcd的目錄的時候,如果目錄下面有key或者別的目錄存在,你刪除的時候會返回下面的錯誤:

{"errorCode":108,"message":"Directory not empty","cause":"/dir2","index":156}

所以如果想直接刪除整個目錄,可以用下面的命令(recursive=true)

curl 'http://127.0.0.1:2379/v2/keys/dir2?recursive=true' -XDELETE

創(chuàng)建隱藏的node

etcd可以通過添加_前綴創(chuàng)建隱藏的key或者目錄,這時候你通過GET指令是看不到這個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,然后查看本地的存儲文件。保密性高,我們可以將我們一些key放在里面。創(chuàng)建的指令如下:

    curl http://127.0.0.1:2379/v2/keys/_message -XPUT -d value="Hello hidden world"

通過文件設(shè)置key

我們可以用etcd存儲一些小的配置文件。

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ù)quorum=true,那么GET操作也要走raft協(xié)議,不是本地直接返回。

統(tǒng)計

etcd的狀態(tài)統(tǒng)計

我們可以查看etcd的一些內(nèi)部統(tǒng)計信息來觀察etcd的狀態(tài)。主要就分為etcd的狀態(tài)和存儲狀態(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

每個標(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的存儲統(tǒng)計

    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的存儲的狀態(tài)是存儲在內(nèi)存中的,所以重啟之后是會重置各個參數(shù)。

https://coreos.com/etcd/docs/latest/v2/api.html

結(jié)束語

掌握etcd的API是使用etcd的前提條件,所以把v2版本的按照個人理解和操作整理出來。后面會整理v3版本的API。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容