4. 参考 go 代码——服务注册与发现

简介: 4. 参考 go 代码——服务注册与发现

来源自etcd 笔记(09)— 基于 etcd 实现微服务的注册与发现_serviceregistry": { "type": "etcd", "endpoints":-CSDN博客

服务注册

go

复制代码

package main
import (
  "context"
  "fmt"
  "time"
  "github.com/coreos/etcd/clientv3"
)
// 服务注册对象
type ServiceRegister struct {
  client     *clientv3.Client
  kv         clientv3.KV
  lease      clientv3.Lease
  canclefunc func()
  key        string
  leaseResp     *clientv3.LeaseGrantResponse
  keepAliveChan <-chan *clientv3.LeaseKeepAliveResponse
}
// 初始化注册服务
func InitService(host []string, timeSeconds int64) (*ServiceRegister, error) {
  config := clientv3.Config{
    Endpoints:   host,
    DialTimeout: 5 * time.Second,
  }
  client, err := clientv3.New(config)
  if err != nil {
    fmt.Printf("create connection etcd failed %s\n", err)
    return nil, err
  }
  // 得到KV和Lease的API子集
  kv := clientv3.NewKV(client)
  lease := clientv3.NewLease(client)
  service := &ServiceRegister{
    client: client,
    kv:     kv,
    lease:  lease,
  }
  return service, nil
}
// 设置租约
func (s *ServiceRegister) setLease(timeSeconds int64) error {
  leaseResp, err := s.lease.Grant(context.TODO(), timeSeconds)
  if err != nil {
    fmt.Printf("create lease failed %s\n", err)
    return err
  }
  // 设置续租
  ctx, cancelFunc := context.WithCancel(context.TODO())
  leaseRespChan, err := s.lease.KeepAlive(ctx, leaseResp.ID)
  if err != nil {
    fmt.Printf("KeepAlive failed %s\n", err)
    return err
  }
  s.leaseResp = leaseResp
  s.canclefunc = cancelFunc
  s.keepAliveChan = leaseRespChan
  return nil
}
// 监听续租情况
func (s *ServiceRegister) ListenLeaseRespChan() {
  for {
    select {
    case leaseKeepResp := <-s.keepAliveChan:
      if leaseKeepResp == nil {
        fmt.Println("续租功能已经关闭")
        return
      } else {
        fmt.Println("续租成功")
      }
    }
  }
}
// 通过租约注册服务
func (s *ServiceRegister) PutService(key, val string) error {
  fmt.Printf("PutService key <%s> val <%s>\n", key, val)
  _, err := s.kv.Put(context.TODO(), key, val, clientv3.WithLease(s.leaseResp.ID))
  return err
}
// 撤销租约
func (s *ServiceRegister) RevokeLease() error {
  s.canclefunc()
  time.Sleep(2 * time.Second)
  _, err := s.lease.Revoke(context.TODO(), s.leaseResp.ID)
  return err
}
func main() {
  service, _ := InitService([]string{"127.0.0.1:12379", "127.0.0.1:22379", "127.0.0.1:32379"}, 5)
  service.setLease(10)
  defer service.RevokeLease()
  go service.ListenLeaseRespChan()
  err := service.PutService("/wohu", "http://localhost:8080")
  if err != nil {
    fmt.Printf("PutService failed %s\n", err)
  }
  // 使得程序阻塞运行,便于观察输出结果
  select {}
}

服务发现

go

复制代码

