【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配置的增删查改 先具体来看一段代码,我将逐步分析每一段的作用 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可以参考这个文章: rabbitmq安装延迟队列-CSDN博客 2、集群安装rabbitmq_delayed_message_exchange 在第二个节点 join_cluster 之后,start_app 就会报错了 (CaseC…...

Linux UDP 编程详解
一、引言 在网络编程领域,UDP(User Datagram Protocol,用户数据报协议)作为一种轻量级的传输层协议,具有独特的优势和适用场景。与 TCP(Transmission Control Protocol,传输控制协议࿰…...

【2024年华为OD机试】(B卷,100分)- 计算最接近的数 (Java JS PythonC/C++)
一、问题描述 题目解析 我们需要找到一个下标 i,使得表达式 X[i] - X[i 1] - ... - X[i K - 1] 的结果最接近于数组的中位数。如果有多个 i 满足条件,则返回最大的 i。 关键点: 中位数计算: 将数组排序后,中位数…...
Pytorch 自学笔记(三):利用自定义文本数据集构建Dataset和DataLoader
Pytorch 自学笔记(三) 1. Dataset与DataLoader1.1 torch.utils.data.Dataset1.2 torch.utils.data.DataLoader Pytorch 自学笔记系列的第三篇。针对Pytorch的Dataset和DataLoader进行简单的介绍,同时,介绍如何使用自定义文本数据集…...

QT 使用QSqlTableModel对数据库进行创建,插入,显示
文章目录 效果图概述功能点代码分析初始数据插入数据数据显示 总结 效果图 概述 本案例用于对数据库中的数据进行显示等其他操作,其他表格筛选,过滤等功能可看此博客 框架:数据模型使用QSqlTableModel,视图使用QTableView&#x…...
如何学习Transformer架构
Transformer架构自提出以来,在自然语言处理领域引发了革命性的变化。作为一种基于注意力机制的模型,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(六千万)减少到 10,000 时,实际上是在缩小模型的词嵌入层及其共享的语言模型头(LM Head)的规模。这将导致参数量显著减少。我们可以通过以下步骤来计算具体的参数减少量。 参数量减少计算…...
03.选择排序
一、题目思路 选择排序是一种简单直观的排序算法。它的工作原理是:首先在未排序序列中找到最小(或最大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(或最大ÿ…...

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

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

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

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

基础入门-传输加密数据格式编码算法密文存储代码混淆逆向保护安全影响
知识点: 1、传输格式&传输数据-类型&编码&算法 2、密码存储&代码混淆-不可逆&非对称性 一、演示案例-传输格式&传输数据-类型&编码&算法 传输格式 JSON XML WebSockets HTML 二进制 自定义 WebSockets:聊天交互较常…...

几个Linux系统安装体验(续): 统信桌面系统
本文介绍统信桌面系统(uos)的安装。 下载 下载地址: https://www.chinauos.com/resource/download-professional 下载文件:本文下载文件名称为uos-desktop-20-professional-1070-amd64.iso。 下载注意事项:可直接下…...

算法日记6.StarryCoding P52:我们都需要0(异或)
一、题目 二、题解: 1、对于这道题,题意为让我们寻找一个数x使得 b[i]a[i]^x, 并且b[1]^b[2]^b[3]^ b[4]^b[5]....0 2、我们把b[i]给拆开,可以得到 3、又因为^满足结合律,因此,可以把括号给拆开 4、接着…...

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

SpringCloud -根据服务名获取服务运行实例并进行负载均衡
Nacos注册中心 每个服务启动之后都要向注册中心发送服务注册请求,注册中心可以和各个注册客户端自定义协议实现服务注册和发现。 pom.xml <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-na…...
CentOS 安装Redis
1. 安装 Redis 安装 EPEL 仓库(对于 CentOS/RHEL 系统): 首先安装 EPEL 仓库,因为 Redis 存在于 EPEL 仓库中: yum install epel-release安装 Redis 数据库: yum install redis2. 修改 Redis 配置文件 …...

龙虎榜——20250610
上证指数放量收阴线,个股多数下跌,盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型,指数短线有调整的需求,大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的:御银股份、雄帝科技 驱动…...
DockerHub与私有镜像仓库在容器化中的应用与管理
哈喽,大家好,我是左手python! Docker Hub的应用与管理 Docker Hub的基本概念与使用方法 Docker Hub是Docker官方提供的一个公共镜像仓库,用户可以在其中找到各种操作系统、软件和应用的镜像。开发者可以通过Docker Hub轻松获取所…...

循环冗余码校验CRC码 算法步骤+详细实例计算
通信过程:(白话解释) 我们将原始待发送的消息称为 M M M,依据发送接收消息双方约定的生成多项式 G ( x ) G(x) G(x)(意思就是 G ( x ) G(x) G(x) 是已知的)࿰…...
Java 加密常用的各种算法及其选择
在数字化时代,数据安全至关重要,Java 作为广泛应用的编程语言,提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景,有助于开发者在不同的业务需求中做出正确的选择。 一、对称加密算法…...

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

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

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

论文笔记——相干体技术在裂缝预测中的应用研究
目录 相关地震知识补充地震数据的认识地震几何属性 相干体算法定义基本原理第一代相干体技术:基于互相关的相干体技术(Correlation)第二代相干体技术:基于相似的相干体技术(Semblance)基于多道相似的相干体…...
SQL慢可能是触发了ring buffer
简介 最近在进行 postgresql 性能排查的时候,发现 PG 在某一个时间并行执行的 SQL 变得特别慢。最后通过监控监观察到并行发起得时间 buffers_alloc 就急速上升,且低水位伴随在整个慢 SQL,一直是 buferIO 的等待事件,此时也没有其他会话的争抢。SQL 虽然不是高效 SQL ,但…...

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