请选择 进入手机版 | 继续访问电脑版

[Golang] Go操作etcd的实现示例

[复制链接]
查看115 | 回复10 | 2021-9-15 02:43:09 | 显示全部楼层 |阅读模式
目次

etcd是近几年比较火热的一个开源的、分布式的键值对数据存储体系 ,提供共享设置 、服务的注册和发现,本文告急 先容 etcd的安装和利用 。

etcdetcd先容

etcd是利用 Go语言开辟 的一个开源的、高可用的分布式key-value存储体系 ,可以用于设置 共享和服务的注册和发现。

雷同 项目有zookeeper和consul。

etcd具有以下特点:

  • 完全复制:集群中的每个节点都可以利用 完备 的存档
  • 高可用性:Etcd可用于避免硬件的单点故障或网络标题
  • 同等 性:每次读取都会返回跨多主机的最新写入
  • 简单:包括一个定义良好 、面向用户的API(gRPC)
  • 安全:实现了带有可选的客户端证书身份验证的主动 化TLS
  • 快速:每秒10000次写入的基准速率
  • 可靠:利用 Raft算法实现了强同等 、高可用的服务存储目次

etcd应用场景

服务发现

服务发现要办理 的也是分布式体系 中最常见的标题 之一,即在同一个分布式集群中的历程 或服务,要怎样 才能找到对方并建立毗连 。本质上来说,服务发现就是想要相识 集群中是否有历程 在监听 udp 或 tcp 端口,并且通过名字就可以查找和毗连 。

Go操作etcd的实现示例

设置 中心

将一些设置 信息放到 etcd 上举行 集中管理。

这类场景的利用 方式通常是如许 :应用在启动的时间 主动 从 etcd 获取一次设置 信息,同时,在 etcd 节点上注册一个 Watcher 并等待,以后每次设置 有更新的时间 ,etcd 都会及时 关照 订阅者,以此达到获取最新设置 信息的目的 。

分布式锁

由于 etcd 利用 Raft 算法保持了数据的强同等 性,某次操作存储到集群中的值必然是全局同等 的,以是 很轻易 实现分布式锁。锁服务有两种利用 方式,一是保持独占,二是控制时序。

保持独占即全部 获取锁的用户终极 只有一个可以得到。etcd 为此提供了一套实现分布式锁原子操作 CAS(

  1. CompareAndSwap
复制代码
)的 API。通过设置
  1. prevExist
复制代码
值,可以保证在多个节点同时去创建某个目次 时,只有一个成功。而创建成功的用户就可以以为 是获得了锁。

控制时序,即全部 想要获得锁的用户都会被安排实行 ,但是获得锁的次序 也是全局唯一的,同时决定了实行 次序 。etcd 为此也提供了一套 API(主动 创建有序键),对一个目次 建值时指定为

  1. POST
复制代码
动作,如许 etcd 会主动 在目次 下天生 一个当前最大的值为键,存储这个新的值(客户端编号)。同时还可以利用 API 按次序 列出全部 当前目次 下的键值。此时这些键的值就是客户端的时序,而这些键中存储的值可以是代表客户端的编号。

Go操作etcd的实现示例

为什么用 etcd 而不用ZooKeeper?

etcd 实现的这些功能,ZooKeeper都能实现。那么为什么要用 etcd 而非直接利用 ZooKeeper呢?

为什么不选择ZooKeeper?

  • 部署维护复杂,其利用 的
    1. Paxos
    复制代码
    强同等 性算法复杂难懂 。官方只提供了
    1. Java
    复制代码
    1. C
    复制代码
    两种语言的接口。
  • 利用
    1. Java
    复制代码
    编写引入大量的依赖 。运维职员 维护起来比较贫苦 。
  • 迩来 几年发展缓慢,不如
    1. etcd
    复制代码
    1. consul
    复制代码
    等后起之秀。

