什么是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简单,使用Go语言编写部署简单,支持HTTP/JSON API,使用简单:使用Raft算法保证强一致性,让用户易于理解。etcd默认数据一更新就进行持久化。etcd支持SSL客户端安全认证。zookeeper部署维护复杂,其使用的PAXOS强一致性算法难懂。官方只提供了JAVA和C两种语言的接口。zookeeper 使用JAVA编写引入大量依赖。运维人员维护起来比较麻烦。 下面开始实战为什么用etcd而不用zookeeper?
所谓安装ectd,实际上就是下载etcd文件,然后解压即可。安装etcd
etcd下载目录:https://github.com/etcd-io/etcd/releases
(如果上面的地址下载不了。就进入这个地址下载:https://studygolang.com/articles/28682)
下载到本地window,通过scp (或者ftp工具)上传到linux目录即可:scp -r ./etcd-v3.4.7-linux-amd64.tar.gz [email protected]:/usr/local
到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的话,会出现下面的报错:
设置key-value通过go代码实现etcd的读写 *** 作
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
}
}
读取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)
}
}
}
监听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
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)