Go语言学习etcd的API使用

etcd是什么?

百度一下有很多介绍etcd的文章。简要来说,ETCD是用于共享配置和服务发现的分布式,一致性的KV存储系统。其功能类似于zookeeper。
详细可以参看: https://www.infoq.cn/article/etcd-interpretation-application-scenario-implement-principle

etcd有什么特点?

  • 简单:基于 HTTP+JSON 的 API 让你用 curl 就可以轻松使用。
  • 安全:可选 SSL 客户认证机制。
  • 快速:每个实例每秒支持一千次写操作。
  • 可信:使用 Raft 算法充分实现了分布式强一致性

etcd安装使用

官网下载:https://coreos.com/etcd/
可以在命令行启动(单机):
nohup ./etcd --listen-client-urls 'http://0.0.0.0:2379' --advertise-client-urls 'http://0.0.0.0:2379' &

etcd的API使用

  • 首先要找到client包,可以通过: go get go.etcd.io/etcd/clientv3 来获取
  • 编程学习etcd的API (包括put、set、delet、租约相关、事务相关、分布式锁等)
package main

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

func main() {
	var (
		config clientv3.Config
		client *clientv3.Client
		err error
		putResp *clientv3.PutResponse
		getResp *clientv3.GetResponse
		delResp *clientv3.DeleteResponse
		leaseResp,leaseResp1 *clientv3.LeaseGrantResponse
	)
	//配置客户端
	config = clientv3.Config{
		Endpoints:[]string{"127.0.0.1:2379"},
		DialTimeout:5*time.Second,
	}

	//建立连接
	if client,err = clientv3.New(config);err!=nil {
		fmt.Println(err.Error())
		return
	}
	//用于读写etcd的键值对
	con := context.TODO()

	if putResp,err = client.Put(con,"test","haha");err !=nil {
		return
	}else{
		fmt.Println(putResp.Header)
	}

	kv := clientv3.NewKV(client)
	//用kv设置key
	if putResp, err = kv.Put(con,"/cron/jobs/job2","hello",clientv3.WithPrevKV());err !=nil {
		fmt.Println(err)
		return
	}else {
		//fmt.Println(putResp.Header)
		if putResp.PrevKv !=nil {
			fmt.Println(string(putResp.PrevKv.Value))
		}
	}
	//用kv获取Key
	if getResp, err = kv.Get(con,"/cron/jobs/",clientv3.WithPrefix());err!=nil {
		fmt.Println(err)
		return
	}else {
		fmt.Println(getResp.Kvs)
	}
	//用kv删除key
	if delResp, err = kv.Delete(con,"/cron/jobs/job2",clientv3.WithPrevKV()); err !=nil {
		fmt.Println(err)
		return
	}else {
		fmt.Println(len(delResp.PrevKvs))
	}
	//建立租约
	if leaseResp, err  = client.Grant(con,10);err !=nil {
		fmt.Println(err)
		return
	}
	//用client也可以设置key,kv是client的一个结构,因此可以使用其方法
	if putResp,err = client.Put(con,"/cron/lock/job1","ok",clientv3.WithLease(leaseResp.ID));err !=nil {
		fmt.Println(err)
		return
	}else {
		fmt.Println(putResp.Header.Revision)
	}
	if keepAliveChan,err := client.KeepAlive(con,leaseResp.ID);err !=nil {//有协程来帮自动续租,每秒一次。
		fmt.Println(err)
		return
	}else {
		go func() {
			for{
				select {
				case resp:= <-keepAliveChan:
					if resp == nil {
						fmt.Println("续租失败")
						goto END
					}else {
						fmt.Println("续租成功")
					}
				}
			}
			END:
		}()
	}
	k := 8
	for k !=0 {
		if getResp,err = client.Get(con,"/cron/lock/job1"); err !=nil {
			fmt.Println(err)
			return
		}else {
			fmt.Println(getResp.Count)
			time.Sleep(2*time.Second)
		}
		k--
	}

	go func(){
		for {
			client.Put(con,"/cron/watch/job1","I am watch job1")
			time.Sleep(1*time.Second)
			client.Delete(con,"/cron/watch/job1")
			time.Sleep(1*time.Second)
		}

	}()
	conWithTimeout,cancelFunc := context.WithCancel(context.TODO())
	wch := client.Watch(conWithTimeout,"/cron/watch/job1",clientv3.WithRev(getResp.Header.Revision))
	tt := time.After(10*time.Second)
	go func() {
		select {
			case <-tt:
				cancelFunc()
		}
	}()


	for resp := range wch {
		for _,res := range resp.Events {
			fmt.Println(res.Type,string(res.Kv.Key),string(res.Kv.Value))
		}
	}

	conWithTimeout1,cancelFunc1 := context.WithCancel(context.TODO())
	//做分布式锁相关,执行事务
	//建立租约、用租约抢key,抢到后执行业务逻辑,抢失败返回。函数退出时要defer吧租约关闭
	client.Grant(conWithTimeout1,10)
	if leaseResp1, err  = client.Grant(con,10);err !=nil {
		fmt.Println(err)
		return
	}
	//defer逻辑可以保证租约被清理,防止长期占用key
	defer client.Revoke(con,leaseResp1.ID)
	defer cancelFunc1()
	if keepAliveChan1 ,err :=client.KeepAlive(conWithTimeout1,leaseResp1.ID);err !=nil {
		fmt.Println(err)
		return
	}else {
		go func() {
			for {
				<-keepAliveChan1
			}
		}()
	}
	//打开下面可以看锁已经被抢占的情况
	//client.Put(con,"/cron/txn/job1","I GET FIRST",clientv3.WithLease(leaseResp1.ID))

	//执行事务,必须带上租约ID,这样在取消租约的时候key会同时失效。
	txn := client.Txn(con)
	txn.If(clientv3.Compare(clientv3.CreateRevision("/cron/txn/job1"),"=",0)).
		Then(clientv3.OpPut("/cron/txn/job1","my job",clientv3.WithLease(leaseResp1.ID))).
		Else(clientv3.OpGet("/cron/txn/job1"))
	if txnResp, err := txn.Commit();err !=nil {
		fmt.Println(err)
		return
	}else{
		if !txnResp.Succeeded {
			fmt.Println("锁被占用:",txnResp.Responses)
			return
		}
	}

	//拿到锁执行任务
	fmt.Println("执行业务逻辑")
	time.Sleep(2*time.Second)
	return
}

你可能感兴趣的:(golang,etcd)