Go微服务: 封装nacos-sdk-go的v2版本与应用

概述

  • 基于前文:https://active.blog.csdn.net/article/details/139213323
  • 我们基于此SDK提供的API封装一个公共方法来用于生产环境

封装 nacos-sdk-go

  • 我们封装一个 nacos.go 文件, 这个是通用的工具库

    package common
    
    import (
    	"fmt"
    
    	"github.com/nacos-group/nacos-sdk-go/v2/clients"
    	"github.com/nacos-group/nacos-sdk-go/v2/clients/config_client"
    	"github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client"
    	"github.com/nacos-group/nacos-sdk-go/v2/common/constant"
    	"github.com/nacos-group/nacos-sdk-go/v2/model"
    	"github.com/nacos-group/nacos-sdk-go/v2/vo"
    )
    
    // 定义常量和一些参数默认值
    var (
    	clientParam              vo.NacosClientParam // 客户端参数缓存
    	nacosIp                  = "127.0.0.1"
    	nacosPort                = uint64(8848)
    	nacosContextPath         = "/nacos"
    	nacosNamespaceId         string
    	nacosTimeout             = uint64(5000)
    	nacosNotLoadCacheAtStart = true
    	nacosLogDir              = "nacos/log"
    	nacosCacheDir            = "nacos/cache"
    	nacosLogLevel            = "debug"
    )
    
    // 结构体
    type NacosParams struct {
    	NacosIp                  string
    	NacosPort                uint64
    	NacosContextPath         string
    	NacosNamespaceId         string
    	NacosTimeoutMs           uint64
    	NacosNotLoadCacheAtStart bool
    	NacosLogDir              string
    	NacosCacheDir            string
    	NacosLogLevel            string
    }
    
    // nacos参数实例
    var nacosParams NacosParams = NacosParams{
    	NacosIp:                  nacosIp,
    	NacosPort:                nacosPort,
    	NacosContextPath:         nacosContextPath,
    	NacosNamespaceId:         nacosNamespaceId,
    	NacosTimeoutMs:           nacosTimeout,
    	NacosNotLoadCacheAtStart: nacosNotLoadCacheAtStart,
    	NacosLogDir:              nacosLogDir,
    	NacosCacheDir:            nacosCacheDir,
    	NacosLogLevel:            nacosLogLevel,
    }
    
    // InitConfig 初始化配置,允许通过函数参数修改默认配置
    func InitNacosParams(paramsModifier func(*NacosParams)) {
    	if paramsModifier != nil {
    		paramsModifier(&nacosParams)
    	}
    }
    
    // 获取当前参数
    func GetNacosCurrentParams() NacosParams {
    	return nacosParams
    }
    
    // 获取 nacos客户端参数
    func getNacosClientParam() vo.NacosClientParam {
    	// 缓存配置参数
    	if clientParam.ClientConfig != nil {
    		return clientParam
    	}
    	// create ServerConfig
    	sc := []constant.ServerConfig{
    		*constant.NewServerConfig(nacosParams.NacosIp, nacosParams.NacosPort, constant.WithContextPath(nacosParams.NacosContextPath)),
    	}
    	// create ClientConfig
    	cc := *constant.NewClientConfig(
    		constant.WithNamespaceId(nacosParams.NacosNamespaceId),
    		constant.WithTimeoutMs(nacosParams.NacosTimeoutMs),
    		constant.WithNotLoadCacheAtStart(nacosParams.NacosNotLoadCacheAtStart),
    		constant.WithLogDir(nacosParams.NacosLogDir),
    		constant.WithCacheDir(nacosParams.NacosCacheDir),
    		constant.WithLogLevel(nacosParams.NacosLogLevel),
    	)
    	clientParam = vo.NacosClientParam{
    		ClientConfig:  &cc,
    		ServerConfigs: sc,
    	}
    	return clientParam
    }
    
    // 获取 NacosNamingClient
    func getNacosNamingClient() (naming_client.INamingClient, error) {
    	return clients.NewNamingClient(getNacosClientParam())
    }
    
    // 获取 NacosConfigClient
    func getNacosConfigClient() (config_client.IConfigClient, error) {
    	return clients.NewConfigClient(getNacosClientParam())
    }
    
    // 注册服务功能
    func NacosRegisterServiceInstance(param vo.RegisterInstanceParam) (bool, error) {
    	client, err := getNacosNamingClient()
    	if err != nil {
    		fmt.Println("getNacosNamingClient Error", err.Error())
    		return false, err
    	}
    	return client.RegisterInstance(param)
    }
    
    // 获取服务
    func NacosGetService(param vo.GetServiceParam) (model.Service, error) {
    	client, err := getNacosNamingClient()
    	if err != nil {
    		fmt.Println("getNacosNamingClient Error", err.Error())
    		return model.Service{}, err
    	}
    	return client.GetService(param) // service, err
    }
    
    // 获取所有服务
    func NacosGetAllService(param vo.GetAllServiceInfoParam) (model.ServiceList, error) {
    	client, err := getNacosNamingClient()
    	if err != nil {
    		fmt.Println("getNacosNamingClient Error", err.Error())
    		return model.ServiceList{}, err
    	}
    	return client.GetAllServicesInfo(param)
    }
    
    // 获取远程配置
    func NacosGetConfig(dataId string, group string) (string, error) {
    	client, err := getNacosConfigClient()
    	if err != nil {
    		fmt.Println("getNacosNamingClient Error", err.Error())
    		return "", err
    	}
    	return client.GetConfig(vo.ConfigParam{
    		DataId: dataId,
    		Group:  group,
    	})
    }
    
    // 搜索配置文件
    func NacosSearchConfig(params vo.SearchConfigParam) (*model.ConfigPage, error) {
    	client, err := getNacosConfigClient()
    	if err != nil {
    		fmt.Println("NacosGetConfig Error", err.Error())
    		return nil, err
    	}
    	return client.SearchConfig(params)
    }
    
    // 监听远程配置改动 回调函数类型
    type ConfigChangeListener func(namespace, group, dataId, data string)
    
    // 监听远程配置改动
    func NacosListenConfigWithCallback(dataId, group string, onChange ConfigChangeListener) error {
    	client, err := getNacosConfigClient()
    	if err != nil {
    		fmt.Println("getNacosNamingClient Error", err.Error())
    		return err
    	}
    	err = client.ListenConfig(vo.ConfigParam{
    		DataId: dataId,
    		Group:  group,
    		OnChange: func(namespace, group, dataId, data string) {
    			// 当配置变更时,调用传入的回调函数
    			onChange(namespace, group, dataId, data)
    		},
    	})
    	return err
    }
    
    • 可以看到,上述集成了服务和配置两大类API
    • 服务
      • 注册服务
      • 获取服务
      • 获取所有服务
    • 配置
      • 获取远程配置
      • 搜索配置文件
      • 监听远程配置改动回调
    • 其他未实现封装
      • 比如:批量注册服务,注销服务,更新服务
      • 比如:选择所有实例,选择实例,选择健康实例,订阅,取消订阅
      • 比如:发布配置,删除配置,取消监听配置
      • 后续可以根据官方提供的其他api继续添加
  • 源码仓库:https://gitee.com/go-micro-services/common/blob/master/nacos.go

