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

概述

封装 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 ) 源码

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 限制
  • 毕竟,框架的使用会有一个通用的问题就是虽然使用方便,但扩展修改可能会受其限制
  • 9
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Wang's Blog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值