为什么选择etcd?

  • 简单。利用 Go 语言编写部署简单;支持HTTP/JSON API,利用 简单;利用 Raft 算法保证强同等 性让用户易于明白 。
  • etcd 默认数据一更新就举行 持久化。
  • etcd 支持 SSL 客户端安全认证。

末了 ,etcd 作为一个年轻的项目,正在高速迭代和开辟 中,这既是一个长处 ,也是一个缺点。长处 是它的将来 具有无穷 的大概 性,缺点是无法得到大项目长时间利用 的检验。然而,如今

  1. CoreOS
复制代码
  1. Kubernetes
复制代码
  1. CloudFoundry
复制代码
等着名 项目均在生产环境中利用 了
  1. etcd
复制代码
,以是 总的来说,etcd值得你去尝试。

etcd集群

etcd 作为一个高可用键值存储体系 ,天生就是为集群化而计划 的。由于 Raft 算法在做决议 时必要 多数节点的投票,以是 etcd 一样寻常 部署集群保举 奇数个节点,保举 的数量 为 3、5 或者 7 个节点构成一个集群。

搭建一个3节点集群示例:

在每个etcd节点指定集群成员,为了区分不同的集群最好同时设置 一个独一无二的token。

下面是提前定义好的集群信息,此中

  1. n1
复制代码
  1. n2
复制代码
  1. n3
复制代码
表示3个不同的etcd节点。

  1. TOKEN=token-01
  2. CLUSTER_STATE=new
  3. CLUSTER=n1=http://10.240.0.17:2380,n2=http://10.240.0.18:2380,n3=http://10.240.0.19:2380
复制代码

  1. n1
复制代码
这台机器上实行 以下下令 来启动etcd:

  1. etcd --data-dir=data.etcd --name n1 \
  2. --initial-advertise-peer-urls http://10.240.0.17:2380 --listen-peer-urls http://10.240.0.17:2380 \
  3. --advertise-client-urls http://10.240.0.17:2379 --listen-client-urls http://10.240.0.17:2379 \
  4. --initial-cluster ${CLUSTER} \
  5. --initial-cluster-state ${CLUSTER_STATE} --initial-cluster-token ${TOKEN}
复制代码

  1. n2
复制代码
这台机器上实行 以下下令 启动etcd:

  1. etcd --data-dir=data.etcd --name n2 \
  2. --initial-advertise-peer-urls http://10.240.0.18:2380 --listen-peer-urls http://10.240.0.18:2380 \
  3. --advertise-client-urls http://10.240.0.18:2379 --listen-client-urls http://10.240.0.18:2379 \
  4. --initial-cluster ${CLUSTER} \
  5. --initial-cluster-state ${CLUSTER_STATE} --initial-cluster-token ${TOKEN}
复制代码

  1. n3
复制代码
这台机器上实行 以下下令 启动etcd:

  1. etcd --data-dir=data.etcd --name n3 \
  2. --initial-advertise-peer-urls http://10.240.0.19:2380 --listen-peer-urls http://10.240.0.19:2380 \
  3. --advertise-client-urls http://10.240.0.19:2379 --listen-client-urls http://10.240.0.19:2379 \
  4. --initial-cluster ${CLUSTER} \
  5. --initial-cluster-state ${CLUSTER_STATE} --initial-cluster-token ${TOKEN}
复制代码

etcd 官网提供了一个可以公网访问的 etcd 存储地址。你可以通过如下下令 得到 etcd 服务的目次 ,并把它作为

  1. -discovery
