写点什么

[ CloudWeGo 微服务实践 - 08 ] Nacos 服务发现扩展 (2)

作者:baiyutang
  • 2021 年 11 月 15 日
  • 本文字数:4482 字

    阅读完需:约 15 分钟

[ CloudWeGo 微服务实践 - 08 ] Nacos 服务发现扩展  (2)

昨天的《 [ CloudWeGo 微服务实践 - 08 ] Nacos 服务发现扩展 (1)》 中,我们准备好了所有的开发环境。今天就准备封装这个组件。

目标

  1. 封装一个以 Nacos 为注册中心的服务发现扩展

环境

  1. MacOS Monterey 12.0.1

  2. go version go1.16.5 darwin/amd64

  3. Nacos 2.0.3

步骤

服务注册

我们先回复一下之前怎么做服务注册的>> [ CloudWeGo 微服务实践 - 05 ] 服务注册(2),作为简单回顾之后,我们尝试重新做一下 Nacos 即可。

创建文件

mkdir registry
cd registry
touch registry.go
复制代码

实现接口

package registry
import ( "log"
"github.com/cloudwego/kitex/pkg/registry")
type nacosRegistry struct{}
func NewNacosRegistry() (registry.Registry, error) { return &nacosRegistry{}, nil}
var _ registry.Registry = (*nacosRegistry)(nil)
func (n *nacosRegistry) Register(info *registry.Info) error { log.Printf("Register info:%v", info) return nil}
func (n *nacosRegistry) Deregister(info *registry.Info) error { log.Printf("Deregister info:%v", info) return nil}
复制代码

引入 Nacos SDK

nacosRegistry 对象中我们增加 nacos 客户端连接的属性,注入依赖

type nacosRegistry struct {	cli naming_client.INamingClient}
复制代码


记得在 import 中添加(IDE 也会自动添加)依赖包

	"github.com/nacos-group/nacos-sdk-go/clients/naming_client"
复制代码


并修改创建实例的 New 方法

func NewNacosRegistry(cli naming_client.INamingClient) (registry.Registry, error) {	return &nacosRegistry{cli: cli}, nil}
复制代码


整体如下:

package registry
import ( "log"
"github.com/cloudwego/kitex/pkg/registry" "github.com/nacos-group/nacos-sdk-go/clients/naming_client")
type nacosRegistry struct { cli naming_client.INamingClient}
func NewNacosRegistry(cli naming_client.INamingClient) (registry.Registry, error) { return &nacosRegistry{cli: cli}, nil}
var _ registry.Registry = (*nacosRegistry)(nil)
func (n *nacosRegistry) Register(info *registry.Info) error { log.Printf("Register info:%v", info) return nil}
func (n *nacosRegistry) Deregister(info *registry.Info) error { log.Printf("Deregister info:%v", info) return nil}
复制代码

Register 单元测试

我们直接用 Goland 的工具生成相关代码



func Test_nacosRegistry_Register(t *testing.T) { sc := []constant.ServerConfig{ *constant.NewServerConfig("127.0.0.1", 8848), }
cc := constant.ClientConfig{ NamespaceId: "public", TimeoutMs: 5000, NotLoadCacheAtStart: true, LogDir: "/tmp/nacos/log", CacheDir: "/tmp/nacos/cache", RotateTime: "1h", MaxAge: 3, LogLevel: "debug", }
client, err := clients.NewNamingClient( vo.NacosClientParam{ ClientConfig: &cc, ServerConfigs: sc, }, ) if err != nil { t.Errorf("err:%v", err) return }
type fields struct { cli naming_client.INamingClient } type args struct { info *registry.Info } tests := []struct { name string fields fields args args wantErr bool }{ { name: "common", fields: fields{client}, args: args{info: &registry.Info{ ServiceName: "demo.baiyutang.local", Addr: &net.TCPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 8848}, Weight: 999, StartTime: time.Now(), Tags: map[string]string{"env": "local"}, }}, wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { n := &nacosRegistry{ cli: tt.fields.cli, } if err := n.Register(tt.args.info); (err != nil) != tt.wantErr { t.Errorf("Register() error = %v, wantErr %v", err, tt.wantErr) } }) }}
复制代码


运行测试用例,我们留意到如下日志:


完善 Register 实现

func (n *nacosRegistry) Register(info *registry.Info) error {	if info.ServiceName == "" {		return fmt.Errorf("registry.Info cannot is empty")	}	host, port, err := net.SplitHostPort(info.Addr.String())	if err == nil {		return fmt.Errorf("parse registry info addr error")	}	p, err := strconv.Atoi(port)	if err != nil {		return err	}	_, e := n.cli.RegisterInstance(vo.RegisterInstanceParam{		Ip:          host,		Port:        uint64(p),		ServiceName: info.ServiceName,		Weight:      float64(info.Weight),		Enable:      true,		Healthy:     true,		Ephemeral:   true,		Metadata:    info.Tags,	})	if e != nil {		return fmt.Errorf("RegisterInstance err:%v", e)	}	return nil}
复制代码

重新运行 Register 单元测试


看到 PASS,就是好消息了,代码运行正常。

验证注册数据

我们直接 curl 查一下 Nanos 的数据:

curl -X GET 'http://127.0.0.1:8848/nacos/v1/ns/instance/list?serviceName=demo.baiyutang.local'
复制代码



成功了。

Deregister 单元测试

还按照刚刚的方法创建用例:


