1.安装

wget下载地址 https://mirrors.huaweicloud.c...

etcd.config

name: "etcd"
data-dir: "/var/lib/etcd/data"
listen-client-urls: "http://0.0.0.0:2379"
advertise-client-urls: "http://0.0.0.0:2379"

启动

./etcd --config-file=/opt/etcd/etcd.config

image.png

2.基本命令

设置key

etcdctl put slq1 1

获取key

etcdctl get slq1 //获取key为slq1的value

etcdctl get slq1 --print-value-only   //只打印对应的值

etcdctl get slq1 --hex   //以十六进制显示

etcdctl get slq1 slq5 //获取一定范围的key 不包括slq5

etcdctl get --prefix slq  //获取以slq为前缀的key

etcdctl get --limit=2 --prefix slq  //限制数量为2

etcdctl get --from-key s  //查找大于s的所有key

etcdctl get --prefix --rev=4 slq1 //获取slq1 key version为4时候对应的值

删除key

etcdctl del slq1 //删除slq1

etcdctl del slq1 slq5 //删除slq1到slq5的key

etcdctl del --prefix slq //删除前缀为slq的key

etcdctl del --from-key s //删除大于s的所有key

监听key变化

etcdctl watch slq1 //监测slq1
        
etcdctl watch slq1 slq5 //监测slq1到slq5的key
        
etcdctl watch --prefix slq //监测前缀为slq的key
        
etcdctl watch --from-key s //监测大于s的所有key

对key进行加锁


etcdctl lock slq1


------------------------------------
[root@node3 opt]# etcdctl lock slq1
slq1/694d7afa84f70318
------------------------------------
[root@node3 etcd]# etcdctl lock slq1
发生阻塞

------------------------------------

事务txn

etcdctl txn -i   //-i使用交互式

清理历史版本信息compact

etcdctl compact 5

[root@prometheus ~]# etcdctl compact 3
compacted revision 3
[root@prometheus ~]# 

lease和ttl

[root@prometheus ~]# etcdctl lease grant 100
lease 694d7afa84f70339 granted with TTL(100s)
[root@prometheus ~]# 
[root@prometheus ~]# 
[root@prometheus ~]# etcdctl put --lease=694d7afa84f70339 slqslq slq
OK
[root@prometheus ~]# 
[root@prometheus ~]# etcdctl get slqslq
slqslq
slq
[root@prometheus ~]#

撤销租期
[root@prometheus ~]# etcdctl lease revoke 694d7afa84f70339
lease 694d7afa84f70339 revoked
[root@prometheus ~]# 
[root@prometheus ~]# 
[root@prometheus ~]# etcdctl get slqslq
[root@prometheus ~]#



查看租期有效时间
[root@prometheus ~]# etcdctl lease timetolive 694d7afa84f70343
lease 694d7afa84f70343 granted with TTL(100s), remaining(66s)

保持租期有效
etcdctl lease keep-alive 694d7afa84f70339 
[root@prometheus ~]# etcdctl lease keep-alive 694d7afa84f70343
lease 694d7afa84f70343 keepalived with TTL(100)

3.clientv3操作api

下载包

go get -u go.etcd.io/etcd/clientv3@v3.3.24

程序

package main


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

//基本操作
//其他操作的查询、设置和删除  详细请参考源码
// Get retrieves keys.
// By default, Get will return the value for "key", if any.
// When passed WithRange(end), Get will return the keys in the range [key, end).
// When passed WithFromKey(), Get returns keys greater than or equal to key.
// When passed WithRev(rev) with rev > 0, Get retrieves keys at the given revision;
// if the required revision is compacted, the request will fail with ErrCompacted .
// When passed WithLimit(limit), the number of returned keys is bounded by limit.
// When passed WithSort(), the keys will be sorted.
func main() {
    client, err := clientv3.New(clientv3.Config{
        Endpoints:   []string{"http://192.168.56.111:2379"},
        DialTimeout: 2 * time.Second,
    })
    if err != nil {
        log.Println("create client err:",err)
    }
    defer client.Close()
    keyName := "slq0909"
    ctx, cancel := context.WithTimeout(context.Background(), time.Second)
    _, err = client.Put(ctx, keyName, "xxxxxx")
    cancel()
    if err != nil {
        log.Printf("put to etcd failed,err:%v \n",err)
        return
    }
    ctx, cancel = context.WithTimeout(context.Background(), time.Second)
    resp,err := client.Get(ctx, keyName)
    cancel()
    if err != nil {
        log.Println("get keyname err:",err)
    }
    for _,ev := range resp.Kvs {
        fmt.Printf("%s:%s \n",ev.Key,ev.Value)
    }

    ctx, cancel = context.WithTimeout(context.Background(), time.Second)
    respon, err := client.Delete(ctx, keyName)
    cancel()
    if err != nil {
        log.Println("del keyname err:",err)
    }
    log.Println("delete result :",respon)
}

