//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. flask 跨域问题

    在Flask开发RESTful后端时,前端请求会遇到跨域的问题.下面是解决方法.Python版本:3.5.1 下载flask_cors包 pip install flask-cors使用flask_c ...

  2. D. Cutting Out 二分

    题意是给你n个数字的序列,让你从中找含k个数字的序列,要求这k个数字要尽可能多次的从n个数字的序列中减去. 解法就是从1到n,二分查找可以删除的最大次数. http://codeforces.com/ ...

  3. 开源流媒体服务器SRS学习笔记(3) - HTTPCallback实现安全认证

    按上回继续,安全论证是绝大多数应用的基本要求,如果任何人都能无限制的发布/播放视频,显然不适合.SRS中可以通过HTTPCallback机制来实现,参考下面的配置: ... vhost __defau ...

  4. pygame-KidsCanCode系列jumpy-part6-主角挂掉重新开始

    游戏的虚拟世界中,最让人happy的一个因素就是主角挂了,而且重来,只要restart就行了,不象现实中人的生命只有1次.回顾上节的效果,如果方块向下落时,挡板没接住,整个游戏就跪了: 如果我们希望方 ...

  5. IPAddress.Any 解决本地ip和服务器ip切换问题

    IPAddress.Any表示本机ip,换言之,如果服务器绑定此地址,则表示侦听本机所有ip对应的那个端口(本机可能有多个ip或只有一个ip)IPAddress.Any微软给出的解释是:Provide ...

  6. StompJS使用文档总结

    STOMP即Simple (or Streaming) Text Orientated Messaging Protocol,简单(流)文本定向消息协议,它提供了一个可互操作的连接格式,允许STOMP ...

  7. VUE温习:nextTick、$refs、嵌套路由、keep-alive缓存、is特性、路由属性用法、路由钩子函数

    一.$nextTick 1.vue的dom执行异步更新,只要观察到数据变化,vue将开启一个队列,并缓冲在同一事件循环中发生的所有数据改变. 2.vue.$nextTick(cb),数据发生变化,更新 ...

  8. Elasticsearch集群优化

    版本配置: ES版本:6.2.4 OS内存64G. 一.参数配置: ES jvm内存31G. vi /etc/sysctl.conf vm.swappiness = 1 elasticsearch.y ...

  9. Fiddler Composer 模拟post请求

    在模拟post请求的时候,发现服务器端无法接收post参数 发现原来的请求表头的设置问题加上表头 Content-Type: application/x-www-form-urlencoded 后正常

  10. ionic 视图滚动到顶部

    问题描述 进入页面, 滚动到底部 点击一个连接, 当前视图内容更新了 内容滚动到了上次的位置导致底部的内容没显示 需要控制自动回滚到顶部 <ion-content delegate-handle ...