Go 连接和使用ETCD | 青训营

419 阅读5分钟

一、ETCD介绍

etcd(读作 et-see-dee)是一种开源的分布式统一键值存储,用于分布式系统或计算机集群的共享配置、服务发现和的调度协调。etcd 有助于促进更加安全的自动更新,协调向主机调度的工作,并帮助设置容器的覆盖网络。

etcd 是许多其他项目的核心组件。最值得注意的是,它是 Kubernetes 的首要数据存储,也是容器编排的实际标准系统。使用 etcd, 云原生应用可以保持更为一致的运行时间,而且在个别服务器发生故障时也能正常工作。应用从 etcd 读取数据并写入到其中;通过分散配置数据,为节点配置提供冗余和弹性。‘

etcd作为服务发现系统,有以下的特点:

  • 简单:安装配置简单,而且提供了HTTP API进行交互,使用也很简单
  • 安全:支持SSL证书验证
  • 快速:根据官方提供的benchmark数据,单实例支持每秒2k+读操作
  • 可靠:采用raft算法,实现分布式系统数据的可用性和一致性

二、ETCD安装(win端)

1、etcd github下载地址,选择对应的版本下载即可,我这里选择的是3.5.9版本,Windows版本解压后文件目录如下:

WkLlfNHtiu.png

etcd是服务端,etcdctl 是内置客户端

2、查看版本号,在etcd根目录下打开cmd执行 etcdctl --version

3.5.9版本默认API version的版本应该是3,如果不为3,则通过如下命令修改:set ETCDCTL_API=3,我们可以通过etcdctl help来查看版本3和2命令和功能方面的差异

3、单机启动 etcd,我们直接在etcd的根目录里面单击 etcd.exe 即可

MKp2IjNEBI.png

4、etcd 默认使用2379端口提供HTTP API服务,2380端口和peer通信。

三、Go 连接和使用ETCD

1、官方文档

2、安装etcd客户端库

go get go.etcd.io/etcd/clientv3     
# 修复
go get -u -x google.golang.org/grpc@v1.26.0

3、连接etcd客户端代码

下面代码需要我们根据自己的配置去修改相应参数,Endpoints 是etcd的地址和端口号,默认端口是2379;DialTimeout 是连接超时时间,这里是5s,运行下面代码后,输出显示 etcd boot success 代表etcd连接成功

package main
​
import (
   "fmt"
   "go.etcd.io/etcd/clientv3"
   "time"
)
​
func main() {
   cli, err := clientv3.New(clientv3.Config{
      Endpoints:   []string{"127.0.0.1:2379"},
      DialTimeout: 5 * time.Second,
   })
   if err != nil {
      panic(err)
   }
   fmt.Println("etcd boot success")
   defer cli.Close()
}

4、使用PUT写数据

通过Put函数写入数据,如果Key存在则覆盖,否则新建一个

下面代码中我们首先设置请求超时时间为5秒,然后设置设置key 为 etcd_test 值为 hello world!,然后通过Put方法进行写入

    // 获取上下文,设置请求超时时间为5秒
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    key := "etcd_test"
    str := "hello world!"
    _, err = cli.Put(ctx, key, str)
    if err != nil {
        fmt.Printf("put to etcd failed, err:%v", err)
        return
    }
    cancel()
    fmt.Println("写入成功!")

5、通过etcdctl 客户端利用命令方式查询

etcdctl 是 etcd 的官方命令行客户端,可以用于与 etcd 集群进行交互。

(1)查询指定键的值,查询刚才代码中的key
etcdctl get etcd_test

image-20230824214810197

(2)列出所有键和值
etcdctl get --prefix ""
(3)列出以特定前缀开头的所有键和值
etcdctl get --prefix etcd

6、通过GET 读数据

通过Get函数,可以查询key的值

虽然下面示例代码我们只是查询一个Key的值,但是Get的查询结果可以表示多个Key的结果例如我们根据Key进行前缀匹配,Get函数可能会返回多个值。

    // 获取上下文,设置请求超时时间为5秒
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    key := "etcd_test"
    resp, err := cli.Get(ctx, key)
    if err != nil {
        fmt.Printf("-------get from etcd failed, err:%v", err)
        return
    }
    for _, ev := range resp.Kvs {
        fmt.Printf("%s : %s\n", ev.Key, ev.Value)
    }
    fmt.Println("读取成功!")
    cancel()

运行结果如下:

image-20230824215534212

7、前缀匹配

etcd支持key前缀匹配:Get,Delele函数都支持前缀匹配,只需要添加clientv3.WithPrefix()参数即可。

我们现在去匹配以etcd为前缀的key

    // 获取上下文,设置请求超时时间为5秒
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    keyPrefix := "etcd" // 读取key前缀等于"etcd"的所有值
    resp, err := cli.Get(ctx, keyPrefix, clientv3.WithPrefix())
    if err != nil {
        fmt.Printf("-------get from etcd failed, err:%v", err)
        return
    }
    for _, ev := range resp.Kvs {
        fmt.Printf("%s : %s\n", ev.Key, ev.Value)
    }
    fmt.Println("读取成功!")
    cancel()

8、通过Delete 删除数据

通过Delete 函数删除数据,现在我们要删除key为 etcd_test 的数据

// 获取上下文,设置请求超时时间为5秒
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
// 删除key="etcd_test" 的值
keyPrefix := "etcd_test"
_, err = cli.Delete(ctx, keyPrefix)
if err != nil {
   fmt.Printf("-------delete from etcd failed, err:%v", err)
   return
}
fmt.Println("删除成功!")
cancel()

批量删除:可以通过 clientv3.WithPrefix() 参数匹配前缀去批量删除数据

_, err = cli.Delete(ctx, keyPrefix, clientv3.WithPrefix())

9、通过watch监控数据

etcd 的核心特性之一,就是我们可以监控key的数据变化,只要有人修改了key的值,我们都可以监控到变化的值。

(1)监控指定Key
	// 监控key为etcd_test 的值
	key := "etcd_test"
	rch := cli.Watch(context.Background(), key)
	// 通过channel遍历key的值的变化
	for wresp := range rch {
		for _, ev := range wresp.Events {
			fmt.Printf("%s %q : %q\n", ev.Type, ev.Kv.Key, ev.Kv.Value)
		}
	}

运行上面代码,这个时候我们通过 etcdctl 命令行 去修改之前创建的 key 为 etcd_test 的数据,我们可以发现之前运行的代码输出了新的值

etcdctl put etcd_test "Hello, etcd!"

image-20230824220900063

(2)根据key前缀监控一组key的值
	// 监控 etcd_test 为前缀的所有key的值
	keyPrefix := "etcd_test"
	rch := cli.Watch(context.Background(), keyPrefix, clientv3.WithPrefix()))
	// 通过channel遍历key的值的变化
	for wresp := range rch {
		for _, ev := range wresp.Events {
			fmt.Printf("%s %q : %q\n", ev.Type, ev.Kv.Key, ev.Kv.Value)
		}
	}