热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

go与etcd

go与etcd,Go语言社区,Golang程序员人脉社

etcd

高可用的分布式key-value存储,可用于配置共享和服务发现,类似项目有zookeeper

提供restful的http接口,使用简单。基于raft算法(主从、选举等)的强一致性,高可用的服务存储目录

应用场景:服务发现和服务注册,配置中心,分布式锁,master选举

参考:

https://coreos.com/etcd/docs/latest/demo.html

https://github.com/etcd-io/etcd


 Windows命令行使用etcd(本地单台机器)

1、环境变量设置ETCDCTL_API=3

2、启动etcd:

./etcd.exe

提示已在指定ip和端口启动

3、通过使用 etcdctl 来和已经启动的集群交互:

./etcdctl.exe --write-out=table --endpoints=localhost:2379 member list

多台机器集群参考http://etcd.doczh.cn/documentation/op-guide/clustering.html

 

 go操作etcd

快速入门

启动etcd.exe后

package main import ( "context" "fmt" "go.etcd.io/etcd/clientv3" "log" "strconv" "time" ) func main() { config := clientv3.Config{ Endpoints: []string{"127.0.0.1:2379"}, DialTimeout: 10 * time.Second, } cli, err := clientv3.New(config) if err != nil { panic(err) } defer cli.Close() go put(cli) rch := cli.Watch(context.Background(), "foo", clientv3.WithPrefix()) for wresp := range rch { for _, ev := range wresp.Events { fmt.Printf("%s %q : %qn", ev.Type, ev.Kv.Key, ev.Kv.Value) } } } func put(cli *clientv3.Client) { num := 1 for { time.Sleep(time.Second) ctx, cancel := context.WithTimeout(context.Background(), time.Second) _, err := cli.Put(ctx, "foo", strconv.Itoa(num)) cancel() if err != nil { log.Fatal(err) } num += 1 } } //output //PUT "foo" : "1" //PUT "foo" : "2" //PUT "foo" : "3" //PUT "foo" : "4" //。。。

其他细节 

type KV interface { Put(ctx context.Context, key, val string, opts ...OpOption) (*PutResponse, error) Get(ctx context.Context, key string, opts ...OpOption) (*GetResponse, error) Delete(ctx context.Context, key string, opts ...OpOption) (*DeleteResponse, error) Compact(ctx context.Context, rev int64, opts ...CompactOption) (*CompactResponse, error) Do(ctx context.Context, op Op) (OpResponse, error) Txn(ctx context.Context) Txn }

kv

//put操作 ctx, cancel := context.WithTimeout(context.Background(), requestTimeout) _, err = cli.Put(ctx, "sample_key", "sample_value") cancel() if err != nil { log.Fatal(err) } ---------- //处理put的错误情况 ctx, cancel := context.WithTimeout(context.Background(), requestTimeout) _, err = cli.Put(ctx, "", "sample_value") cancel() if err != nil { switch err { case context.Canceled: fmt.Printf("ctx is canceled by another routine: %vn", err) case context.DeadlineExceeded: fmt.Printf("ctx is attached with a deadline is exceeded: %vn", err) case rpctypes.ErrEmptyKey: fmt.Printf("client-side error: %vn", err) default: fmt.Printf("bad cluster endpoints, which are not etcd servers: %vn", err) } } // Output: client-side error: etcdserver: key is not provided ---------- //get操作 ctx, cancel := context.WithTimeout(context.Background(), requestTimeout) resp, err := cli.Get(ctx, "foo") cancel() if err != nil { log.Fatal(err) } for _, ev := range resp.Kvs { fmt.Printf("%s : %sn", ev.Key, ev.Value) } // Output: foo : bar ---------- //get某key的某个历史版本 //presp.Header.Revision是int64类型,可以直接填入int64(1)、int64(2)等等获取对应历史版本 presp, err := cli.Put(context.TODO(), "foo", "bar1") if err != nil { log.Fatal(err) } _, err = cli.Put(context.TODO(), "foo", "bar2") if err != nil { log.Fatal(err) } ctx, cancel := context.WithTimeout(context.Background(), requestTimeout) resp, err := cli.Get(ctx, "foo", clientv3.WithRev(presp.Header.Revision)) cancel() if err != nil { log.Fatal(err) } for _, ev := range resp.Kvs { fmt.Printf("%s : %sn", ev.Key, ev.Value) } // Output: foo : bar1 ---------- //排序取出 for i := range make([]int, 3) { ctx, cancel := context.WithTimeout(context.Background(), requestTimeout) _, err = cli.Put(ctx, fmt.Sprintf("key_%d", i), "value") cancel() if err != nil { log.Fatal(err) } } ctx, cancel := context.WithTimeout(context.Background(), requestTimeout) //获得以"key"为前缀的所有key,并以key为排序依据逆序取出 resp, err := cli.Get(ctx, "key", clientv3.WithPrefix(), clientv3.WithSort(clientv3.SortByKey, clientv3.SortDescend)) cancel() if err != nil { log.Fatal(err) } for _, ev := range resp.Kvs { fmt.Printf("%s : %sn", ev.Key, ev.Value) } // Output: // key_2 : value // key_1 : value // key_0 : value ---------- //delete // count keys about to be deleted gresp, err := cli.Get(ctx, "key", clientv3.WithPrefix()) if err != nil { log.Fatal(err) } // delete the keys dresp, err := cli.Delete(ctx, "key", clientv3.WithPrefix()) if err != nil { log.Fatal(err) } fmt.Println("Deleted all keys:", int64(len(gresp.Kvs)) == dresp.Deleted) // Output: // Deleted all keys: true ---------- //compact,删除某版本之前的历史修改 ctx, cancel := context.WithTimeout(context.Background(), requestTimeout) resp, err := cli.Get(ctx, "foo") cancel() if err != nil { log.Fatal(err) } compRev := resp.Header.Revision // 选择一个版本,删除其之前的版本 ctx, cancel = context.WithTimeout(context.Background(), requestTimeout) _, err = cli.Compact(ctx, compRev) cancel() if err != nil { log.Fatal(err) } ---------- //txn,将几个请求放入一个事务中执行 kvc := clientv3.NewKV(cli) _, err = kvc.Put(context.TODO(), "key", "xyz") if err != nil { log.Fatal(err) } ctx, cancel := context.WithTimeout(context.Background(), requestTimeout) _, err = kvc.Txn(ctx). If(clientv3.Compare(clientv3.Value("key"), ">", "abc")). Then(clientv3.OpPut("key", "XYZ")). // 因为"xyz" > "abc"所以执行了这一条,修改为"XYZ" Else(clientv3.OpPut("key", "ABC")). // 不执行 Commit() cancel() if err != nil { log.Fatal(err) } ---------- //do,不采用事务,执行多条指令 ops := []clientv3.Op{ clientv3.OpPut("put-key", "123"), clientv3.OpGet("put-key"), clientv3.OpPut("put-key", "456")} for _, op := range ops { if _, err := cli.Do(context.TODO(), op); err != nil { log.Fatal(err) } }