应用封装


1 )概述

  • 这里,我们应用在一个客户端,比如一个网关中
  • 这里使用gin框架+go-micro来实现封装的sdk

2 )目录结构

gitee.com/go-micro-services/nacos-go-micro-gin
	├── conf                                    # 当前位置文件
	│     └── conf.go
	├── routers                                 # 路由
	│     └── router.go
	├── controllers                             # 控制器
	│     └── api.go
    ├── nacos                                   # nacos目录,后续生成,此文件写入 .gitignore
    │     ├── cache
	│     └── log
	├── main.go
	├── go.mod
	└── .gitignore

3 ) 源码

  • 仓库地址:https://gitee.com/go-micro-services/nacos-go-micro-gin

3.1 conf/conf.go

package conf

var (
	NacosIp                  = "127.0.0.1"
	NacosPort                = uint64(8848)
	NacosTimeoutMs           = uint64(5000)
	NacosContextPath         = "/nacos"
	NacosNamespaceId         = "ff2e8758-33c1-4a88-8005-142cbee91be9" // 这个是配置命名空间后生成的
	NacosLogDir              = "nacos/log"
	NacosCacheDir            = "nacos/cache"
	NacosLogLevel            = "debug"
	NacosServiceName         = "nacos-go-micro-gin"
	NacosGroupName           = "dd"
	NacosNotLoadCacheAtStart = true
	NacosMetadata            = map[string]string{"idc": "shanghai"}
	NacosDataId              = "test.json" // 这个是自己在nacos后台自己配置的
	NacosGroup               = "tt"        // 同上
)
  • 这里做了一个简单的通用配置,一般要拆分环境来管理
  • 这里做一个简单的示例

