//sevice.go

package discovery

import (
"context"
"errors"
"sync"
"time" "github.com/coreos/etcd/clientv3"
l4g "github.com/alecthomas/log4go"
) type Service struct {
closeChan chan struct{} //关闭通道
client *clientv3.Client //etcd v3 client
leaseID clientv3.LeaseID //etcd 租约id
key string //键
val string //值
wg sync.WaitGroup
} // NewService 构造一个注册服务
func NewService(etcdEndpoints []string, key string, val string) (*Service, error) { cli, err := clientv3.New(clientv3.Config{
Endpoints: etcdEndpoints,
DialTimeout: * time.Second,
}) if nil != err {
return nil, err
} s := &Service{
client: cli,
closeChan: make(chan struct{}),
key: key,
val: val,
} return s, nil
} // Start 开启注册
// @param - ttlSecond 租期(秒)
func (s *Service) Start(ttlSecond int64) error { // minimum lease TTL is 5-second
resp, err := s.client.Grant(context.TODO(), ttlSecond)
if err != nil {
panic(err)
} s.leaseID = resp.ID
_, err = s.client.Put(context.TODO(), s.key, s.val, clientv3.WithLease(s.leaseID))
if err != nil {
panic(err)
} ch, err1 := s.client.KeepAlive(context.TODO(), s.leaseID)
if nil != err1 {
panic(err)
} l4g.Info("[discovery] Service Start leaseID:[%d] key:[%s], value:[%s]", s.leaseID, s.key, s.val) s.wg.Add()
defer s.wg.Done() for {
select {
case <-s.closeChan:
return s.revoke()
case <-s.client.Ctx().Done():
return errors.New("server closed")
case ka, ok := <-ch:
if !ok {
l4g.Warn("[discovery] Service Start keep alive channel closed")
return s.revoke()
} else {
l4g.Fine("[discovery] Service Start recv reply from Service: %s, ttl:%d", s.key, ka.TTL)
}
}
} return nil
} // Stop 停止
func (s *Service) Stop() {
close(s.closeChan)
s.wg.Wait()
s.client.Close()
} func (s *Service) revoke() error { _, err := s.client.Revoke(context.TODO(), s.leaseID) if err != nil {
l4g.Error("[discovery] Service revoke key:[%s] error:[%s]", s.key, err.Error())
} else {
l4g.Info("[discovery] Service revoke successfully key:[%s]", s.key)
} return err
}

//watch.go

package discovery

import (
"context"
"os"
"time" "github.com/coreos/etcd/clientv3"
"github.com/coreos/etcd/mvcc/mvccpb"
l4g "github.com/alecthomas/log4go"
"google.golang.org/grpc/grpclog" ) type GroupManager struct {
wg sync.WaitGroup
ctx context.Context
cancel context.CancelFunc
once sync.Once
} func NewGroupManager() *GroupManager {
ret := new(GroupManager)
ret.ctx, ret.cancel = context.WithCancel(context.Background())
return ret
} func (this *GroupManager) Close() {
this.once.Do(this.cancel)
} func (this *GroupManager) Wait() {
this.wg.Wait()
} func (this *GroupManager) Add(delta int) {
this.wg.Add(delta)
} func (this *GroupManager) Done() {
this.wg.Done()
} func (this *GroupManager) Chan() <-chan struct{} {
return this.ctx.Done()
} type Target interface {
Set(string, string)
Create(string, string)
Modify(string, string)
Delete(string)
} type Config struct {
Servers []string
DailTimeout int64
RequestTimeout int64
Prefix bool
Target string
} func Watch(gm *GroupManager, cfg *Config, target Target) {
defer gm.Done() cli, err := clientv3.New(clientv3.Config{
Endpoints: cfg.Servers,
DialTimeout: time.Duration(cfg.DailTimeout) * time.Second,
})
if err != nil {
panic(err.Error())
return
}
defer cli.Close() // make sure to close the client l4g.Info("[discovery] start watch %s", cfg.Target) ctx, cancel := context.WithTimeout(context.Background(), time.Duration(cfg.RequestTimeout)*time.Second)
var resp *clientv3.GetResponse
if cfg.Prefix {
resp, err = cli.Get(ctx, cfg.Target, clientv3.WithPrefix())
} else {
resp, err = cli.Get(ctx, cfg.Target)
}
cancel()
if err != nil {
panic(err.Error())
}
for _, ev := range resp.Kvs {
target.Set(string(string(ev.Key)), string(ev.Value))
} var rch clientv3.WatchChan
if cfg.Prefix {
rch = cli.Watch(context.Background(), cfg.Target, clientv3.WithPrefix(), clientv3.WithRev(resp.Header.Revision+))
} else {
rch = cli.Watch(context.Background(), cfg.Target, clientv3.WithRev(resp.Header.Revision+))
}
for {
select {
case <-gm.Chan():
l4g.Info("[discovery] watch %s close", cfg.Target)
return
case wresp := <-rch:
err := wresp.Err()
if err != nil {
l4g.Info("[discovery] watch %s response error: %s ", cfg.Target, err.Error())
gm.Close()
return
}
l4g.Debug("[discovery] watch %s response %+v", cfg.Target, wresp)
for _, ev := range wresp.Events {
if ev.IsCreate() {
target.Create(string(ev.Kv.Key), string(ev.Kv.Value))
} else if ev.IsModify() {
target.Modify(string(ev.Kv.Key), string(ev.Kv.Value))
} else if ev.Type == mvccpb.DELETE {
target.Delete(string(ev.Kv.Key))
} else {
l4g.Error("[discovery] no found watch type: %s %q", ev.Type, ev.Kv.Key)
}
}
}
}
}

