【微服务】Etcd实现服务器注册和发现|Etcd、Eureka、Consul、Zookeeper 比较
目录
Etcd、Eureka、Consul、Zookeeper 的比较
都是key-value存储,redis 可以代替 etcd吗?
Etcd、Eureka、Consul、Zookeeper 的比较
etcd除了受到Zookeeper与doozer启发而催生的项目,还拥有与之类似的功能外,更具有以下4个特点:
- 简单:基于HTTP+JSON的API让你用curl命令就可以轻松使用。
- 安全:可选SSL客户认证机制。
- 快速:每个实例每秒支持一千次写操作。
- 可信:使用Raft算法充分实现了分布式。
至于为什么不用zookeeper或者eureka等,除了根据项目考虑之外,就看个人喜好了,如果有哪位大佬知道更多内容,麻烦也在留言区告知小编一下,万分感谢!
以下是常用的服务发现产品之间的比较:
Feature | Consul | zookeeper | etcd | euerka |
---|---|---|---|---|
服务健康检查 | 服务状态,内存,硬盘等 | (弱)长连接,keepalive | 连接心跳 | 可配支持 |
多数据中心 | 支持 | — | — | — |
kv存储服务 | 支持 | 支持 | 支持 | — |
一致性 | raft | paxos | raft | — |
cap | ca | cp | cp | ap |
使用接口(多语言能力) | 支持http和dns | 客户端 | http/grpc | http(sidecar) |
watch支持 | 全量/支持long polling | 支持 | 支持 long polling | 支持 long polling/大部分增量 |
自身监控 | metrics | — | metrics | metrics |
安全 | acl /https | acl | https支持(弱) | — |
spring cloud集成 | 已支持 | 已支持 | 已支持 | 已支持 |
https://blog.csdn.net/qq_34395857/article/details/89212566
zookeeper 实现一个简单的服务注册与发现:https://www.cnblogs.com/chinxi/p/12994321.html
Etcd
服务注册与发现的必要:
讲一个很简单的场景,一般服务端架构最前面是 一台网关 ,网关后面是 n 台运行着一样的 服务 的机器。 客户端一般就是访问网关 ,然后 网关 就把流量 转发到 后面的 服务器上。那么我们来考虑这么一个问题,后面 服务器 信息处理不过来的时候,我们需要加机器。最low的方法就是 加上一台服务器,然后 修改网关服务器的配置表 加上 新加的 服务器的IP 和端口,然后重启网关。还有就是 当 后面的 服务器万一 down 了,网关是不知道的,还会把 流量转发到 down的 服务器上,造成 服务的不可用。
要解决上面这个问题就需要 [服务注册与发现] 如etcd 这类产品了,etcd 是一个 分布式 的高一致性的 键值存储系统。我们每次网关 后面加一个服务,只需要向etcd 注册 该服务(其实就是 存一个值)然后向etcd 发送心跳,当etcd 没有检测到心跳就会 把这个键值对 删了(这整个动作是etcd里的租约模式),网关那边 就只需要 watch 这个 key ,就能够知道 所有服务的所有动态了。
链接:https://www.jianshu.com/p/7c0d23c818a5
etcd简介
etcd是一个开源的分布式键值对存储工具。在每个coreos节点上面运行的etcd,共同组建了coreos集群的共享数据总线。etcd可以保证coreos集群的稳定,可靠。当集群网络出现动荡,或者当前master节点出现异常时,etcd可以优雅的进行master节点的选举工作,同时恢复集群中损失的数据。
etcd为分布式系统提供可靠的键值存储。可以用在系统的降级处理、服务的发现、配置的共享等多个方面。而Etcd的底层数据存储上与一个NoSQL的数据库基本没有差别,但更准确的说法说是一个高可用的键值存储系统,并且etcd提供了TTL和订阅与发布(Subscript/Public)功能。与一般的NoSQL数据库不同,Etcd在设计的初衷主要用于是共享配置和服务发现(比noSQL 开发了一些面向服务发现的接口,提供面向服务发现的逻辑和协议),它的灵感来自于ZooKeeper和Doozer。(Etcd是CoreOS生态系统中处于连接各个节点通信和支撑集群服务协同运作的核心地位的模)
etcd有如下的特点:
- 简单:安装配置简单,API丰富(支持http,jason),提供HTTP API进行交互,使用也很简单
- 安全:支持SSL证书验证
- 快速:根据官方提供的benchmark数据,单实例支持每秒2k+读操作
- 可靠:采用raft算法,实现分布式系统数据的可用性和一致性
通过http轮询,监听网络变化
etcd分布式一致性算法
etcd使用的分布式一致性算法是Raft协议:
https://www.bilibili.com/video/BV1yJ411P76f?from=search&seid=5674215994201741363
etcd应用场景
etcd应用场景有服务发现、消息发布与订阅、负载均衡、分布式通知与协调、分布式锁、分布式队列、集群监控与LEADER竞选,详细见文章末尾附录。
etcd应用比较多的应用场景是用于服务发现,服务发现(Service Discovery)要解决的是分布式系统中最常见的问题之一,即在同一个分布式集群中的进程或服务如何才能找到对方并建立连接。
从本质上说,服务发现就是要了解集群中是否有进程在监听upd或者tcp端口,并且通过名字就可以进行查找和链接。
要解决服务发现的问题,需要下面三大支柱,缺一不可。
- 一个强一致性、高可用的服务存储目录。
基于Ralf算法的etcd天生就是这样一个强一致性、高可用的服务存储目录。
- 一种注册服务和健康服务健康状况的机制。
用户可以在etcd中注册服务,并且对注册的服务配置key TTL,定时保持服务的心跳以达到监控健康状态的效果。
- 一种查找和连接服务的机制。
通过在etcd指定的主题下注册的服务也能在对应的主题下查找到。
(还可以在每个服务机器上都部署一个proxy模式的etcd,这样就可以确保访问etcd集群的服务都能够互相连接。)
链接:https://www.jianshu.com/p/f68028682192
etcd安装
etcd在生产环境中一般推荐集群方式部署。本文定位为入门,主要讲讲单节点安装和基本使用。
etcd目前默认使用2379端口提供HTTP API服务,2380端口和peer通信(这两个端口已经被IANA官方预留给etcd);在之前的版本中可能会分别使用4001和7001,在使用的过程中需要注意这个区别。
因为etcd是go语言编写的,所以设备上应该先部署goland环境:https://blog.csdn.net/bandaoyu/article/details/107721973
安装好goland环境后,安装只需要下载对应的二进制文件,并放到合适的路径就行。
下载软件包
$ wget https://github.com/coreos/etcd/releases/download/v3.1.5/etcd-v3.1.5-linux-amd64.tar.gz
$ tar xzvf etcd-v3.1.5-linux-amd64.tar.gz
$ mv etcd-v3.1.5-linux-amd64 /opt/etcd
解压后是一些文档和两个二进制文件etcd和etcdctl。etcd是server端,etcdctl是客户端。
$ ls /opt/etcd/etcd-v3.1.5-linux-amd64/
Documentation etcd etcdctl README-etcdctl.md README.md READMEv2-etcdctl.md
如果在测试环境,启动一个单节点的etcd服务,只需要运行etcd命令就行。
$ cd /opt/etcd/etcd-v3.1.5-linux-amd64/
$ ./etcd
输出:
$ ./etcd 2017-04-10 11:46:44.772465 I | etcdmain: etcd Version: 3.1.5 2017-04-10 11:46:44.772512 I | etcdmain: Git SHA: 20490ca 2017-04-10 11:46:44.772607 I | etcdmain: Go Version: go1.7.5 2017-04-10 11:46:44.772756 I | etcdmain: Go OS/Arch: linux/amd64 2017-04-10 11:46:44.772817 I | etcdmain: setting maximum number of CPUs to 2, total number of available CPUs is 2 2017-04-10 11:46:44.772851 W | etcdmain: no data-dir provided, using default data-dir ./default.etcd 2017-04-10 11:46:44.773298 I | embed: listening for peers on http://localhost:2380 2017-04-10 11:46:44.773583 I | embed: listening for client requests on localhost:2379 2017-04-10 11:46:44.775967 I | etcdserver: name = default 2017-04-10 11:46:44.775993 I | etcdserver: data dir = default.etcd 2017-04-10 11:46:44.776167 I | etcdserver: member dir = default.etcd/member 2017-04-10 11:46:44.776253 I | etcdserver: heartbeat = 100ms 2017-04-10 11:46:44.776264 I | etcdserver: election = 1000ms 2017-04-10 11:46:44.776270 I | etcdserver: snapshot count = 10000 2017-04-10 11:46:44.776285 I | etcdserver: advertise client URLs = http://localhost:2379 2017-04-10 11:46:44.776293 I | etcdserver: initial advertise peer URLs = http://localhost:2380 2017-04-10 11:46:44.776306 I | etcdserver: initial cluster = default=http://localhost:2380 2017-04-10 11:46:44.781171 I | etcdserver: starting member 8e9e05c52164694d in cluster cdf818194e3a8c32 2017-04-10 11:46:44.781323 I | raft: 8e9e05c52164694d became follower at term 0 2017-04-10 11:46:44.781351 I | raft: newRaft 8e9e05c52164694d [peers: [], term: 0, commit: 0, applied: 0, lastindex: 0, lastterm: 0] 2017-04-10 11:46:44.781883 I | raft: 8e9e05c52164694d became follower at term 1 2017-04-10 11:46:44.795542 I | etcdserver: starting server... [version: 3.1.5, cluster version: to_be_decided] 2017-04-10 11:46:44.796453 I | etcdserver/membership: added member 8e9e05c52164694d [http://localhost:2380] to cluster cdf818194e3a8c32 2017-04-10 11:46:45.083350 I | raft: 8e9e05c52164694d is starting a new election at term 1 2017-04-10 11:46:45.083494 I | raft: 8e9e05c52164694d became candidate at term 2 2017-04-10 11:46:45.083520 I | raft: 8e9e05c52164694d received MsgVoteResp from 8e9e05c52164694d at term 2 2017-04-10 11:46:45.083598 I | raft: 8e9e05c52164694d became leader at term 2 2017-04-10 11:46:45.083654 I | raft: raft.node: 8e9e05c52164694d elected leader 8e9e05c52164694d at term 2 2017-04-10 11:46:45.084544 I | etcdserver: published {Name:default ClientURLs:[http://localhost:2379]} to cluster cdf818194e3a8c32 2017-04-10 11:46:45.084638 I | etcdserver: setting up the initial cluster version to 3.1 2017-04-10 11:46:45.084857 I | embed: ready to serve client requests 2017-04-10 11:46:45.085918 E | etcdmain: forgot to set Type=notify in systemd service file? 2017-04-10 11:46:45.086668 N | embed: serving insecure client requests on 127.0.0.1:2379, this is strongly discouraged! 2017-04-10 11:46:45.087004 N | etcdserver/membership: set the initial cluster version to 3.1 2017-04-10 11:46:45.087195 I | etcdserver/api: enabled capabilities for version 3.1 |
从上面的输出中,我们可以看到很多信息。以下是几个比较重要的信息:
- name表示节点名称,默认为default。
- data-dir 保存日志和快照的目录,默认为当前工作目录default.etcd/目录下。
- 在http://localhost:2380和集群中其他节点通信。
- 在http://localhost:2379提供HTTP API服务,供客户端交互。
- heartbeat为100ms,该参数的作用是leader多久发送一次心跳到
- followers,默认值是100ms。
- election为1000ms,该参数的作用是重新投票的超时时间,如果follow在该+ 时间间隔没有收到心跳包,会触发重新投票,默认为1000ms。
- snapshot count为10000,该参数的作用是指定有多少事务被提交时,触发+ 截取快照保存到磁盘。
- 集群和每个节点都会生成一个uuid。
- 启动的时候会运行raft,选举出leader。
- 上面的方法只是简单的启动一个etcd服务,但要长期运行的话,还是做成一个sys服务好一些。下面将以systemd为例,介绍如何建立一个etcd服务。
创建systemd服务(使得可以用 systemctl start etcd 命令启动、控制etcd)
- 设定etcd配置文件
建立相关目录
$ mkdir -p /var/lib/etcd/
$ mkdir -p /opt/etcd/config/
创建etcd配置文件
$ cat <<EOF | sudo tee /opt/etcd/config/etcd.conf
#节点名称
ETCD_NAME=$(hostname -s)
#数据存放位置
ETCD_DATA_DIR=/var/lib/etcd
EOF
创建systemd配置文件
$ cat <<EOF | sudo tee /etc/systemd/system/etcd.service
[Unit]
Description=Etcd Server
Documentation=https://github.com/coreos/etcd
After=network.target
[Service]
User=root
Type=notify
EnvironmentFile=-/opt/etcd/config/etcd.conf
ExecStart=/opt/etcd/etcd
Restart=on-failure
RestartSec=10s
LimitNOFILE=40000
[Install]
WantedBy=multi-user.target
EOF
启动etcd
$ systemctl daemon-reload && systemctl enable etcd && systemctl start etcd
摘自:https://www.jianshu.com/p/f68028682192
验证安装成功
将etcd的路径加入到环境变量PATH中(这样就可以直接从命令行使用etcd)
#通过修改profile文件:
vim /etc/profile
在profile最后添加下面的语句:
export PATH=/opt/etcd:$PATH
再执行下面命令使得配置文件生效:
source /etc/profile
查看版本
$ etcd --version
etcd Version: 3.1.5
Git SHA: 20490ca
Go Version: go1.7.5
Go OS/Arch: linux/amd64
服务注册与发现实例(go语言)
etcd服务注册与发现工作过程简介:
etcd 是一个 分布式 的高一致性的 键值存储系统。我们每次网关后面加一个服务,只需要向etcd 注册 该服务(其实就是 存一个值)然后向etcd 发送心跳,当etcd 没有检测到心跳就会 把这个键值对 删了(这整个动作是etcd里的租约模式),网关那边 就只需要 watch 这个 key ,就能够知道 所有服务的所有动态了。
(etcd的 租约模式:客户端申请 一个租约 并设置 过期时间,每隔一段时间 就要 请求 etcd 申请续租。客户端可以通过租约存key。如果不续租 ,过期了,etcd 会删除这个租约上的 所有key-value。类似于心跳模式。)
系统中实现服务注册与发现所需的基本功能有:
- 服务注册:同一service的所有节点注册到相同目录下,节点启动后将自己的信息注册到所属服务的目录中。
- 健康检查:服务节点定时发送心跳,注册到服务目录中的信息设置一个较短的TTL,运行正常的服务节点每隔一段时间会去更新信息的TTL。
- 服务发现:通过名称能查询到服务提供外部访问的 IP 和端口号。比如网关代理服务时能够及时的发现服务中新增节点、丢弃不可用的服务节点,同时各个服务间也能感知对方的存在。
在分布式系统中,如何管理节点间的状态一直是一个难题,etcd 是由开发并维护的,它使用 Go 语言编写,并通过Raft 一致性算法处理日志复制以保证强一致性。etcd像是专门为集群环境的服务发现和注册而设计,它提供了数据 TTL 失效、数据改变监视、多值、目录监听、分布式锁原子操作等功能,可以方便的跟踪并管理集群节点的状态。
服务注册的简单实现Golang:
1、安装client库
依赖etcd的client库,所以需要先安装
go get -v github.com/coreos/etcd/clientv3
如果下载失败,则手动下载 https://github.com/etcd-io/etcd/archive/v3.1.5.zip,解压,取出clientv3,放到$GOPATH/src/github.com/coreos/etcd/目录下
2、实例代码
写两个 Demo 程序,一个服务充当service,一个客户端程序充当网关代理(gateway)。服务运行后会去etcd 以自己服务名命名的目录中注册服务节点,并定时续租(更新 TTL)。客户端(gateway)从 etcd查询服务目录中的节点信息代理服务的请求,并且会在协程中实时监控服务目录中的变化,维护到自己的服务节点信息列表中。
(更详细的过程说明:https://blog.csdn.net/wohu1104/article/details/108552649)
网上找了蛮多资料后,加上自己封装后的产物。开箱即用。
服务注册的简单实现:
注意:import clientv3的配置填写$GOPATH/src下的真实目录,比如我这里:"github.com/coreos/etcd/client/v3",参考:https://blog.csdn.net/wohu1104/article/details/108552649
package main
import (
"context"
"fmt"
"github.com/coreos/etcd/client/v3"
"time"
)
//创建租约注册服务
type ServiceReg struct {
client *clientv3.Client
lease clientv3.Lease
leaseResp *clientv3.LeaseGrantResponse
canclefunc func()
keepAliveChan <-chan *clientv3.LeaseKeepAliveResponse
key string
}
func NewServiceReg(addr []string, timeNum int64) (*ServiceReg, error) {
conf := clientv3.Config{
Endpoints: addr,
DialTimeout: 5 * time.Second,
}
var (
client *clientv3.Client
)
if clientTem, err := clientv3.New(conf); err == nil {
client = clientTem
} else {
return nil, err
}
ser := &ServiceReg{
client: client,
}
if err := ser.setLease(timeNum); err != nil {
return nil, err
}
go ser.ListenLeaseRespChan()
return ser, nil
}
//设置租约
func (this *ServiceReg) setLease(timeNum int64) error {
lease := clientv3.NewLease(this.client)
//设置租约时间
leaseResp, err := lease.Grant(context.TODO(), timeNum)
if err != nil {
return err
}
//设置续租
ctx, cancelFunc := context.WithCancel(context.TODO())
leaseRespChan, err := lease.KeepAlive(ctx, leaseResp.ID)
if err != nil {
return err
}
this.lease = lease
this.leaseResp = leaseResp
this.canclefunc = cancelFunc
this.keepAliveChan = leaseRespChan
return nil
}
//监听 续租情况
func (this *ServiceReg) ListenLeaseRespChan() {
for {
select {
case leaseKeepResp := <-this.keepAliveChan:
if leaseKeepResp == nil {
fmt.Printf("已经关闭续租功能\n")
return
} else {
fmt.Printf("续租成功\n")
}
}
}
}
//通过租约 注册服务
func (this *ServiceReg) PutService(key, val string) error {
kv := clientv3.NewKV(this.client)
_, err := kv.Put(context.TODO(), key, val, clientv3.WithLease(this.leaseResp.ID))
return err
}
//撤销租约
func (this *ServiceReg) RevokeLease() error {
this.canclefunc()
time.Sleep(2 * time.Second)
_, err := this.lease.Revoke(context.TODO(), this.leaseResp.ID)
return err
}
func main() {
ser,_ := NewServiceReg([]string{"127.0.0.1:2379"},5)
ser.PutService("/node/111","heiheihei")
select{}
}
那就让我来简单的解释一下吧。
- etcd的 租约模式:客户端申请 一个租约 并设置 过期时间,每隔一段时间 就要 请求 etcd 申请续租。客户端可以通过租约存key。如果不续租 ,过期了,etcd 会删除这个租约上的 所有key-value。类似于心跳模式。
- 一般相同的服务存的 key 的前缀是一样的 比如 “server/001"=> "127.0.0.1:1212" 和 ”server/002"=>"127.0.0.1:1313" 这种模式,然后 客户端 就直接 匹配 “server/” 这个key。
- 具体代码不复杂。要是看不懂就留言问我吧。
编译:
go build gotest.go
服务发现的简单实现:
import (
"go.etcd.io/etcd/clientv3"
"time"
"context"
"go.etcd.io/etcd/mvcc/mvccpb"
"sync"
"log"
)
type ClientDis struct {
client *clientv3.Client
serverList map[string]string
lock sync.Mutex
}
func NewClientDis (addr []string)( *ClientDis, error){
conf := clientv3.Config{
Endpoints: addr,
DialTimeout: 5 * time.Second,
}
if client, err := clientv3.New(conf); err == nil {
return &ClientDis{
client:client,
serverList:make(map[string]string),
}, nil
} else {
return nil ,err
}
}
func (this * ClientDis) GetService(prefix string) ([]string ,error){
resp, err := this.client.Get(context.Background(), prefix, clientv3.WithPrefix())
if err != nil {
return nil, err
}
addrs := this.extractAddrs(resp)
go this.watcher(prefix)
return addrs ,nil
}
func (this *ClientDis) watcher(prefix string) {
rch := this.client.Watch(context.Background(), prefix, clientv3.WithPrefix())
for wresp := range rch {
for _, ev := range wresp.Events {
switch ev.Type {
case mvccpb.PUT:
this.SetServiceList(string(ev.Kv.Key),string(ev.Kv.Value))
case mvccpb.DELETE:
this.DelServiceList(string(ev.Kv.Key))
}
}
}
}
func (this *ClientDis) extractAddrs(resp *clientv3.GetResponse) []string {
addrs := make([]string,0)
if resp == nil || resp.Kvs == nil {
return addrs
}
for i := range resp.Kvs {
if v := resp.Kvs[i].Value; v != nil {
this.SetServiceList(string(resp.Kvs[i].Key),string(resp.Kvs[i].Value))
addrs = append(addrs, string(v))
}
}
return addrs
}
func (this *ClientDis) SetServiceList(key,val string) {
this.lock.Lock()
defer this.lock.Unlock()
this.serverList[key] = string(val)
log.Println("set data key :",key,"val:",val)
}
func (this *ClientDis) DelServiceList(key string) {
this.lock.Lock()
defer this.lock.Unlock()
delete(this.serverList,key)
log.Println("del data key:", key)
}
func (this *ClientDis) SerList2Array()[]string {
this.lock.Lock()
defer this.lock.Unlock()
addrs := make([]string,0)
for _, v := range this.serverList {
addrs = append(addrs,v)
}
return addrs
}
func main () {
cli,_ := NewClientDis([]string{"127.0.0.1:2379"})
cli.GetService("/node")
select {}
}
1.创建一个client 连到etcd。
2.匹配到所有相同前缀的 key。把值存到 serverList 这个map里面。
3 watch这个 key前缀,当有增加或者删除的时候 就 修改这个map。
4所以这个map就是 实时的 服务列表
总结
以上就是基于 etcd的服务注册与发现了,大家可以 自己动手试试,载一个 etcd ,然后分别运行这两个文件。就能看到效果了。 代码我也传到github上面了 https://github.com/mistaker/etcdTool 。
作者:xyt001
链接:https://www.jianshu.com/p/7c0d23c818a5
其他参考:https://www.jianshu.com/p/9bd1ab83b220
都是key-value存储,redis 可以代替 etcd吗?
理论上只要是你实现了服务注册与发现的接口 ,存储层是可以替换的.用 redis/mysql 也不是不可以~
选 etcd 这种类型只因它更适合做这个。
原因如下:
1、redis 主从是异步复制的机制,这就导致了其有丢失数据的风险。
2、分布式系统最重要的就是一致性协议,redis 是不支持的。如果发生脑裂,可能两个微服务都会声称自己对某段 IP 的请求负责。
总之,etcd和redis虽然存储层的结构一致,但是为各自业务开发了对应的接口和协议,etcd面向服务发现设计了接口和协议,所以etcd比redis更适合服务发现。redis要用于服务发现,需要用户做一些封装和适配。
参考:https://www.v2ex.com/t/520367?p=1
为什么选择Etcd而不选择Zookeeper
etcd可实现的功能,Zookeeper都能实现,那么为什么要用etcd而非直接使用Zookeeper呢?相较之下,Zookeeper有如下缺点:
1.复杂。Zookeeper的部署维护复杂,管理员需要掌握一系列的知识和技能;而Paxos强一致性算法也是素来以复杂难懂而闻名于世;另外,Zookeeper的使用也比较复杂,需要安装客户端,官方只提供了java和C两种语言的接口。
2.Java编写。这里不是对Java有偏见,而是Java本身就偏向于重型应用,它会引入大量的依赖。而运维人员则普遍希望机器集群尽可能简单,维护起来也不易出错。
3.发展缓慢。Apache基金会项目特有的“Apache Way”在开源界饱受争议,其中一大原因就是由于基金会庞大的结构以及松散的管理导致项目发展缓慢。
etcd的优点:
1.简单。使用Go语言编写部署简单;使用HTTP作为接口使用简单;使用Raft算法保证强一致性让用户易于理解。
2.数据持久化。etcd默认数据一更新就进行持久化。
3.安全。etcd支持SSL客户端安全认证。
最后,etcd作为一个年轻的项目,正在高速迭代和开发中,这既是一个优点,也是一个缺点。优点在于它的未来具有无限的可能性,缺点是版本的迭代导致其使用的可靠性无法保证,无法得到大项目长时间使用的检验。然而,目前CoreOS、Kubernetes和Cloudfoundry等知名项目均在生产环境中使用了etcd,所以总的来说,etcd值得你去尝试。
链接:https://www.jianshu.com/p/d63265949e52
附录
附录1:etcd基本使用(数据库CURD和持久化等)
etcdctl是一个命令行客户端,它能提供一些简洁的命令,供用户直接跟etcd服务打交道,而无需基于 HTTP API方式。可以方便我们在对服务进行测试或者手动修改数据库内容。建议刚刚接触etcd时通过etdctl来熟悉相关操作。这些操作跟HTTP API基本上是对应的。
etcd项目二进制发行包中已经包含了etcdctl工具,etcdctl支持的命令大体上分为数据库操作和非数据库操作两类。
查看版本
$ etcd --version
etcd Version: 3.1.5
Git SHA: 20490ca
Go Version: go1.7.5
Go OS/Arch: linux/amd64
查看帮助:
$ etcdctl -h
NAME:
etcdctl - A simple command line client for etcd.
USAGE:
etcdctl [global options] command [command options] [arguments...]
VERSION:
3.1.5
COMMANDS:
backup backup an etcd directory
cluster-health check the health of the etcd cluster
mk make a new key with a given value
mkdir make a new directory
rm remove a key or a directory
rmdir removes the key if it is an empty directory or a key-value pair
get retrieve the value of a key
ls retrieve a directory
set set the value of a key
setdir create a new directory or update an existing directory TTL
update update an existing key with a given value
updatedir update an existing directory
watch watch a key for changes
exec-watch watch a key for changes and exec an executable
member member add, remove and list subcommands
user user add, grant and revoke subcommands
role role add, grant and revoke subcommands
auth overall auth controls
help, h Shows a list of commands or help for one command
GLOBAL OPTIONS:
--debug output cURL commands which can be used to reproduce the request
--no-sync don't synchronize cluster information before sending request
--output simple, -o simple output response in the given format (simple, `extended` or `json`) (default: "simple")
--discovery-srv value, -D value domain name to query for SRV records describing cluster endpoints
--insecure-discovery accept insecure SRV records describing cluster endpoints
--peers value, -C value DEPRECATED - "--endpoints" should be used instead
--endpoint value DEPRECATED - "--endpoints" should be used instead
--endpoints value a comma-delimited list of machine addresses in the cluster (default: "http://127.0.0.1:2379,http://127.0.0.1:4001")
--cert-file value identify HTTPS client using this SSL certificate file
--key-file value identify HTTPS client using this SSL key file
--ca-file value verify certificates of HTTPS-enabled servers using this CA bundle
--username value, -u value provide username[:password] and prompt if password is not supplied.
--timeout value connection timeout per request (default: 2s)
--total-timeout value timeout for the command execution (except watch) (default: 5s)
--help, -h show help
--version, -v print the version
常用命令选项:
--debug 输出CURL命令,显示执行命令的时候发起的请求
--no-sync 发出请求之前不同步集群信息
--output, -o 'simple' 输出内容的格式(simple 为原始信息,json 为进行json格式解码,易读性好一些)
--peers, -C 指定集群中的同伴信息,用逗号隔开(默认为: "127.0.0.1:4001")
--cert-file HTTPS下客户端使用的SSL证书文件
--key-file HTTPS下客户端使用的SSL密钥文件
--ca-file 服务端使用HTTPS时,使用CA文件进行验证
--help, -h 显示帮助命令信息
--version, -v 打印版本信息
数据库操作
数据库操作围绕对键值和目录的CRUD完整生命周期的管理。
etcd在键的组织上采用了层次化的空间结构(类似于文件系统中目录的概念),用户指定的键可以为单独的名字,如:testkey,此时实际上放在根目录/下面,也可以为指定目录结构,如/cluster1/node2/testkey,则将创建相应的目录结构。
注:CRUD即Create,Read,Update,Delete是符合REST风格的一套API操作。
- set
指定某个键的值。例如:
$ etcdctl set /testdir/testkey "Hello world"
Hello world
支持的选项包括:
--ttl '0' 该键值的超时时间(单位为秒),不配置(默认为0)则永不超时
--swap-with-value value 若该键现在的值是value,则进行设置操作
--swap-with-index '0' 若该键现在的索引值是指定索引,则进行设置操作
- get
获取指定键的值。例如:
$ etcdctl get /testdir/testkey
Hello world
当键不存在时,则会报错。例如:
$ etcdctl get /testdir/testkey2
Error: 100: Key not found (/testdir/testkey2) [5]
支持的选项为:
--sort 对结果进行排序
--consistent 将请求发给主节点,保证获取内容的一致性。
- update
当键存在时,更新值内容。例如:
$ etcdctl update /testdir/testkey "Hello"
Hello
当键不存在时,则会报错。例如:
$ etcdctl update /testdir/testkey2 "Hello"
Error: 100: Key not found (/testdir/testkey2) [6]
支持的选项为:
--ttl '0' 超时时间(单位为秒),不配置(默认为 0)则永不超时。
- rm
删除某个键值。例如:
$ etcdctl rm /testdir/testkey
PrevNode.Value: Hello
当键不存在时,则会报错。例如:
$ etcdctl rm /testdir/testkey
Error: 100: Key not found (/testdir/testkey) [7]
支持的选项为:
--dir 如果键是个空目录或者键值对则删除
--recursive 删除目录和所有子键
--with-value 检查现有的值是否匹配
--with-index '0'检查现有的index是否匹配
- mk
如果给定的键不存在,则创建一个新的键值。例如:
$ etcdctl mk /testdir/testkey "Hello world"
Hello world
当键存在的时候,执行该命令会报错,例如:
$ etcdctl mk /testdir/testkey "Hello world"
Error: 105: Key already exists (/testdir/testkey) [8]
支持的选项为:
--ttl '0' 超时时间(单位为秒),不配置(默认为 0)。则永不超时
- mkdir
如果给定的键目录不存在,则创建一个新的键目录。例如:
$ etcdctl mkdir testdir2
当键目录存在的时候,执行该命令会报错,例如:
$ etcdctl mkdir testdir2
Error: 105: Key already exists (/testdir2) [9]
支持的选项为:
--ttl '0' 超时时间(单位为秒),不配置(默认为0)则永不超时。
- setdir
创建一个键目录。如果目录不存在就创建,如果目录存在更新目录TTL。
$ etcdctl setdir testdir3
支持的选项为:
--ttl '0' 超时时间(单位为秒),不配置(默认为0)则永不超时。
- updatedir
更新一个已经存在的目录。
$ etcdctl updatedir testdir2
支持的选项为:
--ttl '0' 超时时间(单位为秒),不配置(默认为0)则永不超时。
- rmdir
删除一个空目录,或者键值对。
$ etcdctl setdir dir1
$ etcdctl rmdir dir1
若目录不空,会报错:
$ etcdctl set /dir/testkey hi
hi
$ etcdctl rmdir /dir
Error: 108: Directory not empty (/dir) [17]
- ls
列出目录(默认为根目录)下的键或者子目录,默认不显示子目录中内容。
例如:
$ etcdctl ls
/testdir
/testdir2
/dir
$ etcdctl ls dir
/dir/testkey
支持的选项包括:
--sort 将输出结果排序
--recursive 如果目录下有子目录,则递归输出其中的内容
-p 对于输出为目录,在最后添加/进行区分
非数据库操作
- backup
备份etcd的数据。
$ etcdctl backup --data-dir /var/lib/etcd --backup-dir /home/etcd_backup
支持的选项包括:
--data-dir etcd的数据目录
--backup-dir 备份到指定路径
- watch
监测一个键值的变化,一旦键值发生更新,就会输出最新的值并退出。
例如:用户更新testkey键值为Hello watch。
$ etcdctl get /testdir/testkey
Hello world
$ etcdctl set /testdir/testkey "Hello watch"
Hello watch
$ etcdctl watch testdir/testkey
Hello watch
支持的选项包括:
--forever 一直监测直到用户按CTRL+C退出
--after-index '0' 在指定index之前一直监测
--recursive 返回所有的键值和子键值
- exec-watch
监测一个键值的变化,一旦键值发生更新,就执行给定命令。
例如:用户更新testkey键值。
$ etcdctl exec-watch testdir/testkey -- sh -c 'ls'
config Documentation etcd etcdctl README-etcdctl.md README.md READMEv2-etcdctl.md
支持的选项包括:
--after-index '0' 在指定 index 之前一直监测
--recursive 返回所有的键值和子键值
- member
通过list
、add
、remove
命令列出、添加、删除etcd实例到etcd集群中。
查看集群中存在的节点
$ etcdctl member list
8e9e05c52164694d: name=dev-master-01 peerURLs=http://localhost:2380 clientURLs=http://localhost:2379 isLeader=true
删除集群中存在的节点
$ etcdctl member remove 8e9e05c52164694d
Removed member 8e9e05c52164694d from cluster
向集群中新加节点
$ etcdctl member add etcd3 http://192.168.1.100:2380
Added member named etcd3 with ID 8e9e05c52164694d to cluster
摘自:链接:https://www.jianshu.com/p/f68028682192
附录2:etcd应用场景
5.1服务发现
服务发现(Service Discovery)要解决的是分布式系统中最常见的问题之一,即在同一个分布式集群中的进程或服务如何才能找到对方并建立连接。从本质上说,服务发现就是想要了解集群中是否有进程在监听udp或tcp端口,并且通过名字就可以进行查找和连接。要解决服务发现的问题,需要有下面三大支柱,缺一不可。
·一个强一致性、高可用的服务存储目录。基于Raft算法的etcd天生就是这样一个强一致性高可用的服务存储目录。
·一种注册服务和监控服务健康状态的机制。用户可以在etcd中注册服务,并且对注册的服务设置key TTL,定时保持服务的心跳以达到监控健康状态的效果。
·一种查找和连接服务的机制。通过在etcd指定的主题下注册的服务也能在对应的主题下查找到。为了确保连接,我们可以在每个服务机器上都部署一个proxy模式的etcd,这样就可以确保能访问etcd集群的服务都能互相连接。
图3服务发现图
5.2消息发布与订阅
在分布式系统中,最为适用的组件间通信方式是消息发布与订阅机制。具体而言,即构建一个配置共享中心,数据提供者在这个配置中心发布消息,而消息使用者则订阅他们关心的主题,一旦相关主题有消息发布,就会实时通知订阅者。通过这种方式可以实现分布式系统配置的集中式管理与实时动态更新。
·应用中用到的一些配置信息存放在etcd上进行集中管理。这类场景的使用方式通常是这样的:应用在启动的时候主动从etcd获取一次配置信息,同时,在etcd节点上注册一个Watcher并等待,以后每次配置有更新的时候,etcd都会实时通知订阅者,以此达到获取最新配置信息的目的。
·分布式搜索服务中,索引的元信息和服务器集群机器的节点状态信息存放在etcd中,供各个客户端订阅使用。使用etcd的key TTL功能可以确保机器状态是实时更新的。
·分布式日志收集系统。这个系统的核心工作是收集分布在不同机器上的日志。收集器通常按照应用(或主题)来分配收集任务单元,因此可以在etcd上创建一个以应用(或主题)命名的目录P,并将这个应用(或主题)相关的所有机器ip,以子目录的形式存储在目录P下,然后设置一个递归的etcd Watcher,递归式地监控应用(或主题)目录下所有信息的变动。这样就实现了在机器IP(消息)发生变动时,能够实时通知收集器调整任务分配。
·系统中信息需要动态自动获取与人工干预修改信息请求内容的情况。通常的解决方案是对外暴露接口,例如JMX接口,来获取一些运行时的信息或提交修改的请求。而引入etcd之后,只需要将这些信息存放到指定的etcd目录中,即可通过HTTP接口直接被外部访问。
图4消息发布与订阅图
5.3负载均衡
在场景一中也提到了负载均衡,本文提及的负载均衡均指软负载均衡。在分布式系统中,为了保证服务的高可用以及数据的一致性,通常都会把数据和服务部署多份,以此达到对等服务,即使其中的某一个服务失效了,也不影响使用。这样的实现虽然会导致一定程度上数据写入性能的下降,但是却能实现数据访问时的负载均衡。因为每个对等服务节点上都存有完整的数据,所以用户的访问流量就可以分流到不同的机器上。
·etcd本身分布式架构存储的信息访问支持负载均衡。etcd集群化以后,每个etcd的核心节点都可以处理用户的请求。所以,把数据量小但是访问频繁的消息数据直接存储到etcd中也是个不错的选择,如业务系统中常用的二级代码表。二级代码表的工作过程一般是这样,在表中存储代码,在etcd中存储代码所代表的具体含义,业务系统调用查表的过程,就需要查找表中代码的含义。所以如果把二级代码表中的小量数据存储到etcd中,不仅方便修改,也易于大量访问。
·利用etcd维护一个负载均衡节点表。etcd可以监控一个集群中多个节点的状态,当有一个请求发过来后,可以轮询式地把请求转发给存活着的多个节点。类似KafkaMQ,通过Zookeeper来维护生产者和消费者的负载均衡。同样也可以用etcd来做Zookeeper的工作。
图5负载平衡图
5.4分布式通知与协调
这里讨论的分布式通知与协调,与消息发布和订阅有些相似。两者都使用了etcd中的Watcher机制,通过注册与异步通知机制,实现分布式环境下不同系统之间的通知与协调,从而对数据变更进行实时处理。实现方式通常为:不同系统都在etcd上对同一个目录进行注册,同时设置Watcher监控该目录的变化(如果对子目录的变化也有需要,可以设置成递归模式),当某个系统更新了etcd的目录,那么设置了Watcher的系统就会收到通知,并作出相应处理。
·通过etcd进行低耦合的心跳检测。检测系统和被检测系统通过etcd上某个目录关联而非直接关联起来,这样可以大大减少系统的耦合性。
·通过etcd完成系统调度。某系统有控制台和推送系统两部分组成,控制台的职责是控制推送系统进行相应的推送工作。管理人员在控制台做的一些操作,实际上只需要修改etcd上某些目录节点的状态,而etcd就会自动把这些变化通知给注册了Watcher的推送系统客户端,推送系统再做出相应的推送任务。
·通过etcd完成工作汇报。大部分类似的任务分发系统,子任务启动后,到etcd来注册一个临时工作目录,并且定时将自己的进度进行汇报(将进度写入到这个临时目录),这样任务管理者就能够实时知道任务进度。
图6分布式通知与协调图
5.5分布式锁
因为etcd使用Raft算法保持了数据的强一致性,某次操作存储到集群中的值必然是全局一致的,所以很容易实现分布式锁。锁服务有两种使用方式,一是保持独占,二是控制时序。
·保持独占,即所有试图获取锁的用户最终只有一个可以得到。etcd为此提供了一套实现分布式锁原子操作CAS(CompareAndSwap)的API。通过设置prevExist值,可以保证在多个节点同时创建某个目录时,只有一个成功,而该用户即可认为是获得了锁。
·控制时序,即所有试图获取锁的用户都会进入等待队列,获得锁的顺序是全局唯一的,同时决定了队列执行顺序。etcd为此也提供了一套API(自动创建有序键),对一个目录建值时指定为POST动作,这样etcd会自动在目录下生成一个当前最大的值为键,存储这个新的值(客户端编号)。同时还可以使用API按顺序列出所有当前目录下的键值。此时这些键的值就是客户端的时序,而这些键中存储的值可以是代表客户端的编号。
图7分布式锁图
5.6分布式队列
分布式队列的常规用法与场景五中所描述的分布式锁的控制时序用法类似,即创建一个先进先出的队列,保证顺序。
另一种比较有意思的实现是在保证队列达到某个条件时再统一按顺序执行。这种方法的实现可以在/queue这个目录中另外建立一个/queue/condition节点。
·condition可以表示队列大小。比如一个大的任务需要很多小任务就绪的情况下才能执行,每次有一个小任务就绪,就给这个condition数字加1,直到达到大任务规定的数字,再开始执行队列里的一系列小任务,最终执行大任务。
·condition可以表示某个任务在不在队列。这个任务可以是所有排序任务的首个执行程序,也可以是拓扑结构中没有依赖的点。通常,必须执行这些任务后才能执行队列中的其他任务。
·condition还可以表示其它的一类开始执行任务的通知。可以由控制程序指定,当condition出现变化时,开始执行队列任务。
图8分布式队列图
5.7集群监控与LEADER竞选
通过etcd来进行监控实现起来非常简单并且实时性强,用到了以下两点特性。
1.前面几个场景已经提到Watcher机制,当某个节点消失或有变动时,Watcher会第一时间发现并告知用户。
2.节点可以设置TTL key,比如每隔30s向etcd发送一次心跳使代表该节点仍然存活,否则说明节点消失。
这样就可以第一时间检测到各节点的健康状态,以完成集群的监控要求。
另外,使用分布式锁,可以完成Leader竞选。对于一些长时间CPU计算或者使用IO操作,只需要由竞选出的Leader计算或处理一次,再把结果复制给其他Follower即可,从而避免重复劳动,节省计算资源。
Leader应用的经典场景是在搜索系统中建立全量索引。如果每个机器分别进行索引的建立,不但耗时,而且不能保证索引的一致性。通过在etcd的CAS机制竞选Leader,由Leader进行索引计算,再将计算结果分发到其它节点。
图9集群监控与Leader竞选图
作者:Jay_Guo
链接:https://www.jianshu.com/p/d63265949e52
来源:简书
附录3:ETCD 词汇表
Raft:etcd所采用的保证分布式系统强一致性的算法。
Node:一个Raft状态机实例。
Member: 一个etcd实例。它管理着一个Node,并且可以为客户端请求提供服务。
Cluster:由多个Member构成可以协同工作的etcd集群。
Peer:对同一个etcd集群中另外一个Member的称呼。
Client: 向etcd集群发送HTTP请求的客户端。
WAL:预写式日志,etcd用于持久化存储的日志格式。
snapshot:etcd防止WAL文件过多而设置的快照,存储etcd数据状态。
Proxy:etcd的一种模式,为etcd集群提供反向代理服务。
Leader:Raft算法中通过竞选而产生的处理所有数据提交的节点。
Follower:竞选失败的节点作为Raft中的从属节点,为算法提供强一致性保证。
Candidate:当Follower超过一定时间接收不到Leader的心跳时转变为Candidate开始Leader竞选。
Term:某个节点成为Leader到下一次竞选开始的时间周期,称为一个Term。
Index:数据项编号。Raft中通过Term和Index来定位数据。
作者:向永清_数字经济
链接:https://www.jianshu.com/p/3bd041807974
来源:简书