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 main

import (
	...
	"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", // 默认值DEFAULT
    GroupName:   "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", // 默认值DEFAULT
    GroupName:   "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", // 默认值DEFAULT
    GroupName:   "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", // 默认值DEFAULT
    GroupName:   "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_GROUP
    Clusters:    []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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

在OpenStack架构中,Controller节点的配置(基础)

虚拟机的安装 新建虚拟机,选择自定义 默认选择即可 操作系统的镜像稍后选择 客户及操作系统选择Linux,注意选择centos 7 64位 给虚拟机命名 处理器的配置建议1:2 内存大小选择建议为:4GB 网络连接选择为:NAT 默认即可…

蓝桥杯2022年第十三届省赛真题-灭鼠先锋

LLLV solution1 必输:只有一个格子 手算可以模拟出来~ solution2 OOOO状态下,谁先下谁必输 》问题转化为谁先下满第一排,谁必赢,可以非常容易的模拟出来

Vue.js+SpringBoot开发天沐瑜伽馆管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 数据中心模块2.2 瑜伽课程模块2.3 课程预约模块2.4 系统公告模块2.5 课程评价模块2.6 瑜伽器械模块 三、系统设计3.1 实体类设计3.1.1 瑜伽课程3.1.2 瑜伽课程预约3.1.3 系统公告3.1.4 瑜伽课程评价 3.2 数据库设计3.2.…

uniapp实现点击标签文本域中显示标签内容

先上一个效果图 实现的效果有: ①.点击标签时,标签改变颜色并处于可删除状态 ②.切换标签,文本域中出现标签的内容 ③.点击标签右上角的删除可删掉标签,同时清除文本域中标签的内容 ④.可输入内容,切换时不影响输入…

考研C语言复习进阶(5)

目录 1. 为什么使用文件 2. 什么是文件 2.1 程序文件 2.2 数据文件 2.3 文件名 3. 文件的打开和关闭 3.1 文件指针 3.2 文件的打开和关闭 4. 文件的顺序读写 ​编辑 ​编辑 4.1 对比一组函数: ​编辑 5. 文件的随机读写 5.1 fseek 5.2 ftell 5.3 rewind…

tomcat中把项目放在任意目录中的步骤

java web 项目由idea开发&#xff0c;路径如下图所示&#xff1a; 1.在tomcat安装目录conf\Catalina\localhost 里面&#xff0c;编写lesson1.xml文件内容如下&#xff1a; <Context path"/lesson1" docBase"C:\Users\信息技术系\Desktop\2024\学校工作\jav…

基于51单片机的微波炉温度控制器设计[proteus仿真]

基于51单片机的微波炉温度控制器设计[proteus仿真] 温度检测系统这个题目算是课程设计和毕业设计中常见的题目了&#xff0c;本期是一个基于51单片机的微波炉温度控制器设计 需要的源文件和程序的小伙伴可以关注公众号【阿目分享嵌入式】&#xff0c;赞赏任意文章 2&#xff…

【矩阵】240. 搜索二维矩阵 II【中等】

搜索二维矩阵 II 编写一个高效的算法来搜索 m x n 矩阵 matrix 中的一个目标值 target 。该矩阵具有以下特性&#xff1a;每行的元素从左到右升序排列。每列的元素从上到下升序排列。 示例 1&#xff1a; 输入&#xff1a;matrix [[1,4,7,11,15],[2,5,8,12,19],[3,6,9,16,22…

C++:2024/3/12

作业1&#xff1a;试编程&#xff0c;封装一个类 要求&#xff1a;自己封装一个矩形类(Rect)&#xff0c;拥有私有属性:宽度(width)、高度(height)&#xff0c; 定义公有成员函数: 初始化函数:void init(int w, int h) 更改宽度的函数:set_w(int w) 更改高度的函数:set_h(…

如何打造知识管理平台,只需了解这几点

随着企业的发展&#xff0c;知识资源日益丰富和复杂&#xff0c;如果不加以有效管理和整合&#xff0c;这些知识很可能会被埋没或丢失。打造知识管理平台可以将这些知识资源进行统一存储和分类&#xff0c;便于员工查找和使用&#xff0c;从而充分发挥知识的价值。有很多工具可…

蓝桥杯每日一题:血色先锋队

今天浅浅复习巩固一下bfs 答案&#xff1a; #include<iostream> #include<algorithm> #include<cstring>using namespace std; typedef pair<int,int> PII;const int N510; int n,m,a,b; int dist[N][N]; PII q[N*N]; int hh0,tt-1;int dx[]{1,0,-1,…

2024年【安全员-A证】复审考试及安全员-A证模拟试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 安全员-A证复审考试参考答案及安全员-A证考试试题解析是安全生产模拟考试一点通题库老师及安全员-A证操作证已考过的学员汇总&#xff0c;相对有效帮助安全员-A证模拟试题学员顺利通过考试。 1、【多选题】《安全生产…

【深度学习笔记】9_6 目标检测数据集

注&#xff1a;本文为《动手学深度学习》开源内容&#xff0c;部分标注了个人理解&#xff0c;仅为个人学习记录&#xff0c;无抄袭搬运意图 9.6 目标检测数据集&#xff08;皮卡丘&#xff09; 在目标检测领域并没有类似MNIST或Fashion-MNIST那样的小数据集。为了快速测试模型…

Java项目:53 springboot校园管理系统的设计与实现014

作者主页&#xff1a;舒克日记 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文中获取源码 项目介绍 1、关于校园管理系统的基本要求&#xff1a; &#xff08;1&#xff09;功能要求&#xff1a;可以管理首页、个人中心、院校管理、用户管理、单位类别…

2023年全球软件质量效能大会(QECon上海站):智慧碰撞,质量与效能的新篇章(附大会核心PPT资料下载)

在数字化浪潮席卷全球的今天&#xff0c;软件行业正以前所未有的速度发展。作为推动这一进程的重要力量&#xff0c;软件质量与效能的提升显得尤为关键。2023年全球软件质量&效能大会&#xff08;QECon上海站&#xff09;的盛大召开&#xff0c;无疑为业界提供了一次难得的…

VSCode ARM CortexM 开发

VSCode ARM CortexM 开发: http://coffeelatte.vip.cpolar.top/post/software/applications/vscode/vscode_arm_cortexm_开发/ 文章目录 VSCode ARM CortexM 开发: <http://coffeelatte.vip.cpolar.top/post/software/applications/vscode/vscode_arm_cortexm_%E5%BC%80%E5%…

lwip优化任务优先级

在lwIP中&#xff0c;ethernetif_input线程负责接收和处理从以太网接口接收到的数据包&#xff0c;而tcpip主线程则负责处理lwIP协议栈中的各种事件和数据包。一般情况下&#xff0c;ethernetif_input线程的优先级应该设置为低于tcpip主线程的优先级。 这是因为在实时操作系统…

未解决的问题:字符数组中元素的个数

情形1&#xff1a; #include<stdio.h> int main() {int arr_int1[10];int arr_int2[]{1,2,3,4,5};char arr_char1[10];char arr_char2[]"world";char arr_char3[]{h,e,l,l,o};int i;i0;while(arr_char2[i]!\0){i;}printf("%d\n",i);i0;while(arr_ch…

AndroidLinux GPIO控制方法

目录 1 GPIO整体架构 2 user space 层 gpio使用方法 2.1 sysfs控制方法 2.1.1 kernel版本区别 2.1.2 /sys/class/gpio 2.1.3 /sys/bug/gpio/devices 2.2 chardev控制方法 2.2.1 chardev 示例代码 2.2.2 示例代码主要步骤描述 2.2.3 include/linux/gpio.h 全部代码 2.3…

Python错误处理和异常(必要的攻略)

目录 1. 错误类型 2. 异常&#xff08;Exceptions&#xff09; 2.1 try和except 块 2.2 捕获特定类型的异常 2.3 多个 except 块 2.4 else 和 finally 语句 3. 抛出异常 3.1 自定义异常 4. 异常处理的最佳实践 结语 在学Python以来&#xff0c;你敲的代码已经有很多了…