//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. JS膏集03

    JS膏集03 1.复习 原型: * 在构造函数中有一个属性:prototype,是原型,也是一个对象,程序员使用的 * 在实例对象中有一个属性:__proto__,是原型,也是一个对象,浏览器使用的, ...

  2. web项目上传图片需要刷新文件夹才能显示

    问题:上传图片到指定的目录,比如上传到项目文件夹中E:\briup\workspace\Buddhism\WebContent\userImage,但是上传呢完毕之后需要手动刷新该文件夹,网页上才能正 ...

  3. 近期 Unity 提交苹果审核被拒的问题

    游戏提交苹果审核,被打回.在 bugly 上没有查到崩溃信息,苹果给了 crash 日志也说明. 拒绝原因如下: Your app crashed on iPad or iPhone running ...

  4. x264阅读记录-3

      14. x264_macroblock_encode函数-1 这个函数主要根据已经选定的模式来对宏块残差进行编码. )如果是P_SKIP模式,那么调用x264_macroblock_encode_ ...

  5. C# Winform使用Windows Media Player播放多媒体整理

    一.简单使用示例步骤 1.添加Windows Media Player 组件当前是系统的 Com组件 工具箱>右键“选择项”>选择Com组件 2.控件拖拽到桌面,使用 private vo ...

  6. 13、spark-submit

               

  7. iOS:定制自适应大小的透明吐司弹框

    一.简单介绍 创建一个吐司消息的黑色透明弹框,可以根据消息长短自适应大小. 可以手动创建手动显示手动关闭,也可以手动创建自动显示自动关闭. 简单好用. 二.代码使用 .h文件 // // LiveHU ...

  8. MySql.Data.dll的版本

    在.Net下访问Mysql,先是用6.4.4,老有问题,也不知道哪个版本可以用,查询官网 https://dev.mysql.com/doc/connector-net/en/connector-ne ...

  9. Netty实战 - 1. 基本概念

    1. Netty简介 Netty是由JBOSS提供的一个java开源框架.它提供异步的.事件驱动的网络应用程序框架和工具,用以快速开发高性能.高可靠性的网络服务器和客户端程序.Netty是一个基于NI ...

  10. R语言︱处理缺失数据&&异常值检验、离群点分析、异常值处理

    在数据挖掘的过程中,数据预处理占到了整个过程的60% 脏数据:指一般不符合要求,以及不能直接进行相应分析的数据 脏数据包括:缺失值.异常值.不一致的值.重复数据及含有特殊符号(如#.¥.*)的数据 数 ...