当前位置: 首页 > news >正文

【Golang/nacos】nacos配置的增删查改,以及服务注册的golang实例及分析

前言

本文分析的实例来源于nacos在github上的开源仓库

nacos配置的增删查改


先具体来看一段代码,我将逐步分析每一段的作用

package mainimport ("fmt""time""github.com/nacos-group/nacos-sdk-go/clients""github.com/nacos-group/nacos-sdk-go/common/constant""github.com/nacos-group/nacos-sdk-go/vo"
)func main() {// 创建ServerConfigsc := []constant.ServerConfig{*constant.NewServerConfig("192.168.195.129",                  // Nacos服务器的IP地址8848,                               // Nacos服务器的端口号constant.WithContextPath("/nacos"), // 上下文路径),}// 创建ClientConfigcc := *constant.NewClientConfig(constant.WithNamespaceId("ace1b5fe-80c3-4fab-b89a-625f9ff41093"), // 命名空间IDconstant.WithTimeoutMs(5000),                                     // 超时时间(毫秒)constant.WithNotLoadCacheAtStart(true),                           // 启动时不加载缓存constant.WithLogDir("/tmp/nacos/log"),                            // 日志目录constant.WithCacheDir("/tmp/nacos/cache"),                        // 缓存目录constant.WithLogLevel("debug"),                                   // 日志级别constant.WithUsername("nacos"),                                   // 用户名constant.WithPassword("nacos"),                               // 密码)// 创建配置客户端client, err := clients.NewConfigClient(vo.NacosClientParam{ClientConfig:  &cc, // 客户端配置ServerConfigs: sc,  // 服务器配置切片},)if err != nil {panic(err) // 如果创建客户端失败,程序终止并打印错误信息}// 发布配置_, err = client.PublishConfig(vo.ConfigParam{DataId:  "test-data",    // 配置的数据IDGroup:   "test-group",   // 配置的分组Content: "hello world!", // 配置的内容})if err != nil {fmt.Printf("PublishConfig err:%+v \n", err) // 如果发布配置失败,打印错误信息}_, err = client.PublishConfig(vo.ConfigParam{DataId:  "test-data-2",  // 第二个配置的数据IDGroup:   "test-group",   // 第二个配置的分组Content: "hello world!", // 第二个配置的内容})if err != nil {fmt.Printf("PublishConfig err:%+v \n", err) // 如果发布配置失败,打印错误信息}// 等待1秒,确保配置发布完成time.Sleep(1 * time.Second)// 获取配置content, err := client.GetConfig(vo.ConfigParam{DataId: "test-data",  // 要获取的配置的数据IDGroup:  "test-group", // 要获取的配置的分组})fmt.Println("GetConfig,config :" + content) // 打印获取到的配置内容// 监听配置变更err = client.ListenConfig(vo.ConfigParam{DataId: "test-data",  // 要监听的配置的数据IDGroup:  "test-group", // 要监听的配置的分组OnChange: func(namespace, group, dataId, data string) {fmt.Println("config changed group:" + group + ", dataId:" + dataId + ", content:" + data) // 配置变更时打印信息},})if err != nil {fmt.Printf("ListenConfig err:%+v \n", err) // 如果监听失败,打印错误信息}err = client.ListenConfig(vo.ConfigParam{DataId: "test-data-2", // 第二个要监听的配置的数据IDGroup:  "test-group",  // 第二个要监听的配置的分组OnChange: func(namespace, group, dataId, data string) {fmt.Println("config changed group:" + group + ", dataId:" + dataId + ", content:" + data) // 配置变更时打印信息},})if err != nil {fmt.Printf("ListenConfig err:%+v \n", err) // 如果监听失败,打印错误信息}// 等待1秒,确保监听配置生效time.Sleep(1 * time.Second)// 修改配置_, err = client.PublishConfig(vo.ConfigParam{DataId:  "test-data",   // 要修改的配置的数据IDGroup:   "test-group",  // 要修改的配置的分组Content: "test-listen", // 修改后的配置内容})if err != nil {fmt.Printf("PublishConfig err:%+v \n", err) // 如果修改配置失败,打印错误信息}_, err = client.PublishConfig(vo.ConfigParam{DataId:  "test-data-2", // 第二个要修改的配置的数据IDGroup:   "test-group",  // 第二个要修改的配置的分组Content: "test-listen", // 第二个修改后的配置内容})if err != nil {fmt.Printf("PublishConfig err:%+v \n", err) // 如果修改配置失败,打印错误信息}// 等待2秒,确保配置修改被监听到time.Sleep(2 * time.Second)// 等待1秒time.Sleep(1 * time.Second)// 删除配置_, err = client.DeleteConfig(vo.ConfigParam{DataId: "test-data",  // 要删除的配置的数据IDGroup:  "test-group", // 要删除的配置的分组})if err != nil {fmt.Printf("DeleteConfig err:%+v \n", err) // 如果删除配置失败,打印错误信息}// 等待1秒,确保配置删除操作完成time.Sleep(1 * time.Second)// 取消监听配置变更err = client.CancelListenConfig(vo.ConfigParam{DataId: "test-data",  // 要取消监听的配置的数据IDGroup:  "test-group", // 要取消监听的配置的分组})if err != nil {fmt.Printf("CancelListenConfig err:%+v \n", err) // 如果取消监听失败,打印错误信息}// 搜索配置searchPage, _ := client.SearchConfig(vo.SearchConfigParam{Search:   "blur", // 搜索模式,这里是模糊搜索DataId:   "",     // 数据ID,为空表示不指定Group:    "",     // 分组,为空表示不指定PageNo:   1,      // 搜索的页码PageSize: 10,     // 每页的大小})fmt.Printf("Search config:%+v \n", searchPage) // 打印搜索结果
}

Serverconfig

