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

前言

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

nacos配置的增删查改


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

package main

import (
    "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() {
    // 创建ServerConfig
    sc := []constant.ServerConfig{
       *constant.NewServerConfig(
          "192.168.195.129",                  // Nacos服务器的IP地址
          8848,                               // Nacos服务器的端口号
          constant.WithContextPath("/nacos"), // 上下文路径
       ),
    }

    // 创建ClientConfig
    cc := *constant.NewClientConfig(
       constant.WithNamespaceId("ace1b5fe-80c3-4fab-b89a-625f9ff41093"), // 命名空间ID
       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.NewConfigClient(
       vo.NacosClientParam{
          ClientConfig:  &cc, // 客户端配置
          ServerConfigs: sc,  // 服务器配置切片
       },
    )
    if err != nil {
       panic(err) // 如果创建客户端失败,程序终止并打印错误信息
    }

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

    _, err = client.PublishConfig(vo.ConfigParam{
       DataId:  "test-data-2",  // 第二个配置的数据ID
       Group:   "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",  // 要获取的配置的数据ID
       Group:  "test-group", // 要获取的配置的分组
    })
    fmt.Println("GetConfig,config :" + content) // 打印获取到的配置内容

    // 监听配置变更
    err = client.ListenConfig(vo.ConfigParam{
       DataId: "test-data",  // 要监听的配置的数据ID
       Group:  "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", // 第二个要监听的配置的数据ID
       Group:  "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",   // 要修改的配置的数据ID
       Group:   "test-group",  // 要修改的配置的分组
       Content: "test-listen", // 修改后的配置内容
    })
    if err != nil {
       fmt.Printf("PublishConfig err:%+v \n", err) // 如果修改配置失败,打印错误信息
    }

    _, err = client.PublishConfig(vo.ConfigParam{
       DataId:  "test-data-2", // 第二个要修改的配置的数据ID
       Group:   "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",  // 要删除的配置的数据ID
       Group:  "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",  // 要取消监听的配置的数据ID
       Group:  "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"), // 命名空间ID
    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"),
)

这段代码配置了客户端的行为,特别是指定了要操作的命名空间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",    // 配置的数据ID
    Group:   "test-group",   // 配置的分组
    Content: "hello world!", // 配置的内容
})
if err != nil {
    fmt.Printf("PublishConfig err:%+v \n", err) // 如果发布配置失败,打印错误信息
}

_, err = client.PublishConfig(vo.ConfigParam{
    DataId:  "test-data-2",  // 第二个配置的数据ID
    Group:   "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",  // 要获取的配置的数据ID
    Group:  "test-group", // 要获取的配置的分组
})
fmt.Println("GetConfig,config :" + content) // 打印获取到的配置内容

// 监听配置变更
err = client.ListenConfig(vo.ConfigParam{
    DataId: "test-data",  // 要监听的配置的数据ID
    Group:  "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",   // 要修改的配置的数据ID
    Group:   "test-group",  // 要修改的配置的分组
    Content: "test-listen", // 修改后的配置内容
})
_, err = client.DeleteConfig(vo.ConfigParam{
    DataId: "test-data",  // 要删除的配置的数据ID
    Group:  "test-group", // 要删除的配置的分组
})
if err != nil {
    fmt.Printf("DeleteConfig err:%+v \n", err) // 如果删除配置失败,打印错误信息
}

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


nacos服务注册

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

package main

import (
    "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) // 如果客户端创建失败,则抛出异常
    }

    // 注册服务实例到 Nacos
    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"}, // 元数据信息
    })

    //从 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 main