复制代码
参数利用 。

  1. curl https://discovery.etcd.io/new?size=3
  2. https://discovery.etcd.io/a81b5818e67a6ea83e9d4daea5ecbc92
  3. # grab this token
  4. TOKEN=token-01
  5. CLUSTER_STATE=new
  6. DISCOVERY=https://discovery.etcd.io/a81b5818e67a6ea83e9d4daea5ecbc92
  7. etcd --data-dir=data.etcd --name n1 \
  8. --initial-advertise-peer-urls http://10.240.0.17:2380 --listen-peer-urls http://10.240.0.17:2380 \
  9. --advertise-client-urls http://10.240.0.17:2379 --listen-client-urls http://10.240.0.17:2379 \
  10. --discovery ${DISCOVERY} \
  11. --initial-cluster-state ${CLUSTER_STATE} --initial-cluster-token ${TOKEN}
  12. etcd --data-dir=data.etcd --name n2 \
  13. --initial-advertise-peer-urls http://10.240.0.18:2380 --listen-peer-urls http://10.240.0.18:2380 \
  14. --advertise-client-urls http://10.240.0.18:2379 --listen-client-urls http://10.240.0.18:2379 \
  15. --discovery ${DISCOVERY} \
  16. --initial-cluster-state ${CLUSTER_STATE} --initial-cluster-token ${TOKEN}
  17. etcd --data-dir=data.etcd --name n3 \
  18. --initial-advertise-peer-urls http://10.240.0.19:2380 --listen-peer-urls http://10.240.0.19:2380 \
  19. --advertise-client-urls http://10.240.0.19:2379 --listen-client-urls http:/10.240.0.19:2379 \
  20. --discovery ${DISCOVERY} \
  21. --initial-cluster-state ${CLUSTER_STATE} --initial-cluster-token ${TOKEN}
复制代码

到此etcd集群就搭建起来了,可以利用

  1. etcdctl
复制代码
来毗连 etcd。

  1. export ETCDCTL_API=3
  2. HOST_1=10.240.0.17
  3. HOST_2=10.240.0.18
  4. HOST_3=10.240.0.19
  5. ENDPOINTS=$HOST_1:2379,$HOST_2:2379,$HOST_3:2379
  6. etcdctl --endpoints=$ENDPOINTS member lis
复制代码

Go语言操作etcd

这里利用 官方的etcd/clientv3包来毗连 etcd并举行 干系 操作。

安装

  1. go get go.etcd.io/etcd/clientv3
复制代码

put和get操作

  1. put
复制代码
下令 用来设置键值对数据,
  1. get
复制代码
下令 用来根据key获取值。

  1. package main
  2. import (
  3. "context"
  4. "fmt"
  5. "time"
  6. "go.etcd.io/etcd/clientv3"
  7. )
  8. // etcd client put/get demo
  9. // use etcd/clientv3
  10. func main() {
  11. cli, err := clientv3.New(clientv3.Config{
  12. Endpoints: []string{"127.0.0.1:2379"},
  13. DialTimeout: 5 * time.Second,
  14. })
  15. if err != nil {
  16. // handle error!
  17. fmt.Printf("connect to etcd failed, err:%v\n", err)
  18. return
  19. }
  20. fmt.Println("connect to etcd success")
  21. defer cli.Close()
  22. // put
  23. ctx, cancel := context.WithTimeout(context.Background(), time.Second)
  24. _, err = cli.Put(ctx, "q1mi", "dsb")
  25. cancel()
  26. if err != nil {
  27. fmt.Printf("put to etcd failed, err:%v\n", err)
  28. return
  29. }
  30. // get
  31. ctx, cancel = context.WithTimeout(context.Background(), time.Second)
  32. resp, err := cli.Get(ctx, "q1mi")
  33. cancel()
  34. if err != nil {
  35. fmt.Printf("get from etcd failed, err:%v\n", err)
  36. return
  37. }
  38. for _, ev := range resp.Kvs {
  39. fmt.Printf("%s:%s\n", ev.Key, ev.Value)
  40. }
  41. }
复制代码

watch操作

  1. watch