//watch
func main() {

    client, err := clientv3.New(clientv3.Config{
        Endpoints:   []string{"http://192.168.56.111:2379"},
        DialTimeout: 2 * time.Second,
    })
    if err != nil {
        log.Println("create client err:",err)
    }
    defer client.Close()
    keyName := "slq0909"

    watch := client.Watch(context.Background(), keyName)

    for resp := range watch {
        for _,ev := range resp.Events {
            fmt.Printf("Type: %s Key:%s Value:%s\n", ev.Type, ev.Kv.Key, ev.Kv.Value)
        }
    }
}

4.concurrency 分布式锁

版本一

package main

import (
    "context"
    "github.com/coreos/etcd/clientv3"
    "github.com/coreos/etcd/clientv3/concurrency"
    "log"
    "time"
)

func main() {
    cli, err := clientv3.New(clientv3.Config{
        Endpoints:   []string{"http://192.168.56.111:2379"},
        DialTimeout: 2 * time.Second,
    })
    if err != nil {
        log.Println("create client err:", err)
    }
    defer cli.Close()

    session, err := concurrency.NewSession(cli)
    if err != nil {
        log.Println("session err :",err)
    }
    mutex := concurrency.NewMutex(session, "/slq")
    err = mutex.Lock(context.Background())
    if err != nil {
        log.Println("don't get mutex:",err)
    }else {
        log.Println("get mutex:",err)
    }
    time.Sleep(time.Second * 5)
    mutex.Unlock(context.Background())
    log.Println("finished get mutex:",err)
}

//利用原生的concurrency.NexMutex(),实现分布式锁,缺点是如果协程没有抢到锁会一直阻塞住,所以需要加租期时间

版本2

package main

import (
    "context"
    "github.com/coreos/etcd/clientv3"
    "github.com/coreos/etcd/clientv3/concurrency"
    "log"
    "time"
)

var client *clientv3.Client

func init() {
    cli, err := clientv3.New(clientv3.Config{
        Endpoints:   []string{"http://192.168.56.111:2379"},
        DialTimeout: 2 * time.Second,
    })
    if err != nil {
        log.Println("create client err:", err)
    }
    client = cli
}

func main()  {
    disLock("/slq",60, func() {
        log.Println("func start")
        time.Sleep(time.Second * 4)
        log.Println("func end")
    })
}

/**
  key: 加锁key
  timeout: 超时时间,此时间包含整个任务加锁执行时间,是disLock函数整个执行的时间,如果超过timeout的时间,即使业务逻辑执行完成了,也会失败
  x: 业务逻辑
 */
func disLock(key string,timeout int64,x func()) {
    ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout) * time.Second)
    defer cancel()
    lease, err := client.Grant(ctx, timeout)
    session, err := concurrency.NewSession(client, concurrency.WithLease(lease.ID))
    if err != nil {
        log.Println("session err :", err)
    }
    defer session.Close()
    mutex := concurrency.NewMutex(session, key)
    log.Println("start get mutex:", err)
    err = mutex.Lock(ctx)
    if err != nil {
        log.Println("don't get mutex:", err)
    } else {
        log.Println("get mutex:", err)
    }
    x()
    log.Println("unlock mutex:", err)
    err = mutex.Unlock(ctx)
    if err != nil {
        log.Println("unlock mutex error:", err)
    }
    log.Println("finished get mutex:", err)
}

问题

A. 安装clientv3遇到的问题

go: downloading golang.org/x/net v0.0.0-20210726213435-c6fcb2dbf985
# github.com/coreos/etcd/clientv3/balancer/picker
..\..\go\pkg\mod\github.com\coreos\etcd@v3.3.25+incompatible\clientv3\balancer\picker\err.go:37:44: undefined: balancer.PickOptions
..\..\go\pkg\mod\github.com\coreos\etcd@v3.3.25+incompatible\clientv3\balancer\picker\roundrobin_balanced.go:55:54: undefined: balancer.PickOptions
# github.com/coreos/etcd/clientv3/balancer/resolver/endpoint
..\..\go\pkg\mod\github.com\coreos\etcd@v3.3.25+incompatible\clientv3\balancer\resolver\endpoint\endpoint.go:114:78: undefined: resolver.BuildOption
..\..\go\pkg\mod\github.com\coreos\etcd@v3.3.25+incompatible\clientv3\balancer\resolver\endpoint\endpoint.go:182:31: undefined: resolver.ResolveNowOption

go.mod

replace google.golang.org/grpc => google.golang.org/grpc v1.26.0

B. time.Duration()

不能和变量进行相乘

time.Second * timeout //编译报错

time.Duration(timeout) * time.Second //官方推荐

你若安好便是晴天
82 声望10 粉丝

引用和评论

0 条评论