[ CloudWeGo 微服务实践 - 08 ] Nacos 服务发现扩展 (2)
- 2021 年 11 月 15 日
本文字数:4482 字
阅读完需:约 15 分钟
![[ CloudWeGo 微服务实践 - 08 ] Nacos 服务发现扩展 (2)](https://static001.geekbang.org/infoq/7d/7dcec07bf0d47b397a66c048a85a159c.png)
昨天的《 [ CloudWeGo 微服务实践 - 08 ] Nacos 服务发现扩展 (1)》 中,我们准备好了所有的开发环境。今天就准备封装这个组件。
目标
封装一个以 Nacos 为注册中心的服务发现扩展
环境
MacOS Monterey 12.0.1
go version go1.16.5 darwin/amd64
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: ®istry.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: ®istry.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。
版权声明: 本文为 InfoQ 作者【baiyutang】的原创文章。
原文链接:【http://xie.infoq.cn/article/bd6c6bf8bdf4835fed29f4b56】。文章转载请联系作者。
baiyutang
广州 2017.12.13 加入
Microservices | Golang | Cloud Nitive | “Smart work,Not hard”











评论