Golang etcd服务注册与发现的更多相关文章

  1. 实现etcd服务注册与发现

    转载自:实现etcd服务注册与发现 0.1.目录结构 . ├── api │   └── main.go ├── common │   └── common.go ├── docker-compose ...

  2. 一个故事,一段代码告诉你如何使用不同语言(Golang&C#)提供相同的能力基于Consul做服务注册与发现

    目录 引言 什么是微服务 传统服务 微服务 什么是服务注册与服务发现 为什么要使用不同的语言提供相同的服务能力 服务协调器 服务注册 Golang C#(.NetCore3.1) 服务发现 通过Htt ...

  3. Docker+Jenkins持续集成环境(4):使用etcd+confd实现容器服务注册与发现

    前面我们已经通过jenkins+docker搭建了基本的持续集成环境,实现了服务的自动构建和部署,但是,我们遇到一个问题,jenkins构建出来的镜像部署后,需要通过ip:port去访问,有什么更好的 ...

  4. 分布式服务注册和发现consul 简要介绍

    Consul是HashiCorp公司推出的开源工具,用于实现分布式系统的服务发现与配置.与其他分布式服务注册与发现的方案,Consul的方案更"一站式",内置了服务注册与发现框 架 ...

  5. Docker+Consul+Registrator 实现服务注册与发现

    Docker+Consul+Registrator实现服务注册与发现 逻辑图 实现nginx节点自动化加入容器IP代理 1.三台Consul agent server作为高可用通过Consul Tem ...

  6. .netcore consul实现服务注册与发现-单节点部署

    原文:.netcore consul实现服务注册与发现-单节点部署 一.Consul的基础介绍     Consul是HashiCorp公司推出的开源工具,用于实现分布式系统的服务发现与配置.与其他分 ...

  7. 024.微服务架构之服务注册与发现(kubernetes / SpringCloud)

    微服务 微服务是一种架构模式,一种分布式的架构风格. 顾名思义,micro service,将一个庞大的单体应用拆分成若干个“微小”的服务,服务间通过进程通讯完成原本在单体应用中的调用. 其中必要的六 ...

  8. (8)ASP.NET Core3.1 Ocelot Consul服务注册与发现

    1.服务注册与发现(Service Discovery) ●服务注册:我们通过在每个服务实例写入注册代码,实例在启动的时候会先去注册中心(例如Consul.ZooKeeper.etcd.Eureka) ...

  9. Spring Cloud Alibaba Nacos 服务注册与发现功能实现!

    Nacos 是 Spring Cloud Alibaba 中一个重要的组成部分,它提供了两个重要的功能:服务注册与发现和统一的配置中心功能. 服务注册与发现功能解决了微服务集群中,调用者和服务提供者连 ...

随机推荐

  1. python之模块3

    RE模块使用方法 (1)finditer  返回迭代器 (2)search:只匹配第一个结果 import re res=re.search("\d+","djksf34 ...

  2. python gui 之 tkinter库

    http://blog.csdn.net/jcodeer?viewmode=contents http://tieba.baidu.com/p/3082739560 http://blog.sina. ...

  3. PowerBI发布到网页

    如果网页当中需要嵌入PowerBI的报表,可以在PowerBI当中生成链接,然后网页或者博客当中插入这一段html代码. 以下是PowerBI生产网页链接的示例,并且在博客的最后也插入了PowerBI ...

  4. C#多线程技术提高RabbitMQ消费吞吐率

    一.课程介绍 本次分享课程属于<C#高级编程实战技能开发宝典课程系列>中的第二部分,阿笨后续会计划将实际项目中的一些比较实用的关于C#高级编程的技巧分享出来给大家进行学习,不断的收集.整理 ...

  5. hdu5299 Circles Game

    Circles Game Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Tot ...

  6. iOS:UIButton扩大按钮的响应区域

    一.介绍 在开发中有时会遇见设计图里按钮设计的特别小,这时会用到手动扩大UIButton的响应范围 二.方式 下面有两个解决办法: 第一种方法:创建一个类目:UIButton+EnlargeTouch ...

  7. C#退出程序,退出任务管理器

    //窗体关闭之前 this.FormClosing += (s, r) => { System.Environment.Exit(0); }; //窗体关闭 this.Closed += (s, ...

  8. http重定向到https

    <?xml version="1.0" encoding="UTF-8"?> <configuration> <system.we ...

  9. mac下安装xampp、及其之上的组件安装

    由于mac下开发需要用到php7,这里是用的xampp集成开发版本.但是mac下安装xampp失败,失败信息如下: Error starting "XAMPP" stack: fa ...

  10. maven在Idea建立工程,运行出现Server IPC version 9 cannot communicate with client version 4错误

    问题的根源在于,工程当中maven dependencies里面的包,有个hadoop-core的包,版本太低,这样,程序里面所有引用到org.apache.hadoop的地方,都是低版本的,你用的是 ...