复制代码
用来获取将来 更改的关照 。

  1. package main
  2. import (
  3. "context"
  4. "fmt"
  5. "time"
  6. "go.etcd.io/etcd/clientv3"
  7. )
  8. // watch demo
  9. func main() {
  10. cli, err := clientv3.New(clientv3.Config{
  11. Endpoints: []string{"127.0.0.1:2379"},
  12. DialTimeout: 5 * time.Second,
  13. })
  14. if err != nil {
  15. fmt.Printf("connect to etcd failed, err:%v\n", err)
  16. return
  17. }
  18. fmt.Println("connect to etcd success")
  19. defer cli.Close()
  20. // watch key:q1mi change
  21. rch := cli.Watch(context.Background(), "q1mi") // <-chan WatchResponse
  22. for wresp := range rch {
  23. for _, ev := range wresp.Events {
  24. fmt.Printf("Type: %s Key:%s Value:%s\n", ev.Type, ev.Kv.Key, ev.Kv.Value)
  25. }
  26. }
  27. }
复制代码

将上面的代码保存编译实行 ,此时程序就会等待etcd中

  1. q1mi
复制代码
这个key的变化。

比方 :我们打开终端实行 以下下令 修改、删除、设置

  1. q1mi
复制代码
这个key。

  1. etcd> etcdctl.exe --endpoints=http://127.0.0.1:2379 put q1mi "dsb2"
  2. OK
  3. etcd> etcdctl.exe --endpoints=http://127.0.0.1:2379 del q1mi
  4. 1
  5. etcd> etcdctl.exe --endpoints=http://127.0.0.1:2379 put q1mi "dsb3"
  6. OK
复制代码

上面的程序都能收到如下关照 。

  1. watch>watch.exe
  2. connect to etcd success
  3. Type: PUT Key:q1mi Value:dsb2
  4. Type: DELETE Key:q1mi Value:
  5. Type: PUT Key:q1mi Value:dsb3
复制代码

lease租约

  1. package main
  2. import (
  3. "fmt"
  4. "time"
  5. )
  6. // etcd lease
  7. import (
  8. "context"
  9. "log"
  10. "go.etcd.io/etcd/clientv3"
  11. )
  12. func main() {
  13. cli, err := clientv3.New(clientv3.Config{
  14. Endpoints: []string{"127.0.0.1:2379"},
  15. DialTimeout: time.Second * 5,
  16. })
  17. if err != nil {
  18. log.Fatal(err)
  19. }
  20. fmt.Println("connect to etcd success.")
  21. defer cli.Close()
  22. // 创建一个5秒的租约
  23. resp, err := cli.Grant(context.TODO(), 5)
  24. if err != nil {
  25. log.Fatal(err)
  26. }
  27. // 5秒钟之后, /nazha/ 这个key就会被移除
  28. _, err = cli.Put(context.TODO(), "/nazha/", "dsb", clientv3.WithLease(resp.ID))
  29. if err != nil {
  30. log.Fatal(err)
  31. }
  32. }
复制代码

keepAlive

  1. package main
  2. import (
  3. "context"
  4. "fmt"
  5. "log"
  6. "time"
  7. "go.etcd.io/etcd/clientv3"
  8. )
  9. // etcd keepAlive
  10. func main() {
  11. cli, err := clientv3.New(clientv3.Config{
  12. Endpoints: []string{"127.0.0.1:2379"},
  13. DialTimeout: time.Second * 5,
  14. })
  15. if err != nil {
  16. log.Fatal(err)
  17. }
  18. fmt.Println("connect to etcd success.")
  19. defer cli.Close()
  20. resp, err := cli.Grant(context.TODO(), 5)
  21. if err != nil {
  22. log.Fatal(err)
  23. }
  24. _, err = cli.Put(context.TODO(), "/nazha/", "dsb", clientv3.WithLease(resp.ID))
  25. if err != nil {
  26. log.Fatal(err)
  27. }
  28. // the key 'foo' will be kept forever
  29. ch, kaerr := cli.KeepAlive(context.TODO(), resp.ID)
  30. if kaerr != nil {
  31. log.Fatal(kaerr)
  32. }
  33. for {
  34. ka := <-ch
  35. fmt.Println("ttl:", ka.TTL)
  36. }
  37. }
