Go微服务实战——服务的注册与获取(nacos做服务注册中心)

背景

随着访问量的逐渐增大,单体应用结构渐渐不满足需求,在微服务出现之后引用被拆分为一个个的服务,服务之间可以互相访问。初期服务之间的调用只要知道服务地址端口即可,而服务会出现增减、故障、升级等变化导致端口和ip也变化,被调用者的变化也会导致调用者变化。这样很不方便。

注册中心就诞生了,注册中心就像DNS服务器,注册中心是C/S架构,服务调用者通过Client调用服务名称,被调用者通过Client上传服务名称和ip并发送心跳检测该服务的健康状态。注册中心为server端保存服务名称和服务的ip地址及端口,这样不论被调用者服务如何变化,只要服务名称不变,调用者都不说影响。

常见的注册中心:

  • etcd
  • consul
  • eureka
  • nacos
  • zookeeper

go实现nacos注册中心

Nacos的核心API中定义了两个接口NamingService和ConfigService。服务注册与发现围绕着NamingService展开,而配置管理则围绕着ConfigService展开。

Nacos的4个核心特性:服务发现和服务健康监测、动态配置服务、动态DNS服务、服务及其元数据管理。

在这里插入图片描述

作为注册中心的功能来说,Nacos提供的功能与其他主流框架很类似,基本都是围绕服务实例注册、实例健康检查、服务实例获取这三个核心来实现的。

在这里插入图片描述

nacos注册中心基本流程:

  • 服务实例启动将自身注册到Nacos注册中心,随后维持与注册中心的心跳;
  • 心跳维持策略为每5秒向Nacos Server发送一次心跳,并携带实例信息(服务名、实例IP、端口等);
  • Nacos Server也会向Client主动发起健康检查,支持TCP/Http;
  • 15秒内无心跳且健康检查失败则认为实例不健康,如果30秒内健康检查失败则剔除实例;
  • 服务消费者通过注册中心获取实例,并发起调用;

其中服务发现支持两种场景:第一,服务消费者直接向注册中心发送获取某服务实例的请求,注册中心返回所有可用实例,但一般不推荐此种方式;第二、服务消费者向注册中心订阅某服务,并提交一个监听器,当注册中心中服务发生变化时,监听器会收到通知,消费者更新本地服务实例列表,以保证所有的服务均可用。

nacos数据模型

Nacos数据模型的Key由三元组唯一确定,Namespace默认是空串,公共命名空间(public),分组默认是DEFAULT_GROUP

在这里插入图片描述
在这里插入图片描述Nacos基于namespace的设计是为了做多环境以及多租户数据(配置和服务)隔离的。如果用户有多套环境(开发、测试、生产等环境)。

一个服务模型如下:

在这里插入图片描述

在这里插入图片描述

图片来源官方网站:nacos.io

服务注册

go nacos SDK

首先需要有一个rpc框架例如Zero,kit,grpc,kitex等,生成rpc服务的代码来模拟服务注册与获取。如下所示使用kitex框架生成rpc service,这里使用nacos官方go语言的sdk和rpc框架无关联,任选框架即可。

本项目demo

server端:
在这里插入图片描述

client直接连接:

import ("github.com/cloudwego/kitex/client""order/rpc/orderclient"
)func Client() orderclient.RPCClient {rpcClient, err := orderclient.NewRPCClient("orderserver", client.WithHostPorts("192.168.5.118:10000"))if err != nil {panic(err)}return rpcClient
}

在上述的代码中添加nacos配置中心,首先区别存在一个运行的nacos服务,如下:

在这里插入图片描述

go连接nacos实现服务注册参考go-nacos-example

公网域名连接参考

包装工具库简化连接注册配置的方法

服务注册在启动类连接nacos注册中心,注册服务,如下:

package mainimport (..."github.com/flairamos/go-component/nacos"...
)
func main() {opts := kitexInit()svr := orderservice.NewServer(new(OrderServiceImpl), opts...)param := vo.RegisterInstanceParam{Ip:          "127.0.0.1",Port:        8848,Enable:      true,Healthy:     true,Weight:      10,Metadata:    map[string]string{"version": "1.0"},ClusterName: "test",GroupName:   "dev_food_platform",Ephemeral:   true,ServiceName: "test_app",}config := nacos.DefaultClient("public", "mysql", "dev_food_platform", nil)service := nacos.RegisterService(config, param)if !service {log.Println("nacos register failed")return}log.Println("nacos register success")err := svr.Run()if err != nil {klog.Error(err.Error())}
}

