使用Golang客户端实现Nacos服务注册发现和配置管理

背景

最近需要把Golang实现的一个web项目集成到基于Spring Cloud Alibaba的微服务体系中,走Spring Cloud Gateway网关路由实现统一的鉴权入口。

软件版本

组件名称组件版本
Nacos2.2.0
Go1.21.0
Ginv1.9.1
Nacos-sdk-gov2.2.5
Spring Cloud2021.0.2
Spring Cloud Alibaba2021.0.1.0

服务注册发现

项目图示
在这里插入图片描述

服务注册和发现

Golang客户端的注册发现使用的Nacos官方仓库里面的一个实现:https://github.com/nacos-group/nacos-sdk-go,我这边的实现大多数也是参考的官方提供的样例。

首先,初始化clientConfig和serverConfig的配置

sc := []constant.ServerConfig{
  *constant.NewServerConfig("localhost", 8848, constant.WithContextPath("/nacos")),
}

cc := *constant.NewClientConfig(
  constant.WithNamespaceId("xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"),
  constant.WithTimeoutMs(5000),
  constant.WithNotLoadCacheAtStart(true),
  constant.WithLogDir("/tmp/nacos/log"),
  constant.WithCacheDir("/tmp/nacos/cache"),
  constant.WithUsername("nacos"),
  constant.WithPassword("nacos"),
)

创建服务发现客户端

client, _ := clients.NewNamingClient(
  vo.NacosClientParam{
    ClientConfig:  &cc,
    ServerConfigs: sc,
  },
)

服务注册

// 注册服务
registerServiceInstance(client, vo.RegisterInstanceParam{
  Ip:          getHostIp(),
  Port:        8777,
  ServiceName: ServiceName,
  Weight:      10,
  Enable:      true,
  Healthy:     true,
  Ephemeral:   true,
})


// 注册服务
func registerServiceInstance(nacosClient naming_client.INamingClient, param vo.RegisterInstanceParam) {
	success, err := nacosClient.RegisterInstance(param)
	if !success || err != nil {
		panic("register Service Instance failed!")
	}
}

// 获取本机ip地址
func getHostIp() string {
	conn, err := net.Dial("udp", "8.8.8.8:53")
	if err != nil {
		fmt.Println("get current host ip err: ", err)
		return ""
	}
	addr := conn.LocalAddr().(*net.UDPAddr)
	ip := strings.Split(addr.String(), ":")[0]
	return ip
}

启动服务,验证ok
在这里插入图片描述

客户端负载均衡

作为Go语言初学者,没有详细去了解Go中类似@LoadBalanced或者Feign的框架(后续可能会补充一下),然后我这边就是获取实例解析IP和端口信息,然后直接使用Go原生的http库进行的调用。

获取实例方法

// 获取一个健康的实例
func selectOneHealthyInstance(client naming_client.INamingClient, serviceName string) (instance *model.Instance) {
	instances, err := client.SelectOneHealthyInstance(vo.SelectOneHealthInstanceParam{
		ServiceName: serviceName,
	})
	if err != nil {
		panic("SelectOneHealthyInstance failed!")
	}
	return instances
}

具体调用

func hello(c *gin.Context) {
	name := c.Param("name")
	instance := selectOneHealthyInstance(NamingClient, "server-java")
	url := fmt.Sprintf("http://%s:%d/hello/%s", instance.Ip, instance.Port, name)
	resp, _ := http.Get(url)
	defer resp.Body.Close()
	body, _ := io.ReadAll(resp.Body)
	c.String(http.StatusOK, string(body))
}

在server-java中,提供了一个rest接口

/**
 * 启动类
 *
 * @author yuanzhihao
 * @since 2024/3/4
 */
@RestController
@RequestMapping
@SpringBootApplication
public class Application {


    @GetMapping("/hello/{name}")
    public String hello(@PathVariable("name") String name) {
        return "Hello " + name;
    }

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

请求 http://localhost:8777/hello/yuan, 调用成功
在这里插入图片描述

配置管理

图示
在这里插入图片描述

第一步和注册发现一样,初始化clientConfig和serverConfig的配置,略

创建动态配置客户端

configClient, _ := clients.NewConfigClient(
  vo.NacosClientParam{
    ClientConfig:  &cc,
    ServerConfigs: sc,
  },
)

拉取指定data-id的配置

config, _ := ConfigClient.GetConfig(vo.ConfigParam{
  DataId: ServiceName,
})
fmt.Printf("config is " + config)

我这边写了一个rest的接口,可以指定获取某个具体配置的值

// 初始化服务端
func ServerSetup() {
	r := gin.Default()
	r.GET("/hello/:name", hello)
	r.GET("/config", getSpecifiedConfig)
	r.Run(":8777")
}

// 获取指定的配置
func getSpecifiedConfig(c *gin.Context) {
	param := c.DefaultQuery("name", "")
	config, _ := ConfigClient.GetConfig(vo.ConfigParam{
		DataId: ServiceName,
	})
	fmt.Printf("config is " + config)
	// 解析YAML数据
	var data map[string]interface{}
	err := yaml.Unmarshal([]byte(config), &data)
	if err != nil {
		fmt.Println("error unmarshalling YAML", err)
	}
	value := getValue(data, param)
	c.String(http.StatusOK, "param [ "+param+" ] value is "+value)
}

func getValue(data map[string]interface{}, keyPath string) string {
	keys := strings.Split(keyPath, ".")
	var value interface{} = data
	for _, key := range keys {
		if v, ok := value.(map[string]interface{}); ok {
			value = v[key]
		} else {
			return ""
		}
	}
	if v, ok := value.(string); ok {
		return v
	}
	return fmt.Sprintf("%v", value)
}

配置client-go如下

my:
  name: yuan
  age: 27
  city: Nanjing

调用请求 http://localhost:8777/config?name=my.name,http://localhost:8777/config?name=my.age ok
在这里插入图片描述
在这里插入图片描述

结语

参考地址:https://github.com/nacos-group/nacos-sdk-go

代码地址:https://github.com/yzh19961031/blogDemo/tree/master/go-nacos

Logo

一起探索未来云端世界的核心,云原生技术专区带您领略创新、高效和可扩展的云计算解决方案,引领您在数字化时代的成功之路。

更多推荐