3.2 routers/router.go

package routers

import (
	"gitee.com/go-micro-services/nacos-go-micro-gin/controllers"
	"github.com/gin-gonic/gin"
)

func RoutersInit(r *gin.Engine) {
	rr := r.Group("/")
	{
		rr.GET("", controllers.ApiController{}.Index)
		rr.GET("/service", controllers.ApiController{}.FindService)
		rr.GET("/getConfig", controllers.ApiController{}.GetConfig)
		rr.GET("/searchConfig", controllers.ApiController{}.SearchConfig)
	}
}
  • 上面定义了4个路由,分别对应
    • /
    • /service
    • /getConfig
    • /searchConfig

3.3 controllers/api.go

package controllers

import (
	"encoding/json"
	"net/http"
	"strings"

	"gitee.com/go-micro-services/common"
	"gitee.com/go-micro-services/nacos-go-micro-gin/conf"
	"github.com/gin-gonic/gin"
	"github.com/nacos-group/nacos-sdk-go/v2/model"
	"github.com/nacos-group/nacos-sdk-go/v2/vo"
)

var statusOK = http.StatusOK
var statusBadRequest = http.StatusBadRequest

type ApiController struct{}

func (con ApiController) Index(c *gin.Context) {
	params := vo.GetAllServiceInfoParam{
		GroupName: conf.NacosGroupName,
		PageNo:    1,
		PageSize:  10,
	}
	list, err := common.NacosGetAllService(params)
	// 错误处理
	if err != nil {
		// 错误处理
		c.JSON(statusBadRequest, gin.H{
			"err": err.Error(),
		})
		return
	}
	// 正常响应
	c.JSON(statusOK, list)
}

func (con ApiController) FindService(c *gin.Context) {
	params := vo.GetServiceParam{
		ServiceName: conf.NacosServiceName,
		GroupName:   conf.NacosGroupName,
	}
	list, err := common.NacosGetService(params)
	// 错误处理
	if err != nil {
		// 错误处理
		c.JSON(statusBadRequest, gin.H{
			"err": err.Error(),
		})
		return
	}
	// 正常响应
	c.JSON(statusOK, list)
}

func (con ApiController) GetConfig(c *gin.Context) {
	content, err := common.NacosGetConfig(conf.NacosDataId, conf.NacosGroup)
	// 错误处理
	if err != nil {
		c.JSON(statusBadRequest, gin.H{
			"err": err.Error(),
		})
		return
	}
	var contentJson map[string]interface{}
	// 解码JSON字符串到map
	err = json.Unmarshal([]byte(content), &contentJson)
	// 错误处理
	if err != nil {
		c.JSON(statusBadRequest, gin.H{
			"err": err.Error(),
		})
		return
	}
	// 正常响应
	c.JSON(statusOK, contentJson)
}

