go之etcd读写实战

5 篇文章 0 订阅
2 篇文章 0 订阅

什么是etcd?

etcd 发音为/ˈɛtsiːdiː/,名字的由来,“distributed etc directory.”,意思是“分布式etc目录”,说明它存的是大型分布式系统的配置信息。
官网的一句话
A distributed, reliable key-value store for the most critical data of a distributed system.
翻译并理解过来就是:一个用于存储分布式系统中最关键的数据的仓库,它是分布式的、可靠的键值对仓库。首先它是个数据存储仓库,它的特性是分布式的、可靠性的,数据存储格式是键值对存储,它主要用于存储分布式系统中的关键数据。

etcd的应用场景

etcd的应用场景:服务发现和服务注册、配置中心、分布式锁。

1、服务发现
服务发现要解决的也是分布式系统中最常见的问题之一,即在同一个分布式集群中的进程或服务,要如何才能找到对方并建立连接。本质上来说,服务发现就是想要了解集群中是否有进程在监听 udp 或 tcp 端口,并且通过名字就可以查找和连接。
控制时序,即所有想要获得锁的用户都会被安排执行,但是获得锁的顺序也是全局唯一的,同时决定了执行顺序。
2、配置中心
将一些配置信息放到 etcd 上进行集中管理。
这类场景的使用方式通常是这样:应用在启动的时候主动从 etcd 获取一次配置信息,同时,在 etcd 节点上注册一个 Watcher 并等待,以后每次配置有更新的时候,etcd 都会实时通知订阅者,以此达到获取最新配置信息的目的。
3、分布式锁
因为 etcd 使用 Raft 算法保持了数据的强一致性,某次操作存储到集群中的值必然是全局一致的,所以很容易实现分布式锁。锁服务有两种使用方式,一是保持独占,二是控制时序。

  • 保持独占即所有获取锁的用户最终只有一个可以得到。etcd 为此提供了一套实现分布式锁原子操作 CAS(CompareAndSwap)的API。通过设置prevExist值,可以保证在多个节点同时去创建某个目录时,只有一个成功。而创建成功的用户就可以认为是获得了锁。
  • 控制时序,即所有想要获得锁的用户都会被安排执行,但是获得锁的顺序也是全局唯一的,同时决定了执行顺序。etcd 为此也提供了一套API(自动创建有序键),对一个目录建值时指定为POST动作,这样 etcd会自动在目录下生成一个当前最大的值为键,存储这个新的值(客户端编号)。同时还可以使用 API按顺序列出所有当前目录下的键值。此时这些键的值就是客户端的时序,而这些键中存储的值可以是代表客户端的编号。

为什么用etcd而不用zookeeper?

  • etcd简单,使用Go语言编写部署简单,支持HTTP/JSON API,使用简单:使用Raft算法保证强一致性,让用户易于理解。
  • etcd默认数据一更新就进行持久化。
  • etcd支持SSL客户端安全认证。
  • zookeeper部署维护复杂,其使用的PAXOS强一致性算法难懂。官方只提供了JAVA和C两种语言的接口。
  • zookeeper 使用JAVA编写引入大量依赖。运维人员维护起来比较麻烦。

下面开始实战

安装etcd

  1. 所谓安装ectd,实际上就是下载etcd文件,然后解压即可。

etcd下载目录:https://github.com/etcd-io/etcd/releases
在这里插入图片描述