package main
import (
  "context"
  "fmt"
  "sync"
  "time"
  "github.com/coreos/etcd/clientv3"
  "github.com/coreos/etcd/mvcc/mvccpb"
)
// 客户端对象
type Client struct {
  client     *clientv3.Client
  kv         clientv3.KV
  lease      clientv3.Lease
  watch      clientv3.Watcher
  serverList map[string]string
  lock       sync.Mutex
}
// 初始化客户端对象
func InitClient(addr []string) (*Client, error) {
  conf := clientv3.Config{
    Endpoints:   addr,
    DialTimeout: 5 * time.Second,
  }
  client, err := clientv3.New(conf)
  if err != nil {
    fmt.Printf("create connection etcd failed %s\n", err)
    return nil, err
  }
  // 得到 KV 、Lease、 Watcher 的API子集
  kv := clientv3.NewKV(client)
  lease := clientv3.NewLease(client)
  watch := clientv3.NewWatcher(client)
  // 给客户端对象赋值
  c := &Client{
    client:     client,
    kv:         kv,
    lease:      lease,
    watch:      watch,
    serverList: make(map[string]string),
  }
  return c, nil
}
// 根据注册的服务名,获取服务实例的信息
func (c *Client) getServiceByName(prefix string) ([]string, error) {
  // 读取的时候带有 WithPrefix 选项,所以会读取该前缀所有的字段值
  resp, err := c.kv.Get(context.Background(), prefix, clientv3.WithPrefix())
  if err != nil {
    fmt.Printf("getServiceByName failed %s\n", err)
    return nil, err
  }
  // 返回的 resp 是多个字段值。需要遍历提取对应的 key value
  addrs := c.extractAddrs(resp)
  return addrs, nil
}
// 根据 etcd 的响应,提取服务实例的数组
func (c *Client) extractAddrs(resp *clientv3.GetResponse) []string {
  addrs := make([]string, 0)
  if resp == nil || resp.Kvs == nil {
    return addrs
  }
  for i := range resp.Kvs {
    if v := resp.Kvs[i].Value; v != nil {
      // 将 key  value 值保存在  ServiceList 表中
      c.SetServiceList(string(resp.Kvs[i].Key), string(resp.Kvs[i].Value))
      addrs = append(addrs, string(v))
    }
  }
  return addrs
}
// 设置 serverList
func (c *Client) SetServiceList(key, val string) {
  c.lock.Lock()
  defer c.lock.Unlock()
  // serverList 为初始化设置的本地 map 对象,由于考虑到多个 client 运行,所以需要加锁控制
  c.serverList[key] = string(val)
  fmt.Println("set data key :", key, "val:", val)
}
// 删除本地缓存的服务实例信息
func (c *Client) DelServiceList(key string) {
  c.lock.Lock()
  defer c.lock.Unlock()
  delete(c.serverList, key)
  fmt.Println("del data key:", key)
  newRes, err := c.getServiceByName(key)
  if err != nil {
    fmt.Printf("getServiceByName failed %s\n", err)
  } else {
    fmt.Printf("get  key %s", key, " current val is: %v\n", newRes)
  }
}
// 获取服务实例信息
func (c *Client) GetService(prefix string) ([]string, error) {
  if addrs, err := c.getServiceByName(prefix); err != nil {
    panic(err)
  } else {
    fmt.Println("get service ", prefix, " for instance list: ", addrs)
    go c.watcher(prefix)
    return addrs, nil
  }
}
// 监控指定键值对的变更
func (c *Client) watcher(prefix string) {
  watchRespChan := c.watch.Watch(context.Background(), prefix, clientv3.WithPrefix())
  for watchResp := range watchRespChan {
    for _, event := range watchResp.Events {
      switch event.Type {
      case mvccpb.PUT: // 写入的事件
        c.SetServiceList(string(event.Kv.Key), string(event.Kv.Value))
      case mvccpb.DELETE: // 删除的事件
        c.DelServiceList(string(event.Kv.Key))
      }
    }
  }
}
func main() {
  /*
    先创建 etcd 连接,构建 Client 对象,随后获取指定的服务 /wohu 实例信息;
    最后监测 wohu 服务实例的变更事件,根据不同的事件产生不同的行为。
  */
  c, _ := InitClient([]string{"127.0.0.1:12379", "127.0.0.1:22379", "127.0.0.1:32379"})
  c.GetService("/wohu")
  // 使得程序阻塞运行,模拟服务的持续运行
  select {}
}