watch

func Example() { config := clientv3.Config{ Endpoints:[]string{"127.0.0.1:2379"}, DialTimeout:10*time.Second, } cli,err := clientv3.New(config) if err != nil {panic(err)} defer cli.Close() ------------------------------------------------------ //watch基本操作,监控对key的put、delete操作,并返回新值。put的值和之前相同也会返回 rch := cli.Watch(context.Background(), "foo") //返回一个channel类型 for wresp := range rch { //阻塞直到rch有值,然后等待下一个操作 for _, ev := range wresp.Events { fmt.Printf("%s %q : %qn", ev.Type, ev.Kv.Key, ev.Kv.Value) } } // PUT "foo" : "bar" } ---------- //watch以"foo"为前缀的key rch := cli.Watch(context.Background(), "foo", clientv3.WithPrefix()) for wresp := range rch { for _, ev := range wresp.Events { fmt.Printf("%s %q : %qn", ev.Type, ev.Kv.Key, ev.Kv.Value) } } ---------- // watche在['foo1', 'foo4')范围内的key。具体为foo1、foo2、foo3。 rch := cli.Watch(context.Background(), "foo1", clientv3.WithRange("foo4")) for wresp := range rch { for _, ev := range wresp.Events { fmt.Printf("%s %q : %qn", ev.Type, ev.Kv.Key, ev.Kv.Value) } } ---------- //ProgressNotify rch := cli.Watch(context.Background(), "foo", clientv3.WithProgressNotify()) wresp := <-rch fmt.Printf("wresp.Header.Revision: %dn", wresp.Header.Revision) fmt.Println("wresp.IsProgressNotify:", wresp.IsProgressNotify()) // wresp.Header.Revision: 0 // wresp.IsProgressNotify: true // IsProgressNotify returns true if the WatchResponse is progress notification

其他

func Examples() { cli, err := clientv3.New(clientv3.Config{ Endpoints:[]string{"127.0.0.1:2379"}, DialTimeout: dialTimeout, }) if err != nil { log.Fatal(err) } defer cli.Close() //cluster操作 --------------- resp, err := cli.MemberList(context.Background()) if err != nil { log.Fatal(err) } fmt.Println("members:", len(resp.Members)) // Output: members: 3 --------------- peerURLs := endpoints[2:] mresp, err := cli.MemberAdd(context.Background(), peerURLs) if err != nil { log.Fatal(err) } fmt.Println("added member.PeerURLs:", mresp.Member.PeerURLs) // added member.PeerURLs: [http://localhost:32380] -------------- resp, err := cli.MemberList(context.Background()) if err != nil { log.Fatal(err) } _, err = cli.MemberRemove(context.Background(), resp.Members[0].ID) if err != nil { log.Fatal(err) } ------------- resp, err := cli.MemberList(context.Background()) if err != nil { log.Fatal(err) } peerURLs := []string{"http://localhost:12380"} _, err = cli.MemberUpdate(context.Background(), resp.Members[0].ID, peerURLs) if err != nil { log.Fatal(err) } ------------------------------------------------------------- //lease,超时过期的key ------------- // minimum lease TTL is 5-second resp, err := cli.Grant(context.TODO(), 5) if err != nil { log.Fatal(err) } // after 5 seconds, the key 'foo' will be removed _, err = cli.Put(context.TODO(), "foo", "bar", clientv3.WithLease(resp.ID)) if err != nil { log.Fatal(err) } ------------- //不必等超时,立即使和resp.ID相关的key无效 _, err = cli.Revoke(context.TODO(), resp.ID) if err != nil { log.Fatal(err) } ------------- // 与resp.ID关联的key永久有效 ch, kaerr := cli.KeepAlive(context.TODO(), resp.ID) if kaerr != nil { log.Fatal(kaerr) } } ------------------------------------------------------------- //maintenance,维护 ------------- resp, err := cli.Status(context.Background(), "127.0.0.1:2379") if err != nil { log.Fatal(err) } fmt.Printf("endpoint: %s / Leader: %vn", "127.0.0.1:2379", resp.Header.MemberId == resp.Leader) //输出:endpoint: 127.0.0.1:2379 / Leader: true ------------- //删除大量key之后可以用此去除垃圾空间 if _, err = cli.Defragment(context.TODO(), 127.0.0.1:2379); err != nil { log.Fatal(err) } ------------------------------------------------------------ //metrics //待续。。。

 


推荐阅读
  • 用Python手把手教你搭建一个web框架-flask微框架!
    在之前的文章当中,小编已经教过大家怎么搭建一个Django框架,今天我们来探索另外的一种框架的搭建,这个框架就是web框架-flask微框架啦!首先我们带着以下的几个问题来阅读本文:1、flask ... [详细]
  • 本文介绍了C#中生成随机数的三种方法,并分析了其中存在的问题。首先介绍了使用Random类生成随机数的默认方法,但在高并发情况下可能会出现重复的情况。接着通过循环生成了一系列随机数,进一步突显了这个问题。文章指出,随机数生成在任何编程语言中都是必备的功能,但Random类生成的随机数并不可靠。最后,提出了需要寻找其他可靠的随机数生成方法的建议。 ... [详细]
  • springboot启动不了_Spring Boot + MyBatis 多模块搭建教程
    作者:枫本非凡来源:www.cnblogs.comorzlinp9717399.html一、前言1、创建父工程最近公司项目准备开始重构,框 ... [详细]
  • 微软头条实习生分享深度学习自学指南
    本文介绍了一位微软头条实习生自学深度学习的经验分享,包括学习资源推荐、重要基础知识的学习要点等。作者强调了学好Python和数学基础的重要性,并提供了一些建议。 ... [详细]
  • Linux服务器密码过期策略、登录次数限制、私钥登录等配置方法
    本文介绍了在Linux服务器上进行密码过期策略、登录次数限制、私钥登录等配置的方法。通过修改配置文件中的参数,可以设置密码的有效期、最小间隔时间、最小长度,并在密码过期前进行提示。同时还介绍了如何进行公钥登录和修改默认账户用户名的操作。详细步骤和注意事项可参考本文内容。 ... [详细]
  • HDU 2372 El Dorado(DP)的最长上升子序列长度求解方法
    本文介绍了解决HDU 2372 El Dorado问题的一种动态规划方法,通过循环k的方式求解最长上升子序列的长度。具体实现过程包括初始化dp数组、读取数列、计算最长上升子序列长度等步骤。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • 本文介绍了C#中数据集DataSet对象的使用及相关方法详解,包括DataSet对象的概述、与数据关系对象的互联、Rows集合和Columns集合的组成,以及DataSet对象常用的方法之一——Merge方法的使用。通过本文的阅读,读者可以了解到DataSet对象在C#中的重要性和使用方法。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • 本文介绍了在SpringBoot中集成thymeleaf前端模版的配置步骤,包括在application.properties配置文件中添加thymeleaf的配置信息,引入thymeleaf的jar包,以及创建PageController并添加index方法。 ... [详细]
  • 本文详细介绍了Linux中进程控制块PCBtask_struct结构体的结构和作用,包括进程状态、进程号、待处理信号、进程地址空间、调度标志、锁深度、基本时间片、调度策略以及内存管理信息等方面的内容。阅读本文可以更加深入地了解Linux进程管理的原理和机制。 ... [详细]
  • 1,关于死锁的理解死锁,我们可以简单的理解为是两个线程同时使用同一资源,两个线程又得不到相应的资源而造成永无相互等待的情况。 2,模拟死锁背景介绍:我们创建一个朋友 ... [详细]
  • 于2012年3月份开始接触OpenStack项目,刚开始之处主要是与同事合作共同部署公司内部的云平台,使得公司内部服务器能更好的得到资源利用。在部署的过程中遇到各种从未遇到过的问题 ... [详细]
  • boot集成的邮箱 spring_spring boot集成mybatis(2) – 使用pagehelper实现分页
    章节SpringBoot介绍SpringBoot开发环境搭建(Eclipse)SpringBootHelloWorld(restful接口)例子spri ... [详细]
author-avatar
红颜内
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有