etcd API-V2

etcd API V2


获取etcd版本

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

设置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:就是刚才我们执行的请求操作,XPUT对应的是set
2.node.key :对应的是我们设置的key,以“/”作为开头
3.value:代表的是如果命令执行成功,key的值
4.node.createdIndex : 这个其实是raft内部的一个单调递增的值,对于etcd数据的更改操作都会分配一个index,所以可以看到,一开始etcd启动的时候,不是从0而是从4开始的,这是因为etcd内部的一些消息交互(时钟之类)造成的。
5.node.modifiedIndex:set, delete, update, create, compareAndSwap and compareAndDelete 这些操作都会造成这个值的变动。

对于createdIndex 和 modifiedIndex两个值,基本上是一直相等的,当你-XPUT的时候,会同步更新这两个值,但delete和update的时候这两个值就会不同了,只有modifiedIndex会增加。

消息头

etcd的回复里面,包含了简单的消息头,里面的数据也比较重要。

[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的时候默认就是从这个index后面开始的。
    2.X-Raft-Index 代表的是底层的raft的协议
    3.X-Raft-Term 代表的是raft的任期的概念。在raft里面这个和logindex是很重要的两个标志位,对于选举有重大意义(raft协议规定选主必须满足数据最新+term最大)

读取数据

数据读取比较简单,直接读取某个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的值

和设置一样,更改的时候用也是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,代表的是我们在执行该操作之前,node的状态。如果之前不存在这个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设置一个timeout,时间到了之后,key会被自动删除,当然你可以在超时之前删除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是代表剩余的时间。当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来触发,如果一个instance(不是leader)因为某种原因退出集群了,那么该instance的超时key就不会删除了,除非它重新加入集群,leader会帮它删掉(个人认为这时候不再是timeout删除,而是raft底层的数据同步,会把删除操作append到节点,节点会删除数据.如果leader退出了,那么新的leader会执行timeout操作。

更新key的TTL

上面说过,在超时之前可以用命令重新更新key的ttl。

curl http://127.0.0.1:2379/v2/keys/foo -XPUT -d value=bar -d ttl=5 //设置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分别代表什么?作用是什么?

//首先设置了一个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”,这个地方应该会有点疑惑,因为我们用的refresh,理论上应该是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可以理解为强制写入,默认的是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里面一个很重要的功能,现在有v2和v3两个版本。分别对应了不同的设计,可以参照下面的链接:
https://blog.csdn.net/zl1zl2zl3/article/details/79627412

我们可以通过常轮训来监听key的变化,同时可以使用recursive=true参数来对子key(目录)监听。

//启动监听,会一直等待,直到收到事件
curl http://127.0.0.1:2379/v2/keys/foo?wait=true
//设置key,上面会收到该事件消息
curl http://127.0.0.1:2379/v2/keys/foo -XPUT -d value=bar
//可以看到收到事件信息,第一次设置该key只会显示本次的操作,如果再监听一次可以看到上次的操作。
{"action":"set","node":{"key":"/foo","value":"bar","modifiedIndex":22,"createdIndex":22}}
 //因为etcd会保存最近的1000条记录,如果直接加上waitIndex则直接返回上次的数据,这里选择上面的modifiedIndex,是不会阻塞等待的
curl 'http://127.0.0.1:2379/v2/keys/foo?wait=true&waitIndex=22'
//如果选择一个比目前小的waitIndex(比如选择10),则会返回9之后最早发生的一次。
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参数
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,也就是会监听目录下面所有的key的变化。下面的命令,如果操作目录下的key,不会触发事件。

注意:

etcd的watch智能保持100条记录(V2版本,v3没这个限制),所以收到通知之后应该将response交给其它线程去处理,不要在watch线程里处理,不然容易阻塞!!

从已经清空的Index监听

前面已经说过etcd的v2版本只能保存1000个事件,如果已经事件丢失,我们需要先得到目前的状态,然后再开始监听。比如我们对一个key设置了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}

那现在我们怎么开始监听这个key那,因为我们也不知道etcd里面保存的事件是从哪里开始的。所以我们可以使用下面命令得到etcd现在的状态。

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}}

前面已经解释过返回的信息的标志位的意思了。X-Etcd-Index代表的是etcd的状态,modifiedIndex代表的是上次修改这个key的index,X-Etcd-Index是大于等于modifiedIndex的,理论上来讲用两个index + 1是相同效果,但是用modifiedIndex可能会导致上面说的401error,所以我们一般都用X-Etcd-Index。

连接关闭

连接关闭指的是可能server还没来的及发送event,但由于超时或者server发生了关机,因为http的header是一旦接收到连接就会返回头部的,所以返回值会是200:ok和空的消息体。client需要自己处理这种情况。

创建自动增长的顺序keys

在一个目录下使用POST可以创建出顺序的key,有时候这个功能很有用,比如需要严格按照顺序处理的事件。可以保证cleint可以公平的访问mutex锁(这个点没太搞懂)

创建一个顺序的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的存储是以目录树的结构来的,所以我们可以对某个目录设置一个TTL。

    //设置一个目录
    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超时的时候,如果有监听线程正在监听它下面的key,那么watch会收到一个expire的事件。

etcd的CAS操作

etcd的 Compare-and-Swap

etcd主要通过三个标志位来对etcd的数据进行原子操作,注意etcd的原子操作只能针对key,不能针对目录,否则会返回102“Not a file” error。

1.prevValue - 检查key的上一个状态.

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的删除操作也支持原子操作,标志位和上面的比少了prevExist。其它的是一样的。看例子:

    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
        }
    }

创建目录

在大多数情况下,目录都会在创建key的时候自动创建,但有时候我们需要自己去创建一个目录或者删除一个目录。用参数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,那么只会显示目录,不会现在目录下面的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

创建隐藏的node

etcd可以通过添加_前缀创建隐藏的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放在里面。创建的指令如下:

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

通过文件设置key

我们可以用etcd存储一些小的配置文件。

echo "Hello\nWorld" > afile.txt
curl http://127.0.0.1:2379/v2/keys/afile -XPUT --data-urlencode value@afile.txt

线性化读取

大家都知道etcd的写操作是都要经过raft协议同步的,线性化读取也是同样的道理。如果 Get的时候使用参数quorum=true,那么GET操作也要走raft协议,不是本地直接返回。

统计

etcd的状态统计

我们可以查看etcd的一些内部统计信息来观察etcd的状态。主要就分为etcd的状态和存储状态。

    //获取etcd的leader状态
    curl http://127.0.0.1:2379/v2/stats/leader
    
    //获取自己的状态       
    curl http://127.0.0.1:2379/v2/stats/self

每个标志位的含义:

    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的存储统计

    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的存储的状态是存储在内存中的,所以重启之后是会重置各个参数。

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

结束语

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

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容