相关文章
|
2天前
|
监控 算法 Go
Golang深入浅出之-Go语言中的服务熔断、降级与限流策略
【5月更文挑战第4天】本文探讨了分布式系统中保障稳定性的重要策略:服务熔断、降级和限流。服务熔断通过快速失败和暂停故障服务调用来保护系统;服务降级在压力大时提供有限功能以保持整体可用性;限流控制访问频率,防止过载。文中列举了常见问题、解决方案,并提供了Go语言实现示例。合理应用这些策略能增强系统韧性和可用性。
52 0
|
2天前
|
Go 索引
掌握Go语言:Go语言范围,优雅遍历数据结构,简化代码操作实战解析(24)
掌握Go语言:Go语言范围,优雅遍历数据结构,简化代码操作实战解析(24)
|
2天前
|
运维 网络协议 安全
长连接网关技术专题(十):百度基于Go的千万级统一长连接服务架构实践
本文将介绍百度基于golang实现的统一长连接服务,从统一长连接功能实现和性能优化等角度,描述了其在设计、开发和维护过程中面临的问题和挑战,并重点介绍了解决相关问题和挑战的方案和实践经验。
116 1
|
2天前
|
负载均衡 Java 中间件
使用Go语言构建高性能Web服务
Go语言作为一种快速、高效的编程语言,其在构建高性能Web服务方面具有独特优势。本文将探讨如何利用Go语言开发和优化Web服务,以实现更高的性能和可伸缩性。
|
2天前
|
负载均衡 算法 Go
Golang深入浅出之-Go语言中的服务注册与发现机制
【5月更文挑战第4天】本文探讨了Go语言中服务注册与发现的关键原理和实践,包括服务注册、心跳机制、一致性问题和负载均衡策略。示例代码演示了使用Consul进行服务注册和客户端发现服务的实现。在实际应用中,需要解决心跳失效、注册信息一致性和服务负载均衡等问题,以确保微服务架构的稳定性和效率。
20 3
|
2天前
|
存储 负载均衡 监控
【Go 语言专栏】构建高可靠性的 Go 语言服务架构
【4月更文挑战第30天】本文探讨了如何利用Go语言构建高可靠性的服务架构。Go语言凭借其高效、简洁和并发性能,在构建服务架构中备受青睐。关键要素包括负载均衡、容错机制、监控预警、数据存储和服务治理。文章详细阐述了实现这些要素的具体步骤,通过实际案例分析和应对挑战的策略,强调了Go语言在构建稳定服务中的作用,旨在为开发者提供指导。
|
2天前
|
缓存 监控 测试技术
【Go语言专栏】使用Go语言构建高性能Web服务
【4月更文挑战第30天】本文探讨了使用Go语言构建高性能Web服务的策略,包括Go语言在并发处理和内存管理上的优势、基本原则(如保持简单、缓存和并发控制)、标准库与第三方框架的选择、编写高效的HTTP处理器、数据库优化以及性能测试和监控。通过遵循最佳实践,开发者可以充分利用Go语言的特性,构建出高性能的Web服务。
|
2天前
|
运维 Serverless Go
Serverless 应用引擎产品使用之在阿里云函数计算中,Go语言的函数计算服务Go程序没有正确打包如何解决
阿里云Serverless 应用引擎(SAE)提供了完整的微服务应用生命周期管理能力,包括应用部署、服务治理、开发运维、资源管理等功能,并通过扩展功能支持多环境管理、API Gateway、事件驱动等高级应用场景,帮助企业快速构建、部署、运维和扩展微服务架构,实现Serverless化的应用部署与运维模式。以下是对SAE产品使用合集的概述,包括应用管理、服务治理、开发运维、资源管理等方面。
25 0
|
2天前
|
安全 编译器 Serverless
掌握Go语言:深入Go语言常量:代码稳定的关键(10)
掌握Go语言:深入Go语言常量:代码稳定的关键(10)
|
2天前
|
安全 中间件 Go
Go语言Web服务性能优化与安全实践
【2月更文挑战第21天】本文将深入探讨Go语言在Web服务性能优化与安全实践方面的应用。通过介绍性能优化策略、并发编程模型以及安全加固措施,帮助读者理解并提升Go语言Web服务的性能表现与安全防护能力。