ServerConfig主要用于配置Nacos服务器的信息。它告诉客户端如何连接到Nacos服务器,包括服务器的IP地址、端口号和上下文路径等。通过ServerConfig,客户端可以找到并连接到Nacos服务器,这里以切片的形式,是为了可以保证多个选择,并且具有可扩展性。

sc := []constant.ServerConfig{*constant.NewServerConfig("192.168.195.129", // Nacos服务器的IP地址8848,              // Nacos服务器的端口号constant.WithContextPath("/nacos"), // 上下文路径),
}

这段代码配置了一个Nacos服务器,使得客户端可以连接到该服务器。

ClientConfig

ClientConfig用于配置客户端的行为和属性,包括命名空间ID、超时时间、日志级别、缓存目录等。ClientConfig中的命名空间ID(NamespaceId)指定了客户端要操作的特定命名空间。通过命名空间ID,客户端可以确保只管理属于该命名空间的配置和服务。

例如:

cc := *constant.NewClientConfig(constant.WithNamespaceId("ace1b5fe-80c3-4fab-b89a-625f9ff41093"), // 命名空间IDconstant.WithTimeoutMs(5000),constant.WithNotLoadCacheAtStart(true),constant.WithLogDir("/tmp/nacos/log"),constant.WithCacheDir("/tmp/nacos/cache"),constant.WithLogLevel("debug"),constant.WithUsername("nacos"),constant.WithPassword("nacos"),
)

这段代码配置了客户端的行为,特别是指定了要操作的命名空间ID。

  • ServerConfig:用于配置Nacos服务器的信息,告诉客户端如何连接到Nacos服务器。
  • ClientConfig:用于配置客户端的行为和属性,包括命名空间ID,告诉客户端要操作哪个命名空间中的配置和服务。

通过这两个配置,客户端可以正确地连接到Nacos服务器,并操作指定命名空间中的配置和服务。以下是代码中两者的使用关系:

client, err := clients.NewConfigClient(vo.NacosClientParam{ClientConfig:  &cc, // 客户端配置,包括命名空间ID等ServerConfigs: sc,  // 服务器配置切片,用于连接Nacos服务器},
)

这里就创建了一个客户端用于访问服务器特定的命名空间。

命名空间是什么?

AI答 + 我的理解:在Nacos中,命名空间(Namespace)是一种用于隔离配置和服务的机制。通过命名空间,可以将不同的配置和服务分隔开来,避免它们之间的冲突和干扰。命名空间在Nacos中通常用于多环境(如开发环境、测试环境、生产环境)的配置管理,或者用于不同的项目或团队之间的配置隔离。

比如我的代码里面,命名空间ID是 "ace1b5fe-80c3-4fab-b89a-625f9ff41093"。这个ID是一个唯一的标识符,用于指定一个特定的命名空间。通过这个命名空间ID,客户端可以操作属于该命名空间的配置和服务。


添加配置

// 发布配置
_, err = client.PublishConfig(vo.ConfigParam{DataId:  "test-data",    // 配置的数据IDGroup:   "test-group",   // 配置的分组Content: "hello world!", // 配置的内容
})
if err != nil {fmt.Printf("PublishConfig err:%+v \n", err) // 如果发布配置失败,打印错误信息
}_, err = client.PublishConfig(vo.ConfigParam{DataId:  "test-data-2",  // 第二个配置的数据IDGroup:   "test-group",   // 第二个配置的分组Content: "hello world!", // 第二个配置的内容
})
if err != nil {fmt.Printf("PublishConfig err:%+v \n", err) // 如果发布配置失败,打印错误信息

此处实现的功能为发布配置,和nacos控制台的发布配置功能是一样的

在这里插入图片描述

注意要选择相应的命名空间。


监视配置

// 获取配置
content, err := client.GetConfig(vo.ConfigParam{DataId: "test-data",  // 要获取的配置的数据IDGroup:  "test-group", // 要获取的配置的分组
})
fmt.Println("GetConfig,config :" + content) // 打印获取到的配置内容// 监听配置变更
err = client.ListenConfig(vo.ConfigParam{DataId: "test-data",  // 要监听的配置的数据IDGroup:  "test-group", // 要监听的配置的分组OnChange: func(namespace, group, dataId, data string) {fmt.Println("config changed group:" + group + ", dataId:" + dataId + ", content:" + data) // 配置变更时打印信息},
})

这里实现了获取配置,以及对配置的监听,为什么这里可以监听到之后配置的变更呢?这里我认为应该是在函数内部使用了goroutine来实现的,一旦检测到有变化,监听器就会发出响应。


修改/删除配置

_, err = client.PublishConfig(vo.ConfigParam{DataId:  "test-data",   // 要修改的配置的数据IDGroup:   "test-group",  // 要修改的配置的分组Content: "test-listen", // 修改后的配置内容
})
_, err = client.DeleteConfig(vo.ConfigParam{DataId: "test-data",  // 要删除的配置的数据IDGroup:  "test-group", // 要删除的配置的分组
})
if err != nil {fmt.Printf("DeleteConfig err:%+v \n", err) // 如果删除配置失败,打印错误信息
}

至于修改以及删除的逻辑就很简单了,这里不多赘述


nacos服务注册

先看代码示例,在接下来我会详细讲解每一步

package mainimport ("fmt""github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client""time""github.com/nacos-group/nacos-sdk-go/v2/clients""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/util""github.com/nacos-group/nacos-sdk-go/v2/vo"
)func main() {// 创建 ServerConfig,配置 Nacos 服务器地址、端口及上下文路径sc := []constant.ServerConfig{*constant.NewServerConfig("192.168.195.129", 8848, constant.WithContextPath("/nacos")),}// 创建 ClientConfig,配置客户端的基本参数,如命名空间、超时时间、日志路径等cc := *constant.NewClientConfig(constant.WithNamespaceId("ace1b5fe-80c3-4fab-b89a-625f9ff41093"),constant.WithTimeoutMs(5000),constant.WithNotLoadCacheAtStart(true),constant.WithLogDir("/tmp/nacos/log"),constant.WithCacheDir("/tmp/nacos/cache"),constant.WithLogLevel("debug"),constant.WithUsername("nacos"),     // 用户名constant.WithPassword("nacos"), // 密码)// 创建命名服务客户端client, err := clients.NewNamingClient(vo.NacosClientParam{ClientConfig:  &cc,ServerConfigs: sc,},)if err != nil {panic(err) // 如果客户端创建失败,则抛出异常}// 注册服务实例到 NacosregisterServiceInstance(client, vo.RegisterInstanceParam{Ip:          "10.0.0.10",                          // 服务实例的 IP 地址Port:        8848,                                 // 服务实例的端口号ServiceName: "test.go",                            // 服务名称GroupName:   "group-a",                            // 分组名称ClusterName: "cluster-a",                          // 集群名称Weight:      10,                                   // 权重Enable:      true,                                 // 是否启用Healthy:     true,                                 // 是否健康Ephemeral:   true,                                 // 是否为临时实例Metadata:    map[string]string{"idc": "shanghai"}, // 元数据信息})//从 Nacos 取消注册服务实例deRegisterServiceInstance(client, vo.DeregisterInstanceParam{Ip:          "10.0.0.10", // 服务实例的 IP 地址Port:        8848,        // 服务实例的端口号ServiceName: "demo.go",   // 服务名称GroupName:   "group-a",   // 分组名称Cluster:     "cluster-a", // 集群名称Ephemeral:   true,        // 必须为临时实例})time.Sleep(1 * time.Second) // 等待 1 秒// 批量注册多个服务实例batchRegisterServiceInstance(client, vo.BatchRegisterInstanceParam{ServiceName: "demo.go", // 服务名称GroupName:   "group-a", // 分组名称Instances: []vo.RegisterInstanceParam{{Ip:          "10.0.0.10",                          // 第一个服务实例的 IP 地址Port:        8848,                                 // 第一个服务实例的端口号Weight:      10,                                   // 权重Enable:      true,                                 // 是否启用Healthy:     true,                                 // 是否健康Ephemeral:   true,                                 // 是否为临时实例ClusterName: "cluster-a",                          // 集群名称Metadata:    map[string]string{"idc": "shanghai"}, // 元数据信息}, {Ip:          "10.0.0.12",                          // 第二个服务实例的 IP 地址Port:        8848,                                 // 第二个服务实例的端口号Weight:      7,                                    // 权重Enable:      true,                                 // 是否启用Healthy:     true,                                 // 是否健康Ephemeral:   true,                                 // 是否为临时实例ClusterName: "cluster-a",                          // 集群名称Metadata:    map[string]string{"idc": "shanghai"}, // 元数据信息}},})time.Sleep(1 * time.Second) // 等待 1 秒// 根据服务名称、分组名称和集群名称获取服务信息getService(client, vo.GetServiceParam{ServiceName: "demo.go",             // 服务名称GroupName:   "group-a",             // 分组名称Clusters:    []string{"cluster-a"}, // 集群名称列表})// 获取指定服务的所有实例selectAllInstances(client, vo.SelectAllInstancesParam{ServiceName: "demo.go",             // 服务名称GroupName:   "group-a",             // 分组名称Clusters:    []string{"cluster-a"}, // 集群名称列表})// 获取指定服务的健康实例selectInstances(client, vo.SelectInstancesParam{ServiceName: "demo.go",             // 服务名称GroupName:   "group-a",             // 分组名称Clusters:    []string{"cluster-a"}, // 集群名称列表HealthyOnly: true,                  // 仅获取健康实例})// 根据加权随机算法获取一个健康实例selectOneHealthyInstance(client, vo.SelectOneHealthInstanceParam{ServiceName: "demo.go",             // 服务名称GroupName:   "group-a",             // 分组名称Clusters:    []string{"cluster-a"}, // 集群名称列表})// 订阅服务变更,当服务信息发生变化时,会触发回调函数subscribeParam := &vo.SubscribeParam{ServiceName: "demo.go", // 服务名称GroupName:   "group-a", // 分组名称SubscribeCallback: func(services []model.Instance, err error) {fmt.Printf("callback return services:%s \n\n", util.ToJsonString(services)) // 变更时打印服务实例信息},}subscribe(client, subscribeParam)// 等待 3 秒,让客户端从服务端拉取变更time.Sleep(3 * time.Second)// 更新服务实例信息updateServiceInstance(client, vo.UpdateInstanceParam{Ip:          "10.0.0.11",                          // 更新后的 IP 地址Port:        8848,                                 // 服务实例的端口号ServiceName: "demo.go",                            // 服务名称GroupName:   "group-a",                            // 分组名称ClusterName: "cluster-a",                          // 集群名称Weight:      10,                                   // 权重Enable:      true,                                 // 是否启用Healthy:     true,                                 // 是否健康Ephemeral:   true,                                 // 是否为临时实例Metadata:    map[string]string{"idc": "beijing1"}, // 更新后的元数据信息})// 等待 3 秒,让客户端从服务端拉取变更time.Sleep(3 * time.Second)// 取消订阅服务变更unSubscribe(client, subscribeParam)// 获取指定分组下的所有服务名称列表getAllService(client, vo.GetAllServiceInfoParam{GroupName: "group-a", // 分组名称PageNo:    1,         // 分页页码PageSize:  10,        // 每页大小})
}//==========================================以下为函数实现===================================================// registerServiceInstance 向 Nacos 注册一个服务实例
func registerServiceInstance(client naming_client.INamingClient, param vo.RegisterInstanceParam) {// 调用 RegisterInstance 方法注册服务实例success, err := client.RegisterInstance(param)if !success || err != nil {// 如果注册失败,抛出 panic 并打印错误信息panic("RegisterServiceInstance failed!" + err.Error())}// 打印注册参数和结果fmt.Printf("RegisterServiceInstance,param:%+v,result:%+v \n\n", param, success)
}// batchRegisterServiceInstance 向 Nacos 批量注册多个服务实例
func batchRegisterServiceInstance(client naming_client.INamingClient, param vo.BatchRegisterInstanceParam) {// 调用 BatchRegisterInstance 方法批量注册服务实例success, err := client.BatchRegisterInstance(param)if !success || err != nil {// 如果批量注册失败,抛出 panic 并打印错误信息panic("BatchRegisterServiceInstance failed!" + err.Error())}// 打印批量注册参数和结果fmt.Printf("BatchRegisterServiceInstance,param:%+v,result:%+v \n\n", param, success)
}// deRegisterServiceInstance 从 Nacos 取消注册一个服务实例
func deRegisterServiceInstance(client naming_client.INamingClient, param vo.DeregisterInstanceParam) {// 调用 DeregisterInstance 方法取消注册服务实例success, err := client.DeregisterInstance(param)if !success || err != nil {// 如果取消注册失败,抛出 panic 并打印错误信息panic("DeRegisterServiceInstance failed!" + err.Error())}// 打印取消注册参数和结果fmt.Printf("DeRegisterServiceInstance,param:%+v,result:%+v \n\n", param, success)
}// updateServiceInstance 更新 Nacos 中已注册的服务实例信息
func updateServiceInstance(client naming_client.INamingClient, param vo.UpdateInstanceParam) {// 调用 UpdateInstance 方法更新服务实例信息success, err := client.UpdateInstance(param)if !success || err != nil {// 如果更新失败,抛出 panic 并打印错误信息panic("UpdateInstance failed!" + err.Error())}// 打印更新参数和结果fmt.Printf("UpdateServiceInstance,param:%+v,result:%+v \n\n", param, success)
}// getService 从 Nacos 获取指定服务的信息
func getService(client naming_client.INamingClient, param vo.GetServiceParam) {// 调用 GetService 方法获取服务信息service, err := client.GetService(param)if err != nil {// 如果获取服务信息失败,抛出 panic 并打印错误信息panic("GetService failed!" + err.Error())}// 打印获取服务信息的参数和服务信息结果fmt.Printf("GetService,param:%+v, result:%+v \n\n", param, service)
}// selectAllInstances 从 Nacos 获取指定服务的所有实例
func selectAllInstances(client naming_client.INamingClient, param vo.SelectAllInstancesParam) {// 调用 SelectAllInstances 方法获取所有实例instances, err := client.SelectAllInstances(param)if err != nil {// 如果获取所有实例失败,抛出 panic 并打印错误信息panic("SelectAllInstances failed!" + err.Error())}// 打印获取所有实例的参数和实例信息结果fmt.Printf("SelectAllInstance,param:%+v, result:%+v \n\n", param, instances)
}// selectInstances 从 Nacos 获取指定服务的健康实例
func selectInstances(client naming_client.INamingClient, param vo.SelectInstancesParam) {// 调用 SelectInstances 方法获取健康实例instances, err := client.SelectInstances(param)if err != nil {// 如果获取健康实例失败,抛出 panic 并打印错误信息panic("SelectInstances failed!" + err.Error())}// 打印获取健康实例的参数和实例信息结果fmt.Printf("SelectInstances,param:%+v, result:%+v \n\n", param, instances)
}// selectOneHealthyInstance 从 Nacos 获取一个健康实例(使用加权随机算法)
func selectOneHealthyInstance(client naming_client.INamingClient, param vo.SelectOneHealthInstanceParam) {// 调用 SelectOneHealthyInstance 方法获取一个健康实例instances, err := client.SelectOneHealthyInstance(param)if err != nil {// 如果获取健康实例失败,抛出 panic 并打印错误信息panic("SelectOneHealthyInstance failed!")}// 打印获取健康实例的参数和实例信息结果fmt.Printf("SelectOneHealthyInstance,param:%+v, result:%+v \n\n", param, instances)
}// subscribe 订阅 Nacos 中指定服务的变化
func subscribe(client naming_client.INamingClient, param *vo.SubscribeParam) {// 调用 Subscribe 方法订阅服务变化client.Subscribe(param)
}// unSubscribe 取消订阅 Nacos 中指定服务的变化
func unSubscribe(client naming_client.INamingClient, param *vo.SubscribeParam) {// 调用 Unsubscribe 方法取消订阅服务变化client.Unsubscribe(param)
}// getAllService 从 Nacos 获取指定分组下的所有服务名称列表
func getAllService(client naming_client.INamingClient, param vo.GetAllServiceInfoParam) {// 调用 GetAllServicesInfo 方法获取所有服务名称列表service, err := client.GetAllServicesInfo(param)if err != nil {// 如果获取服务名称列表失败,抛出 panic 并打印错误信息panic("GetAllService failed!")}// 打印获取服务名称列表的参数和服务名称列表结果fmt.Printf("GetAllService,param:%+v, result:%+v \n\n", param, service)
}

服务的注册

registerServiceInstance(client, vo.RegisterInstanceParam{Ip:          "10.0.0.10",                          // 服务实例的 IP 地址Port:        8848,                                 // 服务实例的端口号ServiceName: "test.go",                            // 服务名称GroupName:   "group-a",                            // 分组名称ClusterName: "cluster-a",                          // 集群名称Weight:      10,                                   // 权重Enable:      true,                                 // 是否启用Healthy:     true,                                 // 是否健康Ephemeral:   true,                                 // 是否为临时实例Metadata:    map[string]string{"idc": "shanghai"}, // 元数据信息
})

这里通过调用函数实现服务的注册,其中test.go只是是服务的名称,在注册服务之后,可以通过通过服务实例的ip地址和端口号进行访问,随后访问这些地址上运行的实际程序。

而这个函数是我们自己实现的,我们可以来看看函数的内部:

func registerServiceInstance(client naming_client.INamingClient, param vo.RegisterInstanceParam) {// 调用 RegisterInstance 方法注册服务实例success, err := client.RegisterInstance(param)if !success || err != nil {// 如果注册失败,抛出 panic 并打印错误信息panic("RegisterServiceInstance failed!" + err.Error())}// 打印注册参数和结果fmt.Printf("RegisterServiceInstance,param:%+v,result:%+v \n\n", param, success)
}

这里直接通过调用client的方法来为我们注册服务,而这个client可以和nacos服务器进行通信,可以注册、取消注册、更新服务实例的信息,以及从 Nacos 服务器获取这些信息。


取消服务的注册

deRegisterServiceInstance(client, vo.DeregisterInstanceParam{Ip:          "10.0.0.10", // 服务实例的 IP 地址Port:        8848,        // 服务实例的端口号ServiceName: "demo.go",   // 服务名称GroupName:   "group-a",   // 分组名称Cluster:     "cluster-a", // 集群名称Ephemeral:   true,        // 必须为临时实例
})

取消注册服务,以上均为必须要传递的参数,用于匹配将要取消注册的服务实例

实现函数

func deRegisterServiceInstance(client naming_client.INamingClient, param vo.DeregisterInstanceParam) {// 调用 DeregisterInstance 方法取消注册服务实例success, err := client.DeregisterInstance(param)if !success || err != nil {// 如果取消注册失败,抛出 panic 并打印错误信息panic("DeRegisterServiceInstance failed!" + err.Error())}// 打印取消注册参数和结果fmt.Printf("DeRegisterServiceInstance,param:%+v,result:%+v \n\n", param, success)
}

和注册的方式类似,直接通过调用cilent的方法,并传递需要取消注册的服务实例的必要参数,用于定位。


批量注册

batchRegisterServiceInstance(client, vo.BatchRegisterInstanceParam{ServiceName: "demo.go", // 服务名称GroupName:   "group-a", // 分组名称Instances: []vo.RegisterInstanceParam{{Ip:          "10.0.0.10",                          // 第一个服务实例的 IP 地址Port:        8848,                                 // 第一个服务实例的端口号Weight:      10,                                   // 权重Enable:      true,                                 // 是否启用Healthy:     true,                                 // 是否健康Ephemeral:   true,                                 // 是否为临时实例ClusterName: "cluster-a",                          // 集群名称Metadata:    map[string]string{"idc": "shanghai"}, // 元数据信息}, {Ip:          "10.0.0.12",                          // 第二个服务实例的 IP 地址Port:        8848,                                 // 第二个服务实例的端口号Weight:      7,                                    // 权重Enable:      true,                                 // 是否启用Healthy:     true,                                 // 是否健康Ephemeral:   true,                                 // 是否为临时实例ClusterName: "cluster-a",                          // 集群名称Metadata:    map[string]string{"idc": "shanghai"}, // 元数据信息}},
})

和名字一样,可以想函数中传递多个参数,用于注册服务,但不同的一点是,服务的名称和分组名称独立了出来,这表示注册的服务均属于同一种服务和分组,而此时传递的结构体中包含了多个instance实例(这个参数就是服务单独注册时使用到的结构体的切片)。

函数实现

func batchRegisterServiceInstance(client naming_client.INamingClient, param vo.BatchRegisterInstanceParam) {// 调用 BatchRegisterInstance 方法批量注册服务实例success, err := client.BatchRegisterInstance(param)if !success || err != nil {// 如果批量注册失败,抛出 panic 并打印错误信息panic("BatchRegisterServiceInstance failed!" + err.Error())}// 打印批量注册参数和结果fmt.Printf("BatchRegisterServiceInstance,param:%+v,result:%+v \n\n", param, success)
}

无需多说,也是通过调用方法来实现服务注册。


http实例自动注册服务

先看看代码的实现,然后我会一步一步细说:

package mainimport ("context""fmt""github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client""net/http""os""os/signal""syscall""time""github.com/nacos-group/nacos-sdk-go/v2/clients""github.com/nacos-group/nacos-sdk-go/v2/common/constant""github.com/nacos-group/nacos-sdk-go/v2/vo"
)func main() {// 1. 创建 Nacos 客户端client := createNacosClient()// 2. 定义服务实例的元数据信息serviceName := "servicetest"   // 服务名称groupName := "group1"          // 分组名称clusterName := "cluster-a"     // 集群名称ip := "127.0.0.1"              // 服务实例的 IP 地址(当前虚拟机的 IP 地址)port := 8080                   // 服务实例的端口号metadata := map[string]string{ // 元数据信息"idc": "shanghai",}// 3. 注册服务实例到 NacosregisterServiceInstance(client, vo.RegisterInstanceParam{Ip:          ip,Port:        uint64(port),ServiceName: serviceName,GroupName:   groupName,ClusterName: clusterName,Weight:      10,Enable:      true,Healthy:     true,Ephemeral:   true,Metadata:    metadata,})// 4. 启动 HTTP 服务http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {fmt.Fprintf(w, "Hello, this is demo.go service at %s:%d!", ip, port)})server := &http.Server{Addr:    fmt.Sprintf("%s:%d", ip, port),Handler: nil,}go func() {fmt.Printf("Starting HTTP server at http://%s:%d\n", ip, port)if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {fmt.Printf("HTTP server failed: %v\n", err)}}()// 5. 监听系统信号,实现优雅关闭quit := make(chan os.Signal, 1)signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)<-quitfmt.Println("Shutting down server...")// 6. 取消注册服务实例deRegisterServiceInstance(client, vo.DeregisterInstanceParam{Ip:          ip,Port:        uint64(port),ServiceName: serviceName,GroupName:   groupName,Cluster:     clusterName,Ephemeral:   true,})// 7. 关闭 HTTP 服务ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)defer cancel()if err := server.Shutdown(ctx); err != nil {fmt.Printf("HTTP server shutdown failed: %v\n", err)}fmt.Println("Server exited gracefully")
}// createNacosClient 创建 Nacos 客户端
func createNacosClient() naming_client.INamingClient {// 创建 ServerConfigsc := []constant.ServerConfig{*constant.NewServerConfig("192.168.195.129",                  // Nacos 服务器的 IP 地址8848,                               // Nacos 服务器的端口号constant.WithContextPath("/nacos"), // Nacos 服务器的上下文路径),}// 创建 ClientConfigcc := *constant.NewClientConfig(constant.WithNamespaceId(""),              // 命名空间 IDconstant.WithTimeoutMs(5000),              // 超时时间constant.WithNotLoadCacheAtStart(true),    // 启动时不加载缓存constant.WithLogDir("/tmp/nacos/log"),     // 日志目录constant.WithCacheDir("/tmp/nacos/cache"), // 缓存目录constant.WithLogLevel("debug"),            // 日志级别constant.WithUsername("nacos"),constant.WithPassword("nacos"),)// 创建命名服务客户端client, err := clients.NewNamingClient(vo.NacosClientParam{ClientConfig:  &cc,ServerConfigs: sc,},)if err != nil {panic("Failed to create Nacos client: " + err.Error())}return client
}// registerServiceInstance 向 Nacos 注册一个服务实例
func registerServiceInstance(client naming_client.INamingClient, param vo.RegisterInstanceParam) {success, err := client.RegisterInstance(param)if !success || err != nil {panic("Failed to register service instance: " + err.Error())}fmt.Printf("Registered service instance: %+v\n", param)
}wd// deRegisterServiceInstance 从 Nacos 取消注册一个服务实例
func deRegisterServiceInstance(client naming_client.INamingClient, param vo.DeregisterInstanceParam) {success, err := client.DeregisterInstance(param)if !success || err != nil {panic("Failed to deregister service instance: " + err.Error())}fmt.Printf("Deregistered service instance: %+v\n", param)
}

创建client

第一步便是我们的创建client,此时我们也需要serverconfig和clientconfig

但是注册的client类型却不一样,注意,我们之前crud的时候采取的并不是naming client而是configclient,此处需要注意以下

func createNacosClient() naming_client.INamingClient {// 创建 ServerConfigsc := []constant.ServerConfig{*constant.NewServerConfig("192.168.195.129",                  // Nacos 服务器的 IP 地址8848,                               // Nacos 服务器的端口号constant.WithContextPath("/nacos"), // Nacos 服务器的上下文路径),}// 创建 ClientConfigcc := *constant.NewClientConfig(constant.WithNamespaceId(""),              // 命名空间 IDconstant.WithTimeoutMs(5000),              // 超时时间constant.WithNotLoadCacheAtStart(true),    // 启动时不加载缓存constant.WithLogDir("/tmp/nacos/log"),     // 日志目录constant.WithCacheDir("/tmp/nacos/cache"), // 缓存目录constant.WithLogLevel("debug"),            // 日志级别constant.WithUsername("nacos"),constant.WithPassword("nacos"),)// 创建命名服务客户端client, err := clients.NewNamingClient(vo.NacosClientParam{ClientConfig:  &cc,ServerConfigs: sc,},)if err != nil {panic("Failed to create Nacos client: " + err.Error())}return client
}

其他都是一样的,需要指定注册在哪一个服务器,哪一个命名空间。

注册服务到nacos服务器

serviceName := "servicetest"   // 服务名称
groupName := "group1"          // 分组名称
clusterName := "cluster-a"     // 集群名称
ip := "127.0.0.1"              // 服务实例的 IP 地址(当前虚拟机的 IP 地址)
port := 8080                   // 服务实例的端口号
metadata := map[string]string{ // 元数据信息"idc": "shanghai",
}// 3. 注册服务实例到 Nacos
registerServiceInstance(client, vo.RegisterInstanceParam{Ip:          ip,Port:        uint64(port),ServiceName: serviceName,GroupName:   groupName,ClusterName: clusterName,Weight:      10,Enable:      true,Healthy:     true,Ephemeral:   true,Metadata:    metadata,
})

此处我们需要指定当前服务实例所在的ip,端口,以及服务名称等一系列参数,这里只是想nacos服务器表示了这个服务时存在的,随后我们再启动我们的http服务。

启动http服务

http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {fmt.Fprintf(w, "Hello, this is demo.go service at %s:%d!", ip, port)
})server := &http.Server{Addr:    fmt.Sprintf("%s:%d", ip, port),Handler: nil,
}go func() {fmt.Printf("Starting HTTP server at http://%s:%d\n", ip, port)if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {fmt.Printf("HTTP server failed: %v\n", err)}
}()

这里无需多说,就是随便启动了一个http服务

优雅结束

// 5. 监听系统信号,实现优雅关闭
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quitfmt.Println("Shutting down server...")// 6. 取消注册服务实例
deRegisterServiceInstance(client, vo.DeregisterInstanceParam{Ip:          ip,Port:        uint64(port),ServiceName: serviceName,GroupName:   groupName,Cluster:     clusterName,Ephemeral:   true,
})// 7. 关闭 HTTP 服务
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
if err := server.Shutdown(ctx); err != nil {fmt.Printf("HTTP server shutdown failed: %v\n", err)
}fmt.Println("Server exited gracefully")

最后优雅的结束。


结语

关于这篇文章,主要写出来也是想逼自己去看看官方的示例,以便于自己更能理解nacos是怎么运作的,在此之前我网上查了很多资料,也问了AI,但都没学到什么东西说实话,毕竟Golang目前的学习环境跟java比起来真的差远了,反而今晚上看了一会官方给的示例让我收获很大…

所以真的得去看官方的文档。

以上就是我今天所学到的内容,希望对你也会有帮助,如果有问题,欢迎给我留言

毕竟我也只是个初学者,如果有错误希望包涵以下

相关文章:

【Golang/nacos】nacos配置的增删查改,以及服务注册的golang实例及分析

前言 本文分析的实例来源于nacos在github上的开源仓库 nacos配置的增删查改 先具体来看一段代码&#xff0c;我将逐步分析每一段的作用 package mainimport ("fmt""time""github.com/nacos-group/nacos-sdk-go/clients""github.com/naco…...

RabbitMQ集群安装rabbitmq_delayed_message_exchange

1、单节点安装rabbitmq安装延迟队列 安装延迟队列rabbitmq_delayed_message_exchange可以参考这个文章&#xff1a; rabbitmq安装延迟队列-CSDN博客 2、集群安装rabbitmq_delayed_message_exchange 在第二个节点 join_cluster 之后&#xff0c;start_app 就会报错了 (CaseC…...

Linux UDP 编程详解

一、引言 在网络编程领域&#xff0c;UDP&#xff08;User Datagram Protocol&#xff0c;用户数据报协议&#xff09;作为一种轻量级的传输层协议&#xff0c;具有独特的优势和适用场景。与 TCP&#xff08;Transmission Control Protocol&#xff0c;传输控制协议&#xff0…...

【2024年华为OD机试】(B卷,100分)- 计算最接近的数 (Java JS PythonC/C++)

一、问题描述 题目解析 我们需要找到一个下标 i&#xff0c;使得表达式 X[i] - X[i 1] - ... - X[i K - 1] 的结果最接近于数组的中位数。如果有多个 i 满足条件&#xff0c;则返回最大的 i。 关键点&#xff1a; 中位数计算&#xff1a; 将数组排序后&#xff0c;中位数…...

Pytorch 自学笔记(三):利用自定义文本数据集构建Dataset和DataLoader

Pytorch 自学笔记&#xff08;三&#xff09; 1. Dataset与DataLoader1.1 torch.utils.data.Dataset1.2 torch.utils.data.DataLoader Pytorch 自学笔记系列的第三篇。针对Pytorch的Dataset和DataLoader进行简单的介绍&#xff0c;同时&#xff0c;介绍如何使用自定义文本数据集…...

QT 使用QSqlTableModel对数据库进行创建,插入,显示

文章目录 效果图概述功能点代码分析初始数据插入数据数据显示 总结 效果图 概述 本案例用于对数据库中的数据进行显示等其他操作&#xff0c;其他表格筛选&#xff0c;过滤等功能可看此博客 框架&#xff1a;数据模型使用QSqlTableModel&#xff0c;视图使用QTableView&#x…...

如何学习Transformer架构

Transformer架构自提出以来&#xff0c;在自然语言处理领域引发了革命性的变化。作为一种基于注意力机制的模型&#xff0c;Transformer解决了传统序列模型在并行化和长距离依赖方面的局限性。本文将探讨Transformer论文《Attention is All You Need》与Hugging Face Transform…...

浅谈云计算22 | Kubernetes容器编排引擎

Kubernetes容器编排引擎 一、Kubernetes管理对象1.1 Kubernetes组件和架构1.2 主要管理对象类型 二、Kubernetes 服务2.1 服务的作用与原理2.2 服务类型 三、Kubernetes网络管理3.1 网络模型与目标3.2 网络组件3.2.1 kube-proxy3.2.2 网络插件 3.3 网络通信流程 四、Kubernetes…...

计算 SAMOut V3 在将词汇表从1万 增加到6千万的情况下能够减少多少参数

当我们将词汇表从 60,000,000&#xff08;六千万&#xff09;减少到 10,000 时&#xff0c;实际上是在缩小模型的词嵌入层及其共享的语言模型头&#xff08;LM Head&#xff09;的规模。这将导致参数量显著减少。我们可以通过以下步骤来计算具体的参数减少量。 参数量减少计算…...

03.选择排序

一、题目思路 选择排序是一种简单直观的排序算法。它的工作原理是&#xff1a;首先在未排序序列中找到最小&#xff08;或最大&#xff09;元素&#xff0c;存放到排序序列的起始位置&#xff0c;然后&#xff0c;再从剩余未排序元素中继续寻找最小&#xff08;或最大&#xff…...

02_登录窗口

新建场景 重命名为GameRoot 双击GameRoot进入新场景 同样摄像机清除格式 删除平行光并关闭渲染灯光的天空盒 新建空节点重命名为GameRoot GameRoot为游戏的根节点 在整个游戏中都不会被删除 在游戏的根节点下创建UI的根节点Canvas 创建一个空节点 作为UI根节点下的 登录场景UI…...

NodeJS | 搭建本地/公网服务器 live-server 的使用与安装

目录 介绍 安装 live-server 安装方法 安装后的验证 环境变量问题 Node.js 环境变量未配置正确 全局安装的 live-server 路径未添加到环境变量 运行测试 默认访问主界面 访问文件 报错信息与解决 问题一&#xff1a;未知命令 问题二&#xff1a;拒绝脚本 公网配置…...

SystemUI 实现音量条同步功能

需求&#xff1a;SystemUI 实现音量条同步功能 具体问题 以前在SystemUI 下拉框添加了音量条控制&#xff0c;目前发现在SystemUI下拉框显示状态的情况下&#xff0c; 按键或者底部虚拟导航点击音量加减时候&#xff0c;SystemUI音量条不更新。 如下图&#xff1a;两个Syste…...

嵌入式知识点总结 C/C++ 专题提升(一)-关键字

针对于嵌入式软件杂乱的知识点总结起来&#xff0c;提供给读者学习复习对下述内容的强化。 目录 1.C语言宏中"#“和"##"的用法 1.1.(#)字符串化操作符 1.2.(##)符号连接操作符 2.关键字volatile有什么含意?并举出三个不同的例子? 2.1.并行设备的硬件寄存…...

基础入门-传输加密数据格式编码算法密文存储代码混淆逆向保护安全影响

知识点&#xff1a; 1、传输格式&传输数据-类型&编码&算法 2、密码存储&代码混淆-不可逆&非对称性 一、演示案例-传输格式&传输数据-类型&编码&算法 传输格式 JSON XML WebSockets HTML 二进制 自定义 WebSockets&#xff1a;聊天交互较常…...

几个Linux系统安装体验(续): 统信桌面系统

本文介绍统信桌面系统&#xff08;uos&#xff09;的安装。 下载 下载地址&#xff1a; https://www.chinauos.com/resource/download-professional 下载文件&#xff1a;本文下载文件名称为uos-desktop-20-professional-1070-amd64.iso。 下载注意事项&#xff1a;可直接下…...

算法日记6.StarryCoding P52:我们都需要0(异或)

一、题目 二、题解&#xff1a; 1、对于这道题&#xff0c;题意为让我们寻找一个数x使得 b[i]a[i]^x&#xff0c; 并且b[1]^b[2]^b[3]^ b[4]^b[5]....0 2、我们把b[i]给拆开&#xff0c;可以得到 3、又因为^满足结合律&#xff0c;因此&#xff0c;可以把括号给拆开 4、接着…...

【网络协议】RFC3164-The BSD syslog Protocol

引言 Syslog常被称为系统日志或系统记录&#xff0c;是一种标准化的协议&#xff0c;用于网络设备、服务器和应用程序向中央Syslog服务器发送日志消息。互联网工程任务组&#xff08;IETF&#xff09;发布的RFC 3164&#xff0c;专门定义了BSD Syslog协议的规范和实现方式。通…...

SpringCloud -根据服务名获取服务运行实例并进行负载均衡

Nacos注册中心 每个服务启动之后都要向注册中心发送服务注册请求&#xff0c;注册中心可以和各个注册客户端自定义协议实现服务注册和发现。 pom.xml <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-na…...

CentOS 安装Redis

1. 安装 Redis 安装 EPEL 仓库&#xff08;对于 CentOS/RHEL 系统&#xff09;&#xff1a; 首先安装 EPEL 仓库&#xff0c;因为 Redis 存在于 EPEL 仓库中&#xff1a; yum install epel-release安装 Redis 数据库&#xff1a; yum install redis2. 修改 Redis 配置文件 …...

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…...

DockerHub与私有镜像仓库在容器化中的应用与管理

哈喽&#xff0c;大家好&#xff0c;我是左手python&#xff01; Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库&#xff0c;用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...

循环冗余码校验CRC码 算法步骤+详细实例计算

通信过程&#xff1a;&#xff08;白话解释&#xff09; 我们将原始待发送的消息称为 M M M&#xff0c;依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)&#xff08;意思就是 G &#xff08; x ) G&#xff08;x) G&#xff08;x) 是已知的&#xff09;&#xff0…...

Java 加密常用的各种算法及其选择

在数字化时代&#xff0c;数据安全至关重要&#xff0c;Java 作为广泛应用的编程语言&#xff0c;提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景&#xff0c;有助于开发者在不同的业务需求中做出正确的选择。​ 一、对称加密算法…...

【Zephyr 系列 10】实战项目:打造一个蓝牙传感器终端 + 网关系统(完整架构与全栈实现)

🧠关键词:Zephyr、BLE、终端、网关、广播、连接、传感器、数据采集、低功耗、系统集成 📌目标读者:希望基于 Zephyr 构建 BLE 系统架构、实现终端与网关协作、具备产品交付能力的开发者 📊篇幅字数:约 5200 字 ✨ 项目总览 在物联网实际项目中,**“终端 + 网关”**是…...

智能仓储的未来:自动化、AI与数据分析如何重塑物流中心

当仓库学会“思考”&#xff0c;物流的终极形态正在诞生 想象这样的场景&#xff1a; 凌晨3点&#xff0c;某物流中心灯火通明却空无一人。AGV机器人集群根据实时订单动态规划路径&#xff1b;AI视觉系统在0.1秒内扫描包裹信息&#xff1b;数字孪生平台正模拟次日峰值流量压力…...

使用 SymPy 进行向量和矩阵的高级操作

在科学计算和工程领域&#xff0c;向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能&#xff0c;能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作&#xff0c;并通过具体…...

论文笔记——相干体技术在裂缝预测中的应用研究

目录 相关地震知识补充地震数据的认识地震几何属性 相干体算法定义基本原理第一代相干体技术&#xff1a;基于互相关的相干体技术&#xff08;Correlation&#xff09;第二代相干体技术&#xff1a;基于相似的相干体技术&#xff08;Semblance&#xff09;基于多道相似的相干体…...

SQL慢可能是触发了ring buffer

简介 最近在进行 postgresql 性能排查的时候,发现 PG 在某一个时间并行执行的 SQL 变得特别慢。最后通过监控监观察到并行发起得时间 buffers_alloc 就急速上升,且低水位伴随在整个慢 SQL,一直是 buferIO 的等待事件,此时也没有其他会话的争抢。SQL 虽然不是高效 SQL ,但…...

【C++进阶篇】智能指针

C内存管理终极指南&#xff1a;智能指针从入门到源码剖析 一. 智能指针1.1 auto_ptr1.2 unique_ptr1.3 shared_ptr1.4 make_shared 二. 原理三. shared_ptr循环引用问题三. 线程安全问题四. 内存泄漏4.1 什么是内存泄漏4.2 危害4.3 避免内存泄漏 五. 最后 一. 智能指针 智能指…...