func (con ApiController) SearchConfig(c *gin.Context) {
	// 构造搜索参数
	params := vo.SearchConfigParam{
		Search:   "blur",
		DataId:   conf.NacosDataId,
		Group:    conf.NacosGroup,
		PageNo:   1,
		PageSize: 10,
	}
	// 调用接口获得结果
	var currentPage *model.ConfigPage
	var err error
	if currentPage, err = common.NacosSearchConfig(params); err != nil {
		c.JSON(statusBadRequest, gin.H{
			"err": err.Error(),
		})
		return
	}
	// 格式转化
	var jsonBytes []byte
	if jsonBytes, err = json.Marshal(currentPage); err != nil {
		c.JSON(statusBadRequest, gin.H{
			"err": err.Error(),
		})
		return
	}
	// 处理特殊字符,这里性能会有影响,可以用一个循环加一个Map来进行替换,而非每次 ReplaceAll
	str := strings.ReplaceAll(string(jsonBytes), " ", "")
	str = strings.ReplaceAll(str, "\\n", "")
	str = strings.ReplaceAll(str, "\\", "")
	str = strings.ReplaceAll(str, `"{`, "{")
	str = strings.ReplaceAll(str, `}"`, "}")
	// 转换成 json
	var result map[string]interface{}
	if err = json.Unmarshal([]byte(str), &result); err != nil {
		c.JSON(statusBadRequest, gin.H{
			"err": err.Error(),
		})
		return
	}
	c.JSON(statusOK, result)
}
  • 这是对应上述4个路由的四个控制器的处理

3.4 main.go

package main

import (
	"fmt"

	"gitee.com/go-micro-services/common"
	"gitee.com/go-micro-services/nacos-go-micro-gin/conf"
	"gitee.com/go-micro-services/nacos-go-micro-gin/routers"
	"github.com/gin-gonic/gin"
	"github.com/nacos-group/nacos-sdk-go/v2/vo"
	"go-micro.dev/v4/web"
)

func init() {
	// 1. 参数初始化
	initNacosParams()
	// 2. 注册
	regNacos()
	// 3. 监听
	listenNacosConfig()
}

// 参数初始化
func initNacosParams() {
	// 初始化配置,并在过程中修改默认值
	common.InitNacosParams(func(cfg *common.NacosParams) {
		cfg.NacosIp = conf.NacosIp
		cfg.NacosPort = conf.NacosPort
		cfg.NacosNamespaceId = conf.NacosNamespaceId
		cfg.NacosLogLevel = conf.NacosLogLevel
	})
	// 如果需要,可以随时通过GetCurrentConfig获取配置副本
	currentConfig := common.GetNacosCurrentParams()
	fmt.Printf("Retrieved Config - Service Name: %s, Port: %d\n", currentConfig.NacosIp, currentConfig.NacosPort)
}

// 注册
func regNacos() {
	_, err := common.NacosRegisterServiceInstance(vo.RegisterInstanceParam{
		Ip:          conf.NacosIp,
		Port:        conf.NacosPort,
		ServiceName: conf.NacosServiceName,
		GroupName:   conf.NacosGroupName,
		// ClusterName: "cluster-a",
		Weight:    10,
		Enable:    true,
		Healthy:   true,
		Ephemeral: true,
		Metadata:  conf.NacosMetadata,
	})
	if err != nil {
		fmt.Println("注册发生错误: ", err.Error())
	}
}

// 监听配置改动
func listenNacosConfig() {
	var namespace, group, dataId string
	err := common.NacosListenConfigWithCallback(conf.NacosDataId, conf.NacosGroup, func(namespace, group, dataId, data string) {
		namespace = namespace
		group = group
		dataId = dataId
		fmt.Println("Config changed. Namespace: %s, Group: %s, DataId: %s, Content: %s", namespace, group, dataId, data)
	})
	if err != nil {
		fmt.Println("Failed to listen config for Namespace: %s, DataId: %s, Group: %s. Error: %v", namespace, dataId, group, err)
	}
}