import (
    "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. 注册服务实例到 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,
    })

    // 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)
    <-quit

    fmt.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 {
    // 创建 ServerConfig
    sc := []constant.ServerConfig{
       *constant.NewServerConfig(
          "192.168.195.129",                  // Nacos 服务器的 IP 地址
          8848,                               // Nacos 服务器的端口号
          constant.WithContextPath("/nacos"), // Nacos 服务器的上下文路径
       ),
    }

    // 创建 ClientConfig
    cc := *constant.NewClientConfig(
       constant.WithNamespaceId(""),              // 命名空间 ID
       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("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 {
    // 创建 ServerConfig
    sc := []constant.ServerConfig{
       *constant.NewServerConfig(
          "192.168.195.129",                  // Nacos 服务器的 IP 地址
          8848,                               // Nacos 服务器的端口号
          constant.WithContextPath("/nacos"), // Nacos 服务器的上下文路径
       ),
    }

    // 创建 ClientConfig
    cc := *constant.NewClientConfig(
       constant.WithNamespaceId(""),              // 命名空间 ID
       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("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)
<-quit

fmt.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比起来真的差远了,反而今晚上看了一会官方给的示例让我收获很大…

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

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

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

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

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

相关文章

Nvidia Blackwell架构深度剖析:深入了解RTX 50系列GPU的升级

在CES 2025上&#xff0c;英伟达推出了基于Blackwell架构的GeForce RTX 50系列显卡&#xff0c;包括RTX 5090、RTX 5080、RTX 5070 Ti和RTX 5070。一段时间以来&#xff0c;我们已经知晓了该架构的各种细节&#xff0c;其中许多此前还只是传闻。不过&#xff0c;英伟达近日在20…

面试--你的数据库中密码是如何存储的?

文章目录 三种分类使用 MD5 加密存储加盐存储Base64 编码:常见的对称加密算法常见的非对称加密算法https 传输加密 在开发中需要存储用户的密码&#xff0c;这个密码一定是加密存储的&#xff0c;如果是明文存储那么如果数据库被攻击了&#xff0c;密码就泄露了。 我们要对数据…

【24】Word:小郑-准考证❗

目录 题目 准考证.docx 邮件合并-指定考生生成准考证 Word.docx 表格内容居中表格整体相较于页面居中 考试时一定要做一问保存一问❗ 题目 准考证.docx 插入→表格→将文本转换成表格→✔制表符→确定选中第一列→单击右键→在第一列的右侧插入列→布局→合并单元格&#…

WOA-CNN-GRU-Attention、CNN-GRU-Attention、WOA-CNN-GRU、CNN-GRU四模型对比多变量时序预测

WOA-CNN-GRU-Attention、CNN-GRU-Attention、WOA-CNN-GRU、CNN-GRU四模型对比多变量时序预测 目录 WOA-CNN-GRU-Attention、CNN-GRU-Attention、WOA-CNN-GRU、CNN-GRU四模型对比多变量时序预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 基于WOA-CNN-GRU-Attention、…

Spring Boot整合WebSocket

目录 ?引言 1.WebSocket 基础知识 ?1.1 什么是 WebSocket&#xff1f; ?1.2 WebSocket 的应用场景 ?2.Spring Boot WebSocket 整合步骤 2.1 创建 Spring Boot 项目 2.2 添加 Maven 依赖 2.3 配置 WebSocket 2.4 创建 WebSocket 控制器 2.5 创建前端页面 引言 在…

K8S 集群搭建和访问 Kubernetes 仪表板(Dashboard)

一、环境准备 服务器要求&#xff1a; 最小硬件配置&#xff1a;2核CPU、4G内存、30G硬盘。 服务器可以访问外网。 软件环境&#xff1a; 操作系统&#xff1a;Anolis OS 7.9 Docker&#xff1a;19.03.9版本 Kubernetes&#xff1a;v1.18.0版本 内核版本&#xff1a;5.4.203-…

2024:成长、创作与平衡的年度全景回顾

文章目录 1.前言2.突破自我&#xff1a;2024年个人成长与关键突破3.创作历程&#xff1a;从构想到落笔&#xff0c;2024年的文字旅程4.生活与学业的双重奏&#xff1a;如何平衡博客事业与个人生活5.每一步都是前行&#xff1a;2024年度的挑战与收获6.总结 1.前言 回首2024年&a…

计算机网络 (45)动态主机配置协议DHCP

前言 计算机网络中的动态主机配置协议&#xff08;DHCP&#xff0c;Dynamic Host Configuration Protocol&#xff09;是一种网络管理协议&#xff0c;主要用于自动分配IP地址和其他网络配置参数给连接到网络的设备。 一、基本概念 定义&#xff1a;DHCP是一种网络协议&#xf…

学习记录1

[SUCTF 2019]EasyWeb 直接给了源代码&#xff0c;分析一下 <?php function get_the_flag(){// webadmin will remove your upload file every 20 min!!!! $userdir "upload/tmp_".md5($_SERVER[REMOTE_ADDR]);if(!file_exists($userdir)){mkdir($userdir);}if…

git操作(Windows中GitHub)

使用git控制GitHub中的仓库版本&#xff0c;并在Windows桌面中创建与修改代码&#xff0c;与GitHub仓库进行同步。 创建自己的GitHub仓库 创建一个gen_code实验性仓库用来学习和验证git在Windows下的使用方法&#xff1a; gen_code仓库 注意&#xff0c;创建仓库时不要设置…

Redis的安装和使用--Windows系统

Redis下载地址&#xff1a; windows版本readis下载&#xff08;GitHub&#xff09;&#xff1a; https://github.com/tporadowski/redis/releases &#xff08;推荐使用&#xff09; https://github.com/MicrosoftArchive/redis/releases 官网下载&#xff08;无Windows版本…

【odbc】odbc连接kerberos认证的 hive和spark thriftserver

hive odbc驱动&#xff0c;以下两种都可以 教程&#xff1a;使用 ODBC 和 PowerShell 查询 Apache HiveHive ODBC Connector 2.8.0 for Cloudera Enterprise spark thriftserver本质就是披着hiveserver的外壳的spark server 完成kerberos认证: &#xff08;1&#xff09;可以…

AllData数据中台核心菜单十一:数据集成平台

&#x1f525;&#x1f525; AllData大数据产品是可定义数据中台&#xff0c;以数据平台为底座&#xff0c;以数据中台为桥梁&#xff0c;以机器学习平台为中层框架&#xff0c;以大模型应用为上游产品&#xff0c;提供全链路数字化解决方案。 ✨奥零数据科技官网&#xff1a;…

随遇随记篇

vue 函数 unref() 获取原始值 ref 定义的属性 需要 .value 才能拿到值&#xff0c;unref 直接返回原始值&#xff1b;若属性不是ref 定义的&#xff0c;也是直接返回原始值&#xff1b; /* description: 是否必填*/required?: boolean | Ref<boolean>.....let value …

Python操作Excel——openpyxl使用笔记(1)

0. 模块的安装 使用命令&#xff1a; pip install openpyxl 通过命令&#xff1a; pip show openpyxl 检查安装&#xff0c;当前使用的版本信息如下&#xff1a; 1. 文档的基本操作 1.1 创建&#xff0c;保存和关闭 几个简单的函数调用即可&#xff1a; import openpyx…

STM32之FreeRTOS开发介绍(十九)

STM32F407 系列文章 - freertos&#xff08;十九&#xff09; 目录 前言 一、简述 二、开源网址 三、原理及功能特性 1.原理 2.功能 3.特点 4.优缺点 四、参考书籍 五、实现方式 总结 前言 FreeRTOS是一个免费的、开源的实时操作系统&#xff0c;专为微控制器和嵌入…

《数字图像处理(面向新工科的电工电子信息基础课程系列教材)》重印变更的彩插

禹晶、肖创柏、廖庆敏《数字图像处理&#xff08;面向新工科的电工电子信息基础课程系列教材&#xff09;》

【HarmonyOS NEXT】碰一碰开发分享

关键词&#xff1a;鸿蒙、碰一碰、systemShare、harmonyShare、Share Kit 华为分享新推出碰一碰分享&#xff0c;支持用户通过手机碰一碰发起跨端分享&#xff0c;可实现传输图片、共享wifi等。我们只需调用系统 api 传入所需参数拉起对应分享卡片模板即可&#xff0c;无需对 U…

I2S是什么通信协议?它如何传输音频数据?它和I2C是什么关系?

首先我们先明确一点&#xff0c;I2S和I2C没有什么关系&#xff0c;如果非要扯点共同点的话那就是它们都是由飞利浦制定的。 I2C我们用的比较多&#xff0c;我们用的大多数的传感器模块用的通信协议就是I2C&#xff0c;SPI&#xff0c;UART这些。 而I2S应用领域比较单一&#…

ubuntu20.04有亮度调节条但是调节时亮度不变

尝试了修改grub文件&#xff0c;没有作用&#xff0c;下载了brightness-controllor&#xff0c;问题解决了。 sudo add-apt-repository ppa:apandada1/brightness-controller sudo apt update sudo apt install brightness-controller 之后在应用软件中找到brightness-contro…