复制代码

基于etcd实现分布式锁

  1. go.etcd.io/etcd/clientv3/concurrency
复制代码
在etcd之上实现并发操作,如分布式锁、屏蔽 和推选 。

导入该包:

  1. import "go.etcd.io/etcd/clientv3/concurrency"
复制代码

基于etcd实现的分布式锁示例:

  1. cli, err := clientv3.New(clientv3.Config{Endpoints: endpoints})
  2. if err != nil {
  3. log.Fatal(err)
  4. }
  5. defer cli.Close()
  6. // 创建两个单独的会话用来演示锁竞争
  7. s1, err := concurrency.NewSession(cli)
  8. if err != nil {
  9. log.Fatal(err)
  10. }
  11. defer s1.Close()
  12. m1 := concurrency.NewMutex(s1, "/my-lock/")
  13. s2, err := concurrency.NewSession(cli)
  14. if err != nil {
  15. log.Fatal(err)
  16. }
  17. defer s2.Close()
  18. m2 := concurrency.NewMutex(s2, "/my-lock/")
  19. // 会话s1获取锁
  20. if err := m1.Lock(context.TODO()); err != nil {
  21. log.Fatal(err)
  22. }
  23. fmt.Println("acquired lock for s1")
  24. m2Locked := make(chan struct{})
  25. go func() {
  26. defer close(m2Locked)
  27. // 等待直到会话s1释放了/my-lock/的锁
  28. if err := m2.Lock(context.TODO()); err != nil {
  29. log.Fatal(err)
  30. }
  31. }()
  32. if err := m1.Unlock(context.TODO()); err != nil {
  33. log.Fatal(err)
  34. }
  35. fmt.Println("released lock for s1")
  36. <-m2Locked
  37. fmt.Println("acquired lock for s2")
复制代码

输出:

  1. acquired lock for s1
  2. released lock for s1
  3. acquired lock for s2
复制代码

查看文档相识 更多

其他操作

其他操作请查看etcd/clientv3官方文档

参考链接:

https://etcd.io/docs/v3.3.12/demo/

https://www.infoq.cn/article/etcd-interpretation-application-scenario-implement-principle/ 代码改变天下 ,实事求是 ,python、Golang。

到此这篇关于Go操作etcd的实现示例的文章就先容 到这了,更多干系 Go操作etcd内容请搜刮 脚本之家从前 的文章或继续欣赏 下面的干系 文章盼望 大家以后多多支持脚本之家!


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
回复

使用道具 举报

avatar 123457648 | 2021-9-29 12:37:07 | 显示全部楼层
顶一下,收藏了!
回复

使用道具 举报

avatar 礼记离 | 2021-9-30 20:21:22 | 显示全部楼层
信admin楼主,得永生!
回复

使用道具 举报

avatar 六月清晨搅 | 2021-10-1 22:16:54 | 显示全部楼层
很经典,收藏了!
回复

使用道具 举报

avatar 术数古籍专卖疤 | 2021-10-5 10:17:19 | 显示全部楼层
上次给admin楼主开的药,你都吃完了?
回复

使用道具 举报

avatar 心随674 | 2021-10-6 03:43:42 | 显示全部楼层
好东西,学习学习!
回复

使用道具 举报

avatar 追上前面的 | 2021-10-6 05:07:01 | 显示全部楼层
骂人也是要有水平的!
回复

使用道具 举报

avatar 兰905 | 5 天前 | 显示全部楼层
admin楼主很有激情啊!
回复

使用道具 举报

avatar 静254 | 4 天前 | 显示全部楼层
雷锋做好事不留名,都写在帖子里!
回复

使用道具 举报

admin楼主的帖子实在是写得太好了。文笔流畅,修辞得体!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则