func main() {
	ginRouter := gin.Default()
	routers.RoutersInit(ginRouter)

	srv := web.NewService(
		web.Metadata(map[string]string{"version": "latest"}),
		web.Handler(ginRouter),      // 服务路由
		web.Address("0.0.0.0:9999"), // 服务端口
	)
	srv.Run()
}
  • 这里的main.go主要关注的是上面的 init 函数中的nacos配置
  • 这里演示的是客户端,当然服务端也是使用封装后的sdk

演示效果


1 )手动配置的配置文件

2 ) 注册了的服务

3 )API 应用的路由相关返回

其他

  • 在 go-micro中,有 nacos 的相关插件,它也是自己的封装
  • 但是,在使用的时候,可能不能完全满足所有的nacos的接口实现
  • 所以,我个人推荐基于官方提供的api 自行进行封装和扩展,从而不被 go-micro 限制
  • 毕竟,框架的使用会有一个通用的问题就是虽然使用方便,但扩展修改可能会受其限制

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

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

相关文章

如何查看谁连接到了你的Wi-Fi网络?这里提供几种方法或工具

序言 你知道谁连接到你路由器的Wi-Fi网络吗?查看从路由器或计算机连接到Wi-Fi网络的设备列表,找出答案。 请记住,现在很多设备都可以连接到了你的Wi-Fi,该名单包括笔记本电脑、智能手机、平板电脑、智能电视、机顶盒、游戏机、Wi-Fi打印机等。 使用GlassWire Pro查看连接…

蒙自源六一童趣献礼:纯真餐单,唤醒你的童年味蕾

当岁月的车轮滚滚向前,我们总会怀念那些逝去的时光,尤其是那段纯真无瑕的童年。当六一儿童节来临,心底的那份童趣与回忆总会被轻轻触动。从5月25日起,蒙自源旗下各大门店为所有小朋友,以及那些心怀童真的大人们&#x…

Mac vm虚拟机激活版:VMware Fusion Pro for Mac支持Monterey 1

相信之前使用过Win版系统的朋友们对这款VMware Fusion Pro for Mac应该都不会陌生,这款软件以其强大的功能和适配能力广受用户的好评,在Mac端也同样是一款最受用户欢迎之一的虚拟机软件,VM虚拟机mac版可以让您能够轻松的在Apple的macOS和Mac的…

华为交换机、路由器配置查询、用户界面常见配置及安全加固

华为交换机、路由器配置查询、用户界面常见配置及安全加固。 一、查询命令 1.常用的查询命令 查看当前生效的配置信息: display current-configuration //正在生效的配置,默认参数不显示。查看当前视图下生效的配置信息: display this //常…

数字IC基础:主要的FPGA厂商

相关阅读 数字IC基础https://blog.csdn.net/weixin_45791458/category_12365795.html?spm1001.2014.3001.5482 Xilinx(现已被AMD收购) Xilinx, 成立于1984年,是FPGA(现场可编程门阵列)技术的创始者和市场领导者。该公…

Mac下载Homebrew

通过command空格搜索终端打开 直接输入 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" 然后输入电脑密码 然后直接回车等待安装完成 注意⚠️:如果出现报错/opt/homebrew/bin is not in your PATH…

python操作mongodb底层封装并验证pymongo是否应该关闭连接

一、pymongo简介 github地址:https://github.com/mongodb/mongo-python-driver mymongo安装命令:pip install pymongo4.7.2 mymongo接口文档:PyMongo 4.7.2 Documentation PyMongo发行版包含Python与MongoDB数据库交互的工具。bson包是用…

【设计模式深度剖析】【1】【行为型】【模板方法模式】| 以烹饪过程为例加深理解

