【Go语言实战】 (16) gRPC 集成 ETCD 进行服务注册

文章目录

写在前面

本文采用的 ETCD 版本库是 ​ &#x200B;<strong>go.etcd.io/etcd/client/v3</strong>&#x200B;​​
采用的 gRPC 版本库是 ​​ &#x200B;<strong>google.golang.org/grpc</strong>&#x200B;

在Go语言的RPC框架中,gRPC 是比较原生的,并 没有集成 ETCD 服务发现的集成,需要我们去稍微封装一下。而像 micro 框架这种封装性比较好的就有集成 ETCD、consul 等等的服务发现功能,就直接调用就行了。

本文实战例子源码在 ​ &#x200B;<strong>https://github.com/CocaineCong/gRPC-todoList</strong>&#x200B;

【Go语言实战】 (16) gRPC 集成 ETCD 进行服务注册

1. 服务实例定义

  • 定义我们所需要注入进ETCD的服务结构体
type Server struct {    Name    string json:"name"    Addr    string json:"addr"    // 地址    Version string json:"version" // 版本    Weight  int64  json:"weight"  // 权重}

Name:名字为服务的名字(用来进行服务的发现)
Addr:服务的地址(存储服务地址)
Version:服务的版本(方便服务的版本迭代)
Weight:服务的权重(后续用来降级熔断)

  • 定义服务名字前缀的函数
func BuildPrefix(server Server) string {    if server.Version == "" {        return fmt.Sprintf("/%s/", server.Name)    }    return fmt.Sprintf("/%s/%s/", server.Name, server.Version)}
  • 定义注册的地址函数
func BuildRegisterPath(server Server) string {    return fmt.Sprintf("%s%s", BuildPrefix(server), server.Addr)}
  • 将值反序列化成一个注册 Server 服务
func ParseValue(value []byte) (Server, error) {    server := Server{}    if err := json.Unmarshal(value, &server); err != nil {        return server, err    }    return server, nil}
  • 分割路径,后续用作 Server 地址的更新
func SplitPath(path string) (Server, error) {    server := Server{}    strs := strings.Split(path, "/")    if len(strs) == 0 {        return server, errors.New("invalid path")    }    server.Addr = strs[len(strs)-1]    return server, nil}
  • 判断这个服务地址是否已经存在,防止服务访问冲突
func Exist(l []resolver.Address, addr resolver.Address) bool {    for i := range l {        if l[i].Addr == addr.Addr {            return true        }    }    return false}
  • 移除服务
func Remove(s []resolver.Address, addr resolver.Address) ([]resolver.Address, bool) {    for i := range s {        if s[i].Addr == addr.Addr {            s[i] = s[len(s)-1]            return s[:len(s)-1], true        }    }    return nil, false}

2. 服务实例注册

  • 定义服务实例的实例,用来存储全部的实例信息,并且维持各个服务之间的执行,防止宕机等情况
type Register struct {    EtcdAddrs   []string    DialTimeout int    closeCh     chan struct{}    leasesID    clientv3.LeaseID    keepAliveCh chan *clientv3.LeaseKeepAliveResponse    srvInfo Server    srvTTL  int64    cli     *clientv3.Client    logger  *logrus.Logger}
  • 创建一个注册对象
func NewRegister(etcdAddrs []string, logger *logrus.Logger) *Register {    return &Register{        EtcdAddrs:   etcdAddrs,        DialTimeout: 3,        logger:      logger,    }}
  • 注册服务到 ETCD 中
func (r *Register) Register(srvInfo Server, ttl int64) (chan struct{}, error) {    var err error    if strings.Split(srvInfo.Addr, ":")[0] == "" {        // 判断服务地址的正确性        return nil, errors.New("invalid ip address")    }    // 对服务进行注册    if r.cli, err = clientv3.New(clientv3.Config{        Endpoints:   r.EtcdAddrs,        DialTimeout: time.Duration(r.DialTimeout) * time.Second,    }); err != nil {        return nil, err    }    r.srvInfo = srvInfo  // 服务信息的注册    r.srvTTL = ttl         // 服务的存活时间    if err = r.register(); err != nil {        return nil, err    }    // 初始化一个切片来判断这个服务连接是否关闭    r.closeCh = make(chan struct{})     // 异步进行心跳检测    go r.keepAlive()    return r.closeCh, nil}

这里我们要先说明一个名词: 租约

ETCD的 ​ &#x200B;Lease&#x200B;​​ 租约,它类似 ​ &#x200B;TTL&#xFF08;Time To Live&#xFF09;&#x200B;​, 用于 etcd 客户端与服务端之间进行活性检测

在到达 TTL 时间之前,etcd 服务端不会删除相关租约上绑定的键值对;超过 TTL 时间,则会删除。因此我们需要在到达 TTL 时间之前续租,以实现客户端与服务端之间的保活。

func (r *Register) register() error {    //设置超时时间,访问etcd有超时控制    ctx, cancel := context.WithTimeout(context.Background(), time.Duration(r.DialTimeout)*time.Second)    defer cancel()    // 注册一个新的租约    leaseResp, err := r.cli.Grant(ctx, r.srvTTL)     if err != nil {        return err    }        // 赋值租约的ID    r.leasesID = leaseResp.ID    // 对这个 cli 进行心跳检测    if r.keepAliveCh, err = r.cli.KeepAlive(context.Background(), r.leasesID); err != nil {        return err    }        data, err := json.Marshal(r.srvInfo)    if err != nil {        return err    }    // 将服务写到 ETCD 中    _, err = r.cli.Put(context.Background(), BuildRegisterPath(r.srvInfo), string(data), clientv3.WithLease(r.leasesID))    return err}
  • 关闭服务连接
func (r *Register) Stop() {    r.closeCh  struct{}{}}
  • 删除节点
func (r *Register) unregister() error {    _, err := r.cli.Delete(context.Background(), BuildRegisterPath(r.srvInfo))    return err}
  • 存活检测
func (r *Register) keepAlive() {    ticker := time.NewTicker(time.Duration(r.srvTTL) * time.Second)    for {        select {        case r.closeCh: // 是否存在这个服务            if err := r.unregister(); err != nil {                r.logger.Error("unregister failed, error: ", err)            }            // 撤销租约            if _, err := r.cli.Revoke(context.Background(), r.leasesID); err != nil {                r.logger.Error("revoke failed, error: ", err)            }        case res := r.keepAliveCh:            if res == nil {                if err := r.register(); err != nil {                    r.logger.Error("register failed, error: ", err)                }            }        case ticker.C:            if r.keepAliveCh == nil {                if err := r.register(); err != nil {                    r.logger.Error("register failed, error: ", err)                }            }        }    }}
  • 获取注册服务的信息
func (r *Register) GetServerInfo() (Server, error) {    resp, err := r.cli.Get(context.Background(), BuildRegisterPath(r.srvInfo))    if err != nil {        return r.srvInfo, err    }    server := Server{}    if resp.Count >= 1 {        if err := json.Unmarshal(resp.Kvs[0].Value, &server); err != nil {            return server, err        }    }    return server, err}

3. 使用示例

  • 服务注册
etcdRegister := discovery.NewRegister(etcdAddress, logrus.New())
  • 定义一个Node存放服务信息
userNode := discovery.Server{        Name: viper.GetString("server.domain"),        Addr: grpcAddress,    }
  • 注册
if _, err := etcdRegister.Register(userNode, 10); err != nil {    panic(fmt.Sprintf("start server failed, err: %v", err))}

Original: https://blog.51cto.com/u_15314183/5457223
Author: 小生凡一
Title: 【Go语言实战】 (16) gRPC 集成 ETCD 进行服务注册

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/516636/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • HDFS机架感知

    Hadoop版本:2.9.2 什么是机架感知 通常大型 Hadoop 集群是以机架的形式来组织的,同一个机架上的不同节点间的网络状况比不同机架之间的更为理想,NameNode 设法…

    大数据 2023年5月26日
    0111
  • docker安装

    安装环境依赖包 yum install yum-utils device-mapper-persistent-data lvm2 其中 yum utils 提供了 yum-conf…

    大数据 2023年5月29日
    090
  • docker的/var/lib/docker目录迁移

    1 停止docker服务 2 创建新的docker目录 3 迁移/var/lib/docker 4 修改配置路径 方法1:通过建立软连接,把/var/lib/docker 移动到其…

    大数据 2023年5月29日
    099
  • 大数据学习笔记——————-(29)

    第五部分HBASE学习 该部分,主要对HIV学习笔记进行记录,学习资料翻译自《hbase_tutorial.pdf》 该部分为10个章节来学习HBASE: Ø 第29章 HBASE…

    大数据 2023年5月26日
    098
  • fastapi操作sql以及jwt

    文章目录 一、pandas是什么? 二、使用步骤 * 1.引入库 2.读入数据 总结 使用fastapi * install File Structure sql * fastap…

    大数据 2023年11月11日
    062
  • KafkaManager对offset的两种管理方式

    OffsetManager主要提供对offset的保存和读取,每个broker都有一个OffsetManager实例,kafka管理topic的偏移量有2种方式: 1、Zookee…

    大数据 2023年5月28日
    096
  • 【Docker异常】docker-compose up throw UnicodeDecodeError: ‘ascii’ codec can’t decode byte 0xe3 in position 97: ordinal not in range(128)

    一、异常 [root@VM_0_4_centos prometheus-grafana-linux]# docker-compose up Traceback (most rece…

    大数据 2023年5月29日
    0102
  • Java基础——List集合

    List集合 Collection 层次结构中的根接口。一些 collection 允许有重复的元素,而另一些则不允许。一些 collection 是有序的,而另一些则是无序的。J…

    大数据 2023年6月3日
    098
  • Sqlite数据库超详细命令介绍

    一,创建数据库 命令:sqlite3 stu.db (stu为数据库名) 二,sqlite存储类 存储类 三,语法 1.创建表:create table stu (id int p…

    大数据 2023年11月11日
    044
  • Docker与微服务

    镜像下载、域名解析、时间同步请点击阿里云开源镜像站 一、镜像 镜像是一种轻量级、可执行的独立软件包,它包含运行某个软件所需的所有内容,我们把应用程序和配置依赖打包形成一个可交付的运…

    大数据 2023年5月27日
    0160
  • Kafka Manager

    这里记录下安装过程,以及当时遇到的一些问题。 依赖项 kafka manager依赖两个组件: 最新的kafka manager依赖java8,低版本的我没有尝试过。这里不对配置j…

    大数据 2023年5月28日
    097
  • jenkins docker push脚本

    #!/bin/bash docker -v #私有库url repositryUrl=’192.168.7.52:5000′ #获取项目版本号作为镜像的tag version=aw…

    大数据 2023年5月29日
    0118
  • 医院HIS(LIS)系统时钟同步(NTP网络时间服务器)技术详解

    医院HIS(LIS)系统时钟同步(NTP网络时间服务器)技术详解 医院HIS(LIS)系统时钟同步(NTP网络时间服务器)技术详解 京准电子科技官微——ahjzsz NTP网络时间…

    大数据 2023年6月3日
    099
  • python中,Microsoft Visual C++ 14.0 or greater is required问题解决方案

    今天在写一个小程序,安装依赖的时候发现这个问题,平时都是直接安装Visual Studio解决,但是这个安装太大了,所以解决看看怎么安装是最方便的,最容易解决的。 下面这个就是出现…

    大数据 2023年6月2日
    0112
  • Hadoop安装配置

    Hadoop安装配置 原创 冷酷冰川狼2022-06-20 17:13:19©著作权 文章标签 Hadoop安装配置 hadoop 主机名 安装配置 文章分类 Hadoop 大数据…

    大数据 2023年5月26日
    0106
  • Linux权限

    Linux系统上对文件的权限有着严格的控制,如果想对某个文件执行某种操作,必须具有对应的权限才可执行成功。 Linux下文件的权限类型一般包括 读,写,执行。对应字母为 r、w、x…

    大数据 2023年5月27日
    0111
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球