概述
- 基于前文: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 限制
- 毕竟,框架的使用会有一个通用的问题就是虽然使用方便,但扩展修改可能会受其限制