👈️上一篇:结构型设计模式对比 文章目录 模板方法模式定义英文原话直译如何理解呢? 2个角色类图代码示例 应用优点缺点使用场景 示例解析:以烹饪过程为例类图代码示例 模板方法模式 模板方法模式(Template Method Pattern&…

生信分析进阶4 - 比对结果的FLAG和CIGAR信息含义与BAM文件指定区域提取

BAM文件时存储比对数据的常用格式,可用于短reads和长reads数据。BAM是二进制压缩格式,SAM文件为其纯文本格式,CRAM为BAM的高压缩格式,IO效率相比于BAM略差,但是占用存储空间更小。 1. BAM文件的比对信息 BAM的核心信…

软件测试期末复习

第四章 边界黑盒测试续 4.3边界值设计方法 1.边界值设计方法:故障往往出现在定义域或边界值上。通常边界值分析法是作为对等价类划分法的补充。其测试用例来自等价类的边界。是对输入或输出的边界值进行测试的一种黑盒测试方法。 2.边界值分析法和等价类划分法的…

在Visual Studio2022中同一个项目里写作业,有多个cpp文件会报错

为了省事,在同一个项目里写很多个题目,结果只有一个cpp文件时没出错,写了2个cpp文件再想运行时就出错了; 将不相关的cpp文件移出去 在源文件中对其点击右键,找到“从项目中排除”; 结果如图,剩…

网络原理-三

一、连接管理 建立连接,断开连接 建立连接,TCP有连接的. 客户端执行 socket new Socket(SeverIP,severPort); -> 这个操作就是在建立连接. 上述只是调用socket api,真正建立连接的过程,实在操作系统内核完成的. 内核是怎样完成上述的 " 建立连接 "过程的…

云硬盘的基准性能测试场景

参考来源: 深入浅出云计算-05 | 云硬盘:云上IO到底给不给力 云硬盘的性能等级 当下的云硬盘经过了多次的软硬件迭代,尤其是SSD的迅速发展,吞吐量和随机读写能力等各项性能指标都已经不再是问题了。在现代云计算中,已…

数仓建模—ChatETL

数仓建模—ChatETL 前面我们介绍过ChatBI ,就是让用户通过自然语言对话的方式可以获取到自己想要的数据,然后通过合适的报表展示出来,其实我们可以将其理解为应用层面的技术创新,但是这个实现的前提就是我们底层已经有加工好的大量的数据模型数据表,并且有完善的元数据建…

“论SOA在企业集成架构设计中的应用”必过模板,突击2024软考高项论文

考题部分 企业应用集成(Enterprise Application Integration, EAI)是每个企业都必须要面对的实际问题。面向服务的企业应用集成是一种基于面向服务体系结构(Service-OrientedArchitecture,SOA)的新型企业应用集成技术,强调将企业和组织内部的资源和业务功…

【自动化运维】不要相信人,把所有的东西都交给机器去处理

不积跬步,无以至千里;不积小流,无以成江海。 大家好,我是闲鹤,十多年开发、架构经验,先后在华为、迅雷服役过,也在高校从事教学3年;目前已创业了7年多,主要从事物联网/车…

【网络安全的神秘世界】MySQL

🌝博客主页:泥菩萨 💖专栏:Linux探索之旅 | 网络安全的神秘世界 | 专接本 MySQL MySQL 教程 | 菜鸟教程 (runoob.com) 什么是数据库 数据库(Database)是按照数据结构来组织、存储和管理数据的仓库 在do…

COLING 2024: 复旦发布AoR,层级聚合推理突破大模型复杂推理上限

“三个臭皮匠,顶个诸葛亮?” “一个模型不行,那就再堆一个?” 过去当我们在处理复杂任务的时候,往往会考虑集成策略(Ensembling Strategy),通过多个模型投票的方式,选出…

[手游] Florence逝去的爱弗洛伦斯

图片处理工具箱Hummingbird : Hummingbird使用智能压缩技术来减少文件的大小,支持:jpg、png、webp、svg、gif、gif、css、js、html、mp4、mov,可以设置压缩的同时等比例缩放图片或视频的尺寸。可以拖放文件夹压缩,一次最多可处理1…

茉莉香飘,奶茶丝滑——周末悠闲时光的绝佳伴侣

周末的时光总是格外珍贵,忙碌了一周的我们,终于迎来了难得的闲暇。这时,打开喜欢的综艺,窝在舒适的沙发里,再冲泡一杯香飘飘茉莉味奶茶,一边沉浸在剧情的海洋中,一边品味着香浓丝滑的奶茶&#…