这次我们把创建 Nacos 客户端连接的代码简单封装起来,不用每次每个用例都复制好长的代码:

func getNacosClient() (naming_client.INamingClient, error) {	sc := []constant.ServerConfig{		*constant.NewServerConfig("127.0.0.1", 8848),	}
cc := constant.ClientConfig{ NamespaceId: "public", TimeoutMs: 5000, NotLoadCacheAtStart: true, LogDir: "/tmp/nacos/log", CacheDir: "/tmp/nacos/cache", RotateTime: "1h", MaxAge: 3, LogLevel: "debug", }
return clients.NewNamingClient( vo.NacosClientParam{ ClientConfig: &cc, ServerConfigs: sc, }, )}
复制代码


继续完善用例:


func Test_nacosRegistry_Deregister(t *testing.T) { client, err := getNacosClient() if err != nil { t.Errorf("err:%v", err) return } type fields struct { cli naming_client.INamingClient } type args struct { info *registry.Info } tests := []struct { name string fields fields args args wantErr bool }{ { name: "common", args: args{info: &registry.Info{ ServiceName: "demo.baiyutang.local", Addr: &net.TCPAddr{IP: net.IPv4(127, 0, 0, 1), Port: 8848}, Weight: 999, StartTime: time.Now(), Tags: map[string]string{"env": "local"}, }}, fields: fields{client}, wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { n := &nacosRegistry{ cli: tt.fields.cli, } if err := n.Deregister(tt.args.info); (err != nil) != tt.wantErr { t.Errorf("Deregister() error = %v, wantErr %v", err, tt.wantErr) } }) }}
复制代码

运行 Deregister 单元测试

看到如下日志,就是我们刚刚方法中打的日志,我们继续完善代码



完善 Deregister 实现

func (n *nacosRegistry) Deregister(info *registry.Info) error {	host, port, err := net.SplitHostPort(info.Addr.String())	if err != nil {		return err	}	p, err := strconv.Atoi(port)	if err != nil {		return err	}	if _, err = n.cli.DeregisterInstance(vo.DeregisterInstanceParam{		Ip:          host,		Port:        uint64(p),		ServiceName: info.ServiceName,		Ephemeral:   true,	}); err != nil {		return err	}	return nil}
复制代码

验证注销服务

因为 Nacos 检查健康检查机制,会屏蔽不健康的实例,所以可能注册的实例存在一段时间后,我们用 curl 的方式会自动过期。在下面的注销测试时,需要提前跑一下注册的逻辑。调试期间,先简单手动验证。


服务发现

创建文件

mkdir resolver
cd resolver
touch resolver.go
复制代码

实现接口

package resolver
import ( "context"
"github.com/cloudwego/kitex/pkg/discovery" "github.com/cloudwego/kitex/pkg/rpcinfo")
type nacosResolver struct {}
func NewNacosResolver() discovery.Resolver { return &nacosResolver{}}
func (n nacosResolver) Target(_ context.Context, target rpcinfo.EndpointInfo) (description string) {}
func (n nacosResolver) Resolve(_ context.Context, desc string) (discovery.Result, error) {}
func (n nacosResolver) Diff(cacheKey string, prev, next discovery.Result) (discovery.Change, bool) { return discovery.DefaultDiff(cacheKey, prev, next)}
func (n nacosResolver) Name() string { return "nacos"}
var _ discovery.Resolver = (*nacosResolver)(nil)
复制代码

完善逻辑

package resolver
import ( "context" "fmt"
"github.com/cloudwego/kitex/pkg/discovery" "github.com/cloudwego/kitex/pkg/rpcinfo" "github.com/nacos-group/nacos-sdk-go/clients/naming_client" "github.com/nacos-group/nacos-sdk-go/vo")
type nacosResolver struct { cli naming_client.INamingClient}
func NewNacosResolver(cli naming_client.INamingClient) discovery.Resolver { return &nacosResolver{cli: cli}}
func (n nacosResolver) Target(_ context.Context, target rpcinfo.EndpointInfo) (description string) { return target.ServiceName()}
func (n nacosResolver) Resolve(_ context.Context, desc string) (discovery.Result, error) { res, err := n.cli.SelectInstances(vo.SelectInstancesParam{ ServiceName: desc, HealthyOnly: true, }) if err != nil { return discovery.Result{}, err } var instances []discovery.Instance for _, in := range res { instances = append(instances, discovery.NewInstance( "tcp", fmt.Sprintf("%s:%d", in.Ip, in.Port), int(in.Weight), in.Metadata), ) } return discovery.Result{ Cacheable: true, CacheKey: desc, Instances: instances, }, nil}
func (n nacosResolver) Diff(cacheKey string, prev, next discovery.Result) (discovery.Change, bool) { return discovery.DefaultDiff(cacheKey, prev, next)}
func (n nacosResolver) Name() string { return "nacos"}
var _ discovery.Resolver = (*nacosResolver)(nil)
复制代码

总结

后期完善好单元测试再开源出来,稍安勿躁,近期一直再按照 CloudWeGo 的规范调试 GitHub Action。调试完就 PR。


发布于: 2021 年 11 月 15 日阅读数: 31
用户头像

baiyutang

关注

广州 2017.12.13 加入

Microservices | Golang | Cloud Nitive | “Smart work,Not hard”

评论

发布
暂无评论
[ CloudWeGo 微服务实践 - 08 ] Nacos 服务发现扩展  (2)