代码中的nacos是小编封装的包,sdk源码参考nacos-go-sdk,如下:

// 官方源码
success, err := namingClient.RegisterInstance(vo.RegisterInstanceParam{Ip:          "10.0.0.11",Port:        8848,ServiceName: "demo.go",Weight:      10,Enable:      true,Healthy:     true,Ephemeral:   true,Metadata:    map[string]string{"idc":"shanghai"},ClusterName: "cluster-a", // 默认值DEFAULTGroupName:   "group-a",   // 默认值DEFAULT_GROUP
})

启动服务:
在这里插入图片描述
nacso注册中心查看:
在这里插入图片描述

注册配置如下:

"github.com/nacos-group/nacos-sdk-go/vo"vo.RegisterInstanceParam{Ip:          "127.0.0.1",Port:        8848,Enable:      true,Healthy:     true,Weight:      10,Metadata:    map[string]string{"version": "1.0"},ClusterName: "test",GroupName:   "dev.food_platform",Ephemeral:   true,ServiceName: "test_app",}

在这里插入图片描述
在上述过程中服务已经注册到nacso注册中心了,完成了第一步。

服务健康检查

nacos服务健康检查是客户端进行的,通过配置来开启,无需手敲发送心跳,判断活跃,更新服务,剔除亚健康实例等代码。

Nacos中临时实例会定时发送心跳维持活性,基本的健康检查流程基本如下:Nacos客户端会维护一个定时任务,每隔5秒发送一次心跳请求,以确保自己处于活跃状态。Nacos服务端在15秒内如果没收到客户端的心跳请求,会将该实例设置为不健康,在30秒内没收到心跳,会将这个临时实例摘除。

这些都不需要开发者手敲代码,在nacos中有一个github.com/nacos-group/nacos-sdk-go/vo.RegisterInstanceParam配置参数,在服务注册的时候需要用到,其中Ephemeral属性表示临时实例,将其设置为true则开启服务心跳模式。

这个维护心跳的模式的线程是基于主函数的,主要主函数服务一直运行,发送心跳就会一直进行下去。

如果把他设置为false,如下:

在这里插入图片描述

不管服务有没有正常启动这个记录都在注册中心,只能通过代码注销实例:


success, err := namingClient.DeregisterInstance(vo.DeregisterInstanceParam{Ip:          "127.0.0.1",Port:        8848,ServiceName: "test_app",Ephemeral:   true,Cluster:     "test", // 默认值DEFAULTGroupName:   "dev_food_platform",   // 默认值DEFAULT_GROUP
})

多服务实例注册

注意区分服务与实例的区别,一个服务包含若干实例,如user服务可能在不同主机注册那么该服务包含若干ip地址。

如果只有一个实例,该实例宕机系统就崩了,多实例通过服务名获取健康实例提高了系统稳定性。

为了系统的稳定性服务一般都是集群部署,因此在注册的时候可以看到,如下配置:


success, err := namingClient.RegisterInstance(vo.RegisterInstanceParam{Ip:          "10.0.0.11",Port:        8848,ServiceName: "demo.go",Weight:      10,Enable:      true,Healthy:     true,Ephemeral:   true,Metadata:    map[string]string{"idc":"shanghai"},ClusterName: "cluster-a", // 默认值DEFAULTGroupName:   "group-a",   // 默认值DEFAULT_GROUP
})

在这里插入图片描述
在nacos注册时首先由配置信息确定集群Cluster,再去更具服务实例的信息查找如namespace,group,servicename等信息定位服务。注册阶段没有dataId,dataid在注册阶段就是servicename。

多个服务注册重复调用注册方法即可:


success, err := namingClient.RegisterInstance(vo.RegisterInstanceParam{Ip:          "10.0.0.11",Port:        8848,ServiceName: "demo.go",Weight:      10,Enable:      true,Healthy:     true,Ephemeral:   true,Metadata:    map[string]string{"idc":"shanghai"},ClusterName: "cluster-a", // 默认值DEFAULTGroupName:   "group-a",   // 默认值DEFAULT_GROUP
})

