414 lines
11 KiB
Go
414 lines
11 KiB
Go
package registry
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"sync"
|
|
"time"
|
|
|
|
"go_dreamfactory/lego/core"
|
|
"go_dreamfactory/lego/sys/log"
|
|
|
|
hash "github.com/mitchellh/hashstructure"
|
|
"github.com/nacos-group/nacos-sdk-go/clients"
|
|
"github.com/nacos-group/nacos-sdk-go/clients/naming_client"
|
|
"github.com/nacos-group/nacos-sdk-go/common/constant"
|
|
"github.com/nacos-group/nacos-sdk-go/model"
|
|
"github.com/nacos-group/nacos-sdk-go/vo"
|
|
)
|
|
|
|
func newNacos(options Options) (sys *Nacos_Registry, err error) {
|
|
ctx, cancel := context.WithCancel(context.TODO())
|
|
sys = &Nacos_Registry{
|
|
options: options,
|
|
ctx: ctx,
|
|
cancel: cancel,
|
|
shash: make(map[string]uint64),
|
|
services: make(map[string]*ServiceNode),
|
|
subscribe: make(map[string]*vo.SubscribeParam),
|
|
rpcsubs: make(map[core.Rpc_Key][]*ServiceNode),
|
|
}
|
|
// 创建clientConfig
|
|
clientConfig := constant.ClientConfig{
|
|
NamespaceId: options.Nacos_NamespaceId,
|
|
TimeoutMs: options.Nacos_TimeoutMs,
|
|
BeatInterval: options.Nacos_BeatInterval,
|
|
Username: options.Nacos_UserName,
|
|
Password: options.Nacos_Password,
|
|
NotLoadCacheAtStart: true,
|
|
UpdateCacheWhenEmpty: true,
|
|
LogDir: "nacos/log",
|
|
CacheDir: "nacos/cache",
|
|
RotateTime: "1h",
|
|
MaxAge: 3,
|
|
LogLevel: "error",
|
|
}
|
|
// 至少一个ServerConfig
|
|
serverConfigs := []constant.ServerConfig{
|
|
{
|
|
IpAddr: options.Nacos_NacosAddr,
|
|
ContextPath: "/nacos",
|
|
Port: options.Nacos_Port,
|
|
Scheme: "http",
|
|
},
|
|
}
|
|
|
|
// 创建服务发现客户端的另一种方式 (推荐)
|
|
sys.client, err = clients.NewNamingClient(
|
|
vo.NacosClientParam{
|
|
ClientConfig: &clientConfig,
|
|
ServerConfigs: serverConfigs,
|
|
},
|
|
)
|
|
return
|
|
}
|
|
|
|
type Nacos_Registry struct {
|
|
options Options
|
|
client naming_client.INamingClient
|
|
isstart bool
|
|
ctx context.Context
|
|
cancel context.CancelFunc
|
|
slock sync.RWMutex
|
|
shash map[string]uint64
|
|
subscribe map[string]*vo.SubscribeParam
|
|
services map[string]*ServiceNode
|
|
rlock sync.RWMutex
|
|
rpcsubs map[core.Rpc_Key][]*ServiceNode
|
|
}
|
|
|
|
func (this *Nacos_Registry) Start() (err error) {
|
|
if err = this.getServices(); err != nil && err.Error() != "instance list is empty!" {
|
|
return
|
|
}
|
|
if err = this.registerSNode(&ServiceNode{
|
|
Tag: this.options.Service.GetTag(),
|
|
Id: this.options.Service.GetId(),
|
|
IP: this.options.Service.GetIp(),
|
|
Port: this.options.Service.GetPort(),
|
|
Type: this.options.Service.GetType(),
|
|
Category: this.options.Service.GetCategory(),
|
|
Version: this.options.Service.GetVersion(),
|
|
RpcId: this.options.Service.GetRpcId(),
|
|
PreWeight: this.options.Service.GetPreWeight(),
|
|
}); err != nil {
|
|
return
|
|
}
|
|
ctx1, _ := context.WithCancel(this.ctx)
|
|
go this.run(ctx1)
|
|
ctx2, _ := context.WithCancel(this.ctx)
|
|
go this.listener(ctx2)
|
|
this.isstart = true
|
|
return
|
|
}
|
|
|
|
func (this *Nacos_Registry) Stop() (err error) {
|
|
this.cancel()
|
|
this.isstart = false
|
|
return
|
|
}
|
|
|
|
func (this *Nacos_Registry) PushServiceInfo() (err error) {
|
|
if this.isstart {
|
|
err = this.registerSNode(&ServiceNode{
|
|
Tag: this.options.Service.GetTag(),
|
|
Id: this.options.Service.GetId(),
|
|
IP: this.options.Service.GetIp(),
|
|
Port: this.options.Service.GetPort(),
|
|
Type: this.options.Service.GetType(),
|
|
Category: this.options.Service.GetCategory(),
|
|
Version: this.options.Service.GetVersion(),
|
|
RpcId: this.options.Service.GetRpcId(),
|
|
PreWeight: this.options.Service.GetPreWeight(),
|
|
})
|
|
}
|
|
return
|
|
}
|
|
func (this *Nacos_Registry) GetServiceById(sId string) (n *ServiceNode, err error) {
|
|
this.slock.RLock()
|
|
n, ok := this.services[sId]
|
|
this.slock.RUnlock()
|
|
if !ok {
|
|
return nil, fmt.Errorf("No Found %s", sId)
|
|
}
|
|
return
|
|
}
|
|
func (this *Nacos_Registry) GetServiceByType(sType string) (n []*ServiceNode) {
|
|
this.slock.RLock()
|
|
defer this.slock.RUnlock()
|
|
n = make([]*ServiceNode, 0)
|
|
for _, v := range this.services {
|
|
if v.Type == sType {
|
|
n = append(n, v)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
func (this *Nacos_Registry) GetAllServices() (n []*ServiceNode) {
|
|
this.slock.RLock()
|
|
defer this.slock.RUnlock()
|
|
n = make([]*ServiceNode, 0)
|
|
for _, v := range this.services {
|
|
n = append(n, v)
|
|
}
|
|
return
|
|
}
|
|
func (this *Nacos_Registry) GetServiceByCategory(category core.S_Category) (n []*ServiceNode) {
|
|
this.slock.RLock()
|
|
defer this.slock.RUnlock()
|
|
n = make([]*ServiceNode, 0)
|
|
for _, v := range this.services {
|
|
if v.Category == category {
|
|
n = append(n, v)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
func (this *Nacos_Registry) GetRpcSubById(rId core.Rpc_Key) (n []*ServiceNode) {
|
|
n = make([]*ServiceNode, 0)
|
|
this.rlock.RLock()
|
|
d, ok := this.rpcsubs[rId]
|
|
this.rlock.RUnlock()
|
|
if ok {
|
|
n = d
|
|
}
|
|
return
|
|
}
|
|
|
|
//内部函数-------------------------------------------------------------------------------------------------------------------------
|
|
func (this *Nacos_Registry) run(ctx context.Context) {
|
|
t := time.NewTicker(time.Duration(this.options.Consul_RegisterInterval) * time.Second)
|
|
defer t.Stop()
|
|
locp:
|
|
for {
|
|
select {
|
|
case <-t.C:
|
|
err := this.PushServiceInfo()
|
|
if err != nil {
|
|
log.Warnf("service run Server.Register error: %v", err)
|
|
}
|
|
case <-ctx.Done():
|
|
break locp
|
|
}
|
|
}
|
|
log.Infof("Sys Registry is succ exit run")
|
|
}
|
|
|
|
func (this *Nacos_Registry) listener(ctx context.Context) {
|
|
var (
|
|
slist model.ServiceList
|
|
instances []model.Instance
|
|
err error
|
|
)
|
|
t := time.NewTicker(time.Duration(this.options.Nacos_RegisterTTL) * time.Second)
|
|
defer t.Stop()
|
|
locp:
|
|
for {
|
|
select {
|
|
case <-t.C:
|
|
if slist, err = this.client.GetAllServicesInfo(vo.GetAllServiceInfoParam{
|
|
NameSpace: this.options.Nacos_NamespaceId,
|
|
GroupName: this.options.Service.GetTag(),
|
|
PageNo: 1,
|
|
PageSize: 100,
|
|
}); err == nil {
|
|
var service = make(map[string]struct{})
|
|
for _, v := range slist.Doms {
|
|
if instances, err = this.client.SelectInstances(vo.SelectInstancesParam{
|
|
ServiceName: v,
|
|
GroupName: this.options.Service.GetTag(),
|
|
HealthyOnly: true,
|
|
}); err == nil {
|
|
for _, v1 := range instances {
|
|
if v1.Enable && v1.Healthy {
|
|
service[v] = struct{}{}
|
|
this.addandupdataServiceNode(v1)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
temp := make(map[string]*ServiceNode)
|
|
this.rlock.RLock()
|
|
for k, v := range this.services {
|
|
temp[k] = v
|
|
}
|
|
this.rlock.RUnlock()
|
|
for k, v := range temp {
|
|
if _, ok := service[k]; !ok { //不存在了 移除
|
|
this.removeServiceNode(v.Id)
|
|
}
|
|
}
|
|
}
|
|
case <-ctx.Done():
|
|
break locp
|
|
}
|
|
}
|
|
log.Infof("Sys Registry is succ exit listener")
|
|
}
|
|
|
|
func (this *Nacos_Registry) getServices() (err error) {
|
|
var (
|
|
slist model.ServiceList
|
|
instances []model.Instance
|
|
)
|
|
if slist, err = this.client.GetAllServicesInfo(vo.GetAllServiceInfoParam{
|
|
NameSpace: this.options.Nacos_NamespaceId,
|
|
GroupName: this.options.Service.GetTag(),
|
|
PageNo: 1,
|
|
PageSize: 20,
|
|
}); err == nil {
|
|
for _, v := range slist.Doms {
|
|
this.rlock.RLock()
|
|
_, ok := this.services[v]
|
|
this.rlock.RUnlock()
|
|
if !ok {
|
|
if instances, err = this.client.SelectInstances(vo.SelectInstancesParam{
|
|
ServiceName: v,
|
|
GroupName: this.options.Service.GetTag(),
|
|
HealthyOnly: true,
|
|
}); err == nil {
|
|
for _, v1 := range instances {
|
|
if v1.Enable && v1.Healthy {
|
|
this.addandupdataServiceNode(v1)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return err
|
|
}
|
|
func (this *Nacos_Registry) registerSNode(snode *ServiceNode) (err error) {
|
|
h, err := hash.Hash(snode, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if v, ok := this.shash[snode.Id]; ok && v != 0 && v == h { //没变化不用注册
|
|
return
|
|
}
|
|
var (
|
|
// success bool
|
|
)
|
|
this.deregisterSNode()
|
|
_, err = this.client.RegisterInstance(vo.RegisterInstanceParam{
|
|
Ip: snode.IP,
|
|
Port: uint64(snode.Port),
|
|
Weight: snode.PreWeight,
|
|
GroupName: snode.Tag,
|
|
ServiceName: snode.Id,
|
|
Enable: true,
|
|
Healthy: true,
|
|
Ephemeral: true,
|
|
Metadata: map[string]string{
|
|
"id": snode.Id,
|
|
"tag": snode.Tag,
|
|
"type": string(snode.Type),
|
|
"category": string(snode.Category),
|
|
"version": fmt.Sprintf("%v", snode.Version),
|
|
"rpcid": snode.RpcId,
|
|
},
|
|
})
|
|
return
|
|
}
|
|
func (this *Nacos_Registry) deregisterSNode() (err error) {
|
|
_, err = this.client.DeregisterInstance(vo.DeregisterInstanceParam{
|
|
Ip: this.options.Service.GetIp(),
|
|
Port: 8848,
|
|
ServiceName: this.options.Service.GetId(),
|
|
GroupName: this.options.Service.GetTag(),
|
|
})
|
|
return
|
|
}
|
|
func (this *Nacos_Registry) addandupdataServiceNode(as model.Instance) (sn *ServiceNode, err error) {
|
|
var (
|
|
version string
|
|
rpcid string
|
|
snode *ServiceNode
|
|
h uint64
|
|
)
|
|
version = as.Metadata["version"]
|
|
rpcid = as.Metadata["rpcid"]
|
|
snode = &ServiceNode{
|
|
Tag: as.Metadata["tag"],
|
|
Type: as.Metadata["type"],
|
|
Category: core.S_Category(as.Metadata["category"]),
|
|
Id: as.Metadata["id"],
|
|
IP: as.Ip,
|
|
Port: int(as.Port),
|
|
Version: version,
|
|
RpcId: rpcid,
|
|
PreWeight: as.Weight,
|
|
}
|
|
if h, err = hash.Hash(snode, nil); err == nil {
|
|
_, ok := this.services[snode.Id]
|
|
if !ok {
|
|
log.Infof("发现新的服务【%s:%s】", snode.Id, snode.Version)
|
|
sub := &vo.SubscribeParam{
|
|
ServiceName: snode.Id,
|
|
GroupName: snode.Tag,
|
|
SubscribeCallback: this.subscribecallback,
|
|
}
|
|
this.rlock.Lock()
|
|
this.services[snode.Id] = snode
|
|
this.shash[snode.Id] = h
|
|
this.subscribe[snode.Id] = sub
|
|
this.rlock.Unlock()
|
|
if this.options.Listener != nil { //异步通知
|
|
go this.options.Listener.FindServiceHandlefunc(*snode)
|
|
}
|
|
err = this.client.Subscribe(sub)
|
|
} else {
|
|
this.rlock.RLock()
|
|
v, ok := this.shash[snode.Id]
|
|
this.rlock.RUnlock()
|
|
if !ok || v != h { //校验不一致
|
|
// log.Debugf("更新服务【%s】", snode.Id)
|
|
this.rlock.Lock()
|
|
this.services[snode.Id] = snode
|
|
this.shash[snode.Id] = h
|
|
this.rlock.Unlock()
|
|
if this.options.Listener != nil { //异步通知
|
|
go this.options.Listener.UpDataServiceHandlefunc(*snode)
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
log.Errorf("registry 校验服务 hash 值异常:%s", err.Error())
|
|
}
|
|
return
|
|
}
|
|
func (this *Nacos_Registry) removeServiceNode(sId string) {
|
|
this.rlock.RLock()
|
|
_, ok := this.services[sId]
|
|
sub, _ := this.subscribe[sId]
|
|
this.rlock.RUnlock()
|
|
if !ok {
|
|
return
|
|
}
|
|
log.Infof("丢失服务【%s】", sId)
|
|
this.rlock.Lock()
|
|
delete(this.services, sId)
|
|
delete(this.subscribe, sId)
|
|
delete(this.shash, sId)
|
|
this.rlock.Unlock()
|
|
this.client.Unsubscribe(sub)
|
|
if this.options.Listener != nil { //异步通知
|
|
go this.options.Listener.LoseServiceHandlefunc(sId)
|
|
}
|
|
}
|
|
|
|
func (this *Nacos_Registry) subscribecallback(services []model.SubscribeService, err error) {
|
|
for _, v := range services {
|
|
// log.Debugf("subscribecallback:%+v", v)
|
|
this.addandupdataServiceNode(model.Instance{
|
|
Valid: v.Valid,
|
|
Enable: v.Enable,
|
|
ServiceName: v.ServiceName,
|
|
Ip: v.Ip,
|
|
Port: v.Port,
|
|
Metadata: v.Metadata,
|
|
Weight: v.Weight,
|
|
})
|
|
}
|
|
}
|