(如果上面的地址下载不了。就进入这个地址下载:https://studygolang.com/articles/28682)

  1. 下载到本地window,通过scp (或者ftp工具)上传到linux目录即可:

scp -r ./etcd-v3.4.7-linux-amd64.tar.gz root@81.68.81.16:/usr/local

  1. 到linux的环境执行以下操作:
cd /usr/local
tar -xzvf etcd-v3.4.7-linux-amd64.tar.gz

解压成功后能看到下面的文件:
在这里插入图片描述

首先,必须开启etcd(需要在etcd的解压目录下执行)

./etcd (后台挂起程序:nohup ./etcd &

查看etcd默认的端口是否已经开启:netstat -antp | grep 2379
在这里插入图片描述
然后就可以在xshell控制台新增和读取key-value(需要在etcd的解压目录下执行)

./etcdctl --endpoints=localhost:2379 put mylove lxz
./etcdctl --endpoints=localhost:2379 get mylove
#删除key:
./etcdctl del /hello

在这里插入图片描述
如果没开启etcd,就进行读取或新增key的话,会出现下面的报错:
在这里插入图片描述

通过go代码实现etcd的读写操作

  1. 设置key-value
package main
import (
	"fmt"
	"context"
	clientv3 "go.etcd.io/etcd/client/v3"
	"time"
)
// 设置etcd的值
func main() {
    key := "bookname"
    value := "收敛之道"
    SetEtcdData(key,value)
}

func SetEtcdData(key string,value string) {
	cli, err := clientv3.New(clientv3.Config{
        Endpoints:   []string{"127.0.0.1:2379"},
        DialTimeout: 5 * time.Second,
    })
    if err != nil {
        // handle error!
        fmt.Printf("connect to etcd failed, err:%v\n", err)
        return
    }
    fmt.Println("connect to etcd success")
    defer cli.Close()
    // put
    ctx, cancel := context.WithTimeout(context.Background(), time.Second)
    _, err = cli.Put(ctx, key, value)
    cancel()
    if err != nil {
        fmt.Printf("put to etcd failed, err:%v\n", err)
        return
    }
}
  1. 读取key-value
package main
import (
	"fmt"
	"context"
	clientv3 "go.etcd.io/etcd/client/v3"
	"time"
)

//获取etcd的值
func main() {
	fmt.Println("获取etcd的key")
	//获取单个key
	// key := "foo"
	// GetEtcdData(key)
	// GetEtcdData2(key)

	//获取多个key
	keys := []string{"foo","mylove"}
	GetEtcdData3(keys...)
}

func GetEtcdData(key string) {
	cli, err := clientv3.New(clientv3.Config{
        Endpoints:   []string{"127.0.0.1:2379"},
        DialTimeout: 5 * time.Second,
    })
    if err != nil {
        // handle error!
        fmt.Printf("connect to etcd failed, err:%v\n", err)
        return
    }
    fmt.Println("connect to etcd success")
    defer cli.Close()
    //限制请求时间为1秒
    ctx, cancel := context.WithTimeout(context.Background(), time.Second)
    resp, err := cli.Get(ctx, key)
    cancel()
    if err != nil {
        fmt.Printf("get from etcd failed, err:%v\n", err)
        return
    }
    fmt.Println(resp.Kvs) //[key:"foo" create_revision:2 mod_revision:3 version:2 value:"bar" ]
    fmt.Println(resp.Kvs[0])//key:"foo" create_revision:2 mod_revision:3 version:2 value:"bar"
    for _, ev := range resp.Kvs {
        fmt.Printf("获取etcd的key-value为:%s:%s\n", ev.Key, ev.Value)
    }
}


func GetEtcdData2(key string) {
	var kv clientv3.KV
	cli, err := clientv3.New(clientv3.Config{
        Endpoints:   []string{"127.0.0.1:2379"},
        DialTimeout: 5 * time.Second,
    })
    if err != nil {
        // handle error!
        fmt.Printf("connect to etcd failed, err:%v\n", err)
        return
    }
    fmt.Println("connect to etcd success")
    defer cli.Close()
    //限制请求时间为1秒
    ctx, cancel := context.WithTimeout(context.Background(), time.Second)
    kv = clientv3.NewKV(cli)//用newKv的方式获取键值对
    resp, err := kv.Get(ctx, key)
    cancel()
    if err != nil {
        fmt.Printf("get from etcd failed, err:%v\n", err)
        return
    }
    fmt.Println(resp.Kvs) //[key:"foo" create_revision:2 mod_revision:3 version:2 value:"bar" ]
    fmt.Println(resp.Kvs[0])//key:"foo" create_revision:2 mod_revision:3 version:2 value:"bar"
    for _, ev := range resp.Kvs {
        fmt.Printf("22获取etcd的key-value为:%s:%s\n", ev.Key, ev.Value)
    }
}


//获取多个键值对
func GetEtcdData3(keys...string) {
	cli, err := clientv3.New(clientv3.Config{
        Endpoints:   []string{"127.0.0.1:2379"},
        DialTimeout: 5 * time.Second,
    })
    if err != nil {
        // handle error!
        fmt.Printf("connect to etcd failed, err:%v\n", err)
        return
    }
    fmt.Println("connect to etcd success")
    defer cli.Close()
    ctx := context.TODO()//做法1
    //限制请求时间为1秒
    for _,k := range keys {
    	//做法2
    	// ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	    resp, err := cli.Get(ctx, k)
	    // cancel()
	    if err != nil {
	        fmt.Printf("get from etcd failed, err:%v\n", err)
	        return
	    }
	    for _, ev := range resp.Kvs {
	        fmt.Printf("获取etcd的多个key-value为:%s:%s\n", ev.Key, ev.Value)
	    }
    }
}
  1. 监听key的变化
package main

import (
    "context"
    "fmt"
    "time"
    clientv3 "go.etcd.io/etcd/client/v3"
)

// watch demo
//监听某个key的变化
func main() {
    cli, err := clientv3.New(clientv3.Config{
        Endpoints:   []string{"127.0.0.1:2379"},
        DialTimeout: 5 * time.Second,
    })
    if err != nil {
        fmt.Printf("connect to etcd failed, err:%v\n", err)
        return
    }
    fmt.Println("connect to etcd success")
    defer cli.Close()
    // watch key:bookname change
    rch := cli.Watch(context.Background(), "bookname") // <-chan WatchResponse
    for wresp := range rch {
        for _, ev := range wresp.Events {
            fmt.Printf("Type: %s Key:%s Value:%s\n", ev.Type, ev.Kv.Key, ev.Kv.Value)
        }
    }
}

效果:
在这里插入图片描述

在这里插入图片描述

etcd操作多个key:

#获取某个前缀的key,--keys-only代表不展示value。
./etcdctl --endpoints=localhost:2379 get / --prefix --keys-only 


#获取所有key,以及对应的value
./etcdctl --endpoints=localhost:2379 get  ""  --prefix

#获取所有key
./etcdctl --endpoints=localhost:2379 get  ""  --prefix --keys-only

#监听某个前缀的所有key的修改
./etcdctl --endpoints=localhost:2379 watch "/k" --prefix

#删除前缀是/hello开头下的所有key:
./etcdctl del /hello --prefix

在这里插入图片描述

在这里插入图片描述
代码实战:

//获取某个前缀下的所有key
//当pre_key=""的时候,获取全部的key。
func GetEtcdData4(pre_key string) {
	var kv clientv3.KV
	cli, err := clientv3.New(clientv3.Config{
        Endpoints:   []string{"127.0.0.1:2379"},
        DialTimeout: 5 * time.Second,
    })
    if err != nil {
        // handle error!
        fmt.Printf("connect to etcd failed, err:%v\n", err)
        return
    }
    fmt.Println("connect to etcd success")
    defer cli.Close()
    //限制请求时间为1秒
    ctx := context.TODO()//做法1
    kv = clientv3.NewKV(cli)//用newKv的方式获取键值对
    //获取前缀下面的所有key。当pre_key=""的时候,获取全部的key。
    resp, err := kv.Get(ctx, pre_key, clientv3.WithPrefix())
    if err != nil {
        fmt.Printf("get from etcd failed, err:%v\n", err)
        return
    }
    fmt.Println(resp.Kvs) //[key:"foo" create_revision:2 mod_revision:3 version:2 value:"bar" ]
    // fmt.Println(resp.Kvs[0])//key:"foo" create_revision:2 mod_revision:3 version:2 value:"bar"
    for _, ev := range resp.Kvs {
        fmt.Printf("22获取etcd的key-value为:%s:%s\n", ev.Key, ev.Value)
    }
}

推荐阅读:
https://blog.csdn.net/wohu1104/article/details/108552649
https://www.topgoer.com/%E6%95%B0%E6%8D%AE%E5%BA%93%E6%93%8D%E4%BD%9C/go%E6%93%8D%E4%BD%9Cetcd/etcd%E4%BB%8B%E7%BB%8D.html

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值