注意事项:

  • 没有使用集群(ClusterName),自定义字符串即可但同一名称的服务要保持一致。
  • 注册时的配置ip与端口并没有验证,可以随意写,在不同机器上需要获取实际值。
  • 不同主机注册同一服务服务名称(ServiceName)和组(GroupName)必须一致

开发的不同阶段以namespace区分如dev,prod;不同的项目以GroupName区分,如order,user;不同的实例以ServiceName区分,如demo服务可能有,192.168.5.117:8000与192.168.5.118:8000两个实例。

同一服务不同实例的表现形式如下:

在这里插入图片描述
在这里插入图片描述
记录了服务的两个实例的信息。

获取服务

rpc服务也是基于c/s架构的,在之前的章节中通过nacos client注册服务,该服务处于运行状态nacos server会维护该服务的活性。接下来就是服务的获取了。

服务获取也是基于nacos client的,而且是在rpc 的client。在很多的rpc框架如grpc等框架rpc的客户端与服务端都是生成式的,通过生成的方法创建服务端与客户端。这种方式也被称为直连方式。如下:

import ("github.com/cloudwego/kitex/client""order/rpc/orderclient"
)
// order/rpc/orderclient是框架生成的创建客户端对的目录
// 调用生成方法创建客户端实例
// 传入服务名与ip端口地址
func Client() orderclient.RPCClient {rpcClient, err := orderclient.NewRPCClient("orderserver", client.WithHostPorts("192.168.5.118:10000"))if err != nil {panic(err)}return rpcClient
}

其实上述代码的服务名没起作用,传入空字符串也可以,关键是ip与端口。

这种直连的方式弊端也很明显,打那个传入的ip:port宕机后,该客户端也无法使用,其所在服务也受影响。当然也可以将所有ip配置,形成ip列表,使用一些算法获取其中一个作为参数创建客户端实例等一些方法解决,但是了注册中心是目前最好的方法。

引入nacos后,任然需要使用生成的方法,但是此时地址就不在是通过ip:port获取了,而是nacos client通过服务名称获取,nacos client通过namespace,servicename,cluster,group等信息获取一个服务或者一个服务实例列表,当然最好是获取一个健康的正在运行的实例,将其ip:port传递,即完成调用。

这样一来,服务调用者不用保存任何ip信息,提前约定服务名称即可,十分方便。

在这里插入图片描述
图片显示的是nacos-go-sdk提供的获取实例的方法。最好直接使用获取一个健康的实例的方法(免去开发者筛选),如下:

// SelectOneHealthyInstance将会按加权随机轮询的负载均衡策略返回一个健康的实例
// 实例必须满足的条件:health=true,enable=true and weight>0
instance, err := namingClient.SelectOneHealthyInstance(vo.SelectOneHealthInstanceParam{ServiceName: "demo.go",GroupName:   "group-a",             // 默认值DEFAULT_GROUPClusters:    []string{"cluster-a"}, // 默认值DEFAULT
})
// "github.com/nacos-group/nacos-sdk-go/model"
// model.Instance
type Instance struct {Valid       bool              `json:"valid"`Marked      bool              `json:"marked"`InstanceId  string            `json:"instanceId"`Port        uint64            `json:"port"`Ip          string            `json:"ip"`Weight      float64           `json:"weight"`Metadata    map[string]string `json:"metadata"`ClusterName string            `json:"clusterName"`ServiceName string            `json:"serviceName"`Enable      bool              `json:"enabled"`Healthy     bool              `json:"healthy"`Ephemeral   bool              `json:"ephemeral"`
}

获取的实例对象如上,将其ip与端口作为参数传递给rpc client即可。


import ("fmt""github.com/cloudwego/kitex/client""github.com/flairamos/go-component/nacos""github.com/nacos-group/nacos-sdk-go/vo""order/rpc/orderclient"
)// 直接连接
func Client() orderclient.RPCClient {rpcClient, err := orderclient.NewRPCClient("orderserver", client.WithHostPorts("192.168.5.118:10000"))if err != nil {panic(err)}return rpcClient
}// nacos注册中心连接
func ClientFormNacos() orderclient.RPCClient {var param = vo.SelectOneHealthInstanceParam{Clusters:    []string{"test"},ServiceName: "test_app1",GroupName:   "dev_food_platform",}config := nacos.DefaultClient("public", "mysql", "dev_food_platform", nil)instance, err := nacos.SelectOneHealthyInstance(config, param)if err != nil {panic(err)}addr := fmt.Sprintf("%s:%d", instance.Ip, instance.Port)rpcClient, err := orderclient.NewRPCClient("test_app1", client.WithHostPorts(addr))return rpcClient
}

在这里插入图片描述
本项目demo

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/548886.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Mesh网格obj文件构成解析

众所周知,Mesh网格是三维重建的常用手法,通过顶点-三角面的形式来完成对三维物体的表达。其中,最常见的Mesh网格文件格式就是obj格式。看起来复杂的三维形状其实在数值表示上是很简单的,大家跟我一起来做个小实验就好:…

【考研数学】全年保姆级规划+资料选择

直接跟考研课学即可 跟宋浩学第一遍说是基础,但宋浩的课程毕竟针对的是大学期末考试,基础知识方面讲的可能没有考研课程全面,毕竟大学课程的授课时间和考核要求不同于考研。 备考之前对自己做一个评估,每一个要准备的科目和其中…

安卓14谷歌GooglePlay商店安装 谷歌基础服务打开 GMS服务开关 谷歌三件套安装

环境:最新的安卓手机已经内置了谷歌三件套例如小米手机打开Go安装器可以看到结果,但是为什么没有Play 商店的桌面进入图标呢,因为默认厂商把图标给隐藏了,只需要重新打开即可。 提示安装 Google 服务后系统会增加显著的耗电&…

力扣思路题:最长特殊序列1

int findLUSlength(char * a, char * b){int alenstrlen(a),blenstrlen(b);if (strcmp(a,b)0)return -1;return alen>blen?alen:blen; }

Java项目:62 基于ssm的校园驿站管理系统+jsp

作者主页:舒克日记 简介:Java领域优质创作者、Java项目、学习资料、技术互助 文中获取源码 项目介绍 管理员管理快递仓库信息,管理待发货信息,管理已收快递,管理物流以及留言信息,管理员工和用户资料。 员…

市场复盘总结 20240318

仅用于记录当天的市场情况,用于统计交易策略的适用情况,以便程序回测 短线核心:不参与任何级别的调整,采用龙空龙模式 一支股票 10%的时候可以操作, 90%的时间适合空仓等待 二进三: 进级率中 36% 最常用…

深入解析JVM加载机制

一、背景 Java代码被编译器变成生成Class字节码,但字节码仅是一个特殊的二进制文件,无法直接使用。因此,都需要放到JVM系统中执行,将Class字节码文件放入到JVM的过程,简称类加载。 二、整体流程 三、阶段逻辑分析 3…

【推荐系统】NCF神经协同过滤

NCF框架 NCF框架是本文要实现的3个模型的主体结构。 首先是输入层,分别包含两个特征向量 v u v_u vu​和 v i v_i vi​,描述了用户u和物品i。输入仅由一个用户向量和一个物品向量构成,它们分别是以one-hot编码的二值化稀疏向量。 接着是Em…

RocketMQ学习

1.下载安装jdk1.8和rocketmq 下载linux 64位 jdk1.8(自行百度资源) 下载 | RocketMQ (apache.org) ## 解压 tar -zxvf jdk-8u144-linux-x64.tar.gz ## 删除安装包 rm -rf jdk-8u144-linux-x64.tar.gz ##安装jdk环境变量自行百度 解压rocketmq二进制包 ## 解压 unzip rocket…

IT系统可观测性

什么是可观测性 可观测性(Observability)是指能够从系统的外部输出推断出系统内部状态的能力。在IT和云计算领域,它涉及使用软件工具和实践来收集、关联和分析分布式应用程序以及运行这些应用程序的硬件和网络产生的性能数据流。这样做可以更…

达梦如何备份以及导入

启动达梦服务 右键选择管理服务器 点击系统管理,点击配置,点击转换 归档配置点击归档 创建文件夹,选择文件夹目录点击确定 命令方式 逻辑备份与还原 逻辑导出(dexp)和逻辑导入(dimp)支持如下四种级别操作: 数据库级(FULL)&#…

【人工智能】英文学习材料03(每日一句)

🌻个人主页:相洋同学 🥇学习在于行动、总结和坚持,共勉! 目录 Chain Rule (链式法则) Dimensionality Reduction (降维) Long Short-Term Memory (LSTM) (长短期记忆网络) Gradient Explosion (梯度爆炸) Gradie…