mirror of
https://github.com/alibaba/higress.git
synced 2026-06-09 04:37:31 +08:00
add mcp bridge (#107)
This commit is contained in:
171
registry/nacos/address/address_discovery.go
Normal file
171
registry/nacos/address/address_discovery.go
Normal file
@@ -0,0 +1,171 @@
|
||||
package address
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"math/rand"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"go.uber.org/atomic"
|
||||
"istio.io/pkg/log"
|
||||
)
|
||||
|
||||
const (
|
||||
NACOS_PATH = "/nacos/serverlist"
|
||||
MODULE_HEADER_KEY = "Request-Module"
|
||||
MODULE_HEADER_VALUE = "Naming"
|
||||
DEFAULT_INTERVAL = 30 * time.Second
|
||||
)
|
||||
|
||||
type NacosAddressProvider struct {
|
||||
serverAddr string
|
||||
nacosAddr string
|
||||
nacosBackupAddr []string
|
||||
namespace string
|
||||
stop chan struct{}
|
||||
trigger chan struct{}
|
||||
cond *sync.Cond
|
||||
isStop *atomic.Bool
|
||||
mutex *sync.Mutex
|
||||
}
|
||||
|
||||
func NewNacosAddressProvider(serverAddr, namespace string) *NacosAddressProvider {
|
||||
provider := &NacosAddressProvider{
|
||||
serverAddr: serverAddr,
|
||||
namespace: namespace,
|
||||
stop: make(chan struct{}),
|
||||
trigger: make(chan struct{}, 1),
|
||||
cond: sync.NewCond(new(sync.Mutex)),
|
||||
isStop: atomic.NewBool(false),
|
||||
mutex: &sync.Mutex{},
|
||||
}
|
||||
go provider.Run()
|
||||
return provider
|
||||
}
|
||||
|
||||
func (p *NacosAddressProvider) Run() {
|
||||
ticker := time.NewTicker(DEFAULT_INTERVAL)
|
||||
defer ticker.Stop()
|
||||
p.addressDiscovery()
|
||||
for {
|
||||
select {
|
||||
case <-p.trigger:
|
||||
p.addressDiscovery()
|
||||
case <-ticker.C:
|
||||
p.addressDiscovery()
|
||||
case <-p.stop:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *NacosAddressProvider) Update(serverAddr, namespace string) {
|
||||
p.mutex.Lock()
|
||||
p.serverAddr = serverAddr
|
||||
p.namespace = namespace
|
||||
p.mutex.Unlock()
|
||||
p.addressDiscovery()
|
||||
}
|
||||
|
||||
func (p *NacosAddressProvider) Trigger() {
|
||||
p.cond.L.Lock()
|
||||
oldAddr := p.nacosAddr
|
||||
if len(p.nacosBackupAddr) > 0 {
|
||||
p.nacosAddr = p.nacosBackupAddr[rand.Intn(len(p.nacosBackupAddr))]
|
||||
for i := len(p.nacosBackupAddr) - 1; i >= 0; i-- {
|
||||
if p.nacosBackupAddr[i] == p.nacosAddr {
|
||||
p.nacosBackupAddr = append(p.nacosBackupAddr[:i], p.nacosBackupAddr[i+1:]...)
|
||||
}
|
||||
}
|
||||
p.nacosBackupAddr = append(p.nacosBackupAddr, oldAddr)
|
||||
}
|
||||
p.cond.Broadcast()
|
||||
p.cond.L.Unlock()
|
||||
select {
|
||||
case p.trigger <- struct{}{}:
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
func (p *NacosAddressProvider) Stop() {
|
||||
p.isStop.Store(true)
|
||||
p.stop <- struct{}{}
|
||||
}
|
||||
|
||||
func (p *NacosAddressProvider) GetNacosAddress(oldAddress string) <-chan string {
|
||||
addressChan := make(chan string)
|
||||
go func() {
|
||||
var addr string
|
||||
p.cond.L.Lock()
|
||||
log.Debugf("get nacos address, p.nacosAddr, oldAddress", p.nacosAddr, oldAddress)
|
||||
for p.nacosAddr == oldAddress || p.nacosAddr == "" {
|
||||
if p.isStop.Load() {
|
||||
return
|
||||
}
|
||||
p.cond.Wait()
|
||||
}
|
||||
addr = p.nacosAddr
|
||||
p.cond.L.Unlock()
|
||||
addressChan <- addr
|
||||
}()
|
||||
return addressChan
|
||||
}
|
||||
|
||||
func (p *NacosAddressProvider) addressDiscovery() {
|
||||
p.mutex.Lock()
|
||||
url := fmt.Sprintf("%s%s?namespace=%s", p.serverAddr, NACOS_PATH, p.namespace)
|
||||
p.mutex.Unlock()
|
||||
if !strings.HasPrefix(url, "http://") && !strings.HasPrefix(url, "https://") {
|
||||
url = "http://" + url
|
||||
}
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
log.Errorf("create request failed, err:%v, url:%s", err, url)
|
||||
return
|
||||
}
|
||||
req.Header.Add(MODULE_HEADER_KEY, MODULE_HEADER_VALUE)
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
log.Errorf("get nacos address failed, err:%v, url:%s", err, url)
|
||||
return
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != 200 {
|
||||
log.Errorf("get nacos address failed, statusCode:%d", resp.StatusCode)
|
||||
return
|
||||
}
|
||||
body, _ := ioutil.ReadAll(resp.Body)
|
||||
addresses := string(body)
|
||||
addrVec := strings.Fields(addresses)
|
||||
if len(addrVec) == 0 {
|
||||
return
|
||||
}
|
||||
needUpdate := true
|
||||
p.cond.L.Lock()
|
||||
for _, address := range addrVec {
|
||||
ip := net.ParseIP(address)
|
||||
if ip == nil {
|
||||
log.Errorf("ip parse failed, ip:%s", address)
|
||||
return
|
||||
}
|
||||
if p.nacosAddr == address {
|
||||
needUpdate = false
|
||||
}
|
||||
}
|
||||
p.nacosBackupAddr = addrVec
|
||||
if needUpdate {
|
||||
p.nacosAddr = addrVec[rand.Intn(len(addrVec))]
|
||||
p.cond.Broadcast()
|
||||
log.Infof("nacos address updated, address:%s", p.nacosAddr)
|
||||
}
|
||||
for i := len(p.nacosBackupAddr) - 1; i >= 0; i-- {
|
||||
if p.nacosBackupAddr[i] == p.nacosAddr {
|
||||
p.nacosBackupAddr = append(p.nacosBackupAddr[:i], p.nacosBackupAddr[i+1:]...)
|
||||
}
|
||||
}
|
||||
p.cond.L.Unlock()
|
||||
}
|
||||
287
registry/nacos/address/address_discovery_test.go
Normal file
287
registry/nacos/address/address_discovery_test.go
Normal file
@@ -0,0 +1,287 @@
|
||||
package address
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func setUpServer(status int, body []byte) (string, func()) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
rw.WriteHeader(status)
|
||||
rw.Write(body)
|
||||
}))
|
||||
return server.URL, func() {
|
||||
server.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func setUpServerWithBodyPtr(status int, body *[]byte) (string, func()) {
|
||||
server := httptest.NewServer(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
|
||||
rw.WriteHeader(status)
|
||||
rw.Write(*body)
|
||||
}))
|
||||
return server.URL, func() {
|
||||
server.Close()
|
||||
}
|
||||
}
|
||||
func TestGetNacosAddress(t *testing.T) {
|
||||
goodURL, goodTearDown := setUpServer(200, []byte("1.1.1.1\n 2.2.2.2"))
|
||||
defer goodTearDown()
|
||||
badURL, badTearDown := setUpServer(200, []byte("abc\n 2.2.2.2"))
|
||||
defer badTearDown()
|
||||
errURL, errTearDown := setUpServer(503, []byte("1.1.1.1\n 2.2.2.2"))
|
||||
defer errTearDown()
|
||||
tests := []struct {
|
||||
name string
|
||||
serverAddr string
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
"good",
|
||||
goodURL,
|
||||
[]string{"1.1.1.1", "2.2.2.2"},
|
||||
},
|
||||
{
|
||||
"bad",
|
||||
badURL,
|
||||
[]string{},
|
||||
},
|
||||
{
|
||||
"err",
|
||||
errURL,
|
||||
[]string{},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
provider := NewNacosAddressProvider(tt.serverAddr, "")
|
||||
timeout := time.NewTicker(1 * time.Second)
|
||||
var got string
|
||||
if len(tt.want) == 0 {
|
||||
select {
|
||||
case got = <-provider.GetNacosAddress(""):
|
||||
t.Errorf("GetNacosAddress() = %v, want empty", got)
|
||||
case <-timeout.C:
|
||||
return
|
||||
}
|
||||
}
|
||||
select {
|
||||
case got = <-provider.GetNacosAddress(""):
|
||||
case <-timeout.C:
|
||||
t.Error("GetNacosAddress timeout")
|
||||
}
|
||||
for _, value := range tt.want {
|
||||
if got == value {
|
||||
return
|
||||
}
|
||||
}
|
||||
t.Errorf("GetNacosAddress() = %v, want %v", got, tt.want)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTrigger(t *testing.T) {
|
||||
body := []byte("1.1.1.1 ")
|
||||
url, tearDown := setUpServerWithBodyPtr(200, &body)
|
||||
defer tearDown()
|
||||
provider := NewNacosAddressProvider(url, "xxxx")
|
||||
address := <-provider.GetNacosAddress("")
|
||||
if address != "1.1.1.1" {
|
||||
t.Errorf("got %s, want %s", address, "1.1.1.1")
|
||||
}
|
||||
body = []byte(" 2.2.2.2 ")
|
||||
tests := []struct {
|
||||
name string
|
||||
trigger bool
|
||||
want string
|
||||
}{
|
||||
{
|
||||
"no trigger",
|
||||
false,
|
||||
"1.1.1.1",
|
||||
},
|
||||
{
|
||||
"trigger",
|
||||
true,
|
||||
"2.2.2.2",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.trigger {
|
||||
provider.Trigger()
|
||||
}
|
||||
timeout := time.NewTicker(1 * time.Second)
|
||||
select {
|
||||
case <-provider.GetNacosAddress("1.1.1.1"):
|
||||
case <-timeout.C:
|
||||
}
|
||||
if provider.nacosAddr != tt.want {
|
||||
t.Errorf("got %s, want %s", provider.nacosAddr, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestBackup(t *testing.T) {
|
||||
body := []byte("1.1.1.1 ")
|
||||
url, tearDown := setUpServerWithBodyPtr(200, &body)
|
||||
defer tearDown()
|
||||
provider := NewNacosAddressProvider(url, "xxxx")
|
||||
address := <-provider.GetNacosAddress("")
|
||||
if address != "1.1.1.1" {
|
||||
t.Errorf("got %s, want %s", address, "1.1.1.1")
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
oldaddr string
|
||||
newaddr string
|
||||
triggerNum int
|
||||
want string
|
||||
}{
|
||||
{
|
||||
"case1",
|
||||
"1.1.1.1",
|
||||
"1.1.1.1\n2.2.2.2",
|
||||
1,
|
||||
"2.2.2.2",
|
||||
},
|
||||
{
|
||||
"case2",
|
||||
"1.1.1.1",
|
||||
"3.3.3.3 1.1.1.1",
|
||||
1,
|
||||
"3.3.3.3",
|
||||
},
|
||||
{
|
||||
"case3",
|
||||
"1.1.1.1",
|
||||
"3.3.3.3 1.1.1.1",
|
||||
2,
|
||||
"1.1.1.1",
|
||||
},
|
||||
{
|
||||
"case4",
|
||||
"1.1.1.1",
|
||||
"3.3.3.3\n 1.1.1.1",
|
||||
3,
|
||||
"3.3.3.3",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
provider.nacosAddr = tt.oldaddr
|
||||
body = []byte(tt.newaddr)
|
||||
provider.addressDiscovery()
|
||||
for i := 0; i < tt.triggerNum; i++ {
|
||||
provider.Trigger()
|
||||
}
|
||||
timeout := time.NewTicker(1 * time.Second)
|
||||
var newAddr string
|
||||
select {
|
||||
case newAddr = <-provider.GetNacosAddress(""):
|
||||
case <-timeout.C:
|
||||
}
|
||||
if newAddr != tt.want {
|
||||
t.Errorf("got %s, want %s", newAddr, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestKeepIp(t *testing.T) {
|
||||
body := []byte("1.1.1.1")
|
||||
url, tearDown := setUpServerWithBodyPtr(200, &body)
|
||||
defer tearDown()
|
||||
provider := NewNacosAddressProvider(url, "xxxx")
|
||||
address := <-provider.GetNacosAddress("")
|
||||
if address != "1.1.1.1" {
|
||||
t.Errorf("got %s, want %s", address, "1.1.1.1")
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
newAddr []byte
|
||||
want string
|
||||
}{
|
||||
{
|
||||
"add ip",
|
||||
[]byte("1.1.1.1\n 2.2.2.2"),
|
||||
"1.1.1.1",
|
||||
},
|
||||
{
|
||||
"remove ip",
|
||||
[]byte("2.2.2.2"),
|
||||
"2.2.2.2",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
body = tt.newAddr
|
||||
provider.addressDiscovery()
|
||||
timeout := time.NewTicker(1 * time.Second)
|
||||
select {
|
||||
case <-provider.GetNacosAddress("1.1.1.1"):
|
||||
case <-timeout.C:
|
||||
}
|
||||
if provider.nacosAddr != tt.want {
|
||||
t.Errorf("got %s, want %s", provider.nacosAddr, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultiClient(t *testing.T) {
|
||||
body := []byte("1.1.1.1")
|
||||
url, tearDown := setUpServerWithBodyPtr(200, &body)
|
||||
defer tearDown()
|
||||
provider := NewNacosAddressProvider(url, "xxxx")
|
||||
address := <-provider.GetNacosAddress("")
|
||||
if address != "1.1.1.1" {
|
||||
t.Errorf("got %s, want %s", address, "1.1.1.1")
|
||||
}
|
||||
body = []byte("2.2.2.2")
|
||||
tests := []struct {
|
||||
name string
|
||||
oldAddrs []string
|
||||
want []string
|
||||
}{
|
||||
{
|
||||
"case1",
|
||||
[]string{"1.1.1.1", "1.1.1.1"},
|
||||
[]string{"2.2.2.2", "2.2.2.2"},
|
||||
},
|
||||
{
|
||||
"case2",
|
||||
[]string{"2.2.2.2", "1.1.1.1"},
|
||||
[]string{"", "2.2.2.2"},
|
||||
},
|
||||
{
|
||||
"case3",
|
||||
[]string{"1.1.1.1", "2.2.2.2"},
|
||||
[]string{"2.2.2.2", ""},
|
||||
},
|
||||
{
|
||||
"case4",
|
||||
[]string{"2.2.2.2", "2.2.2.2"},
|
||||
[]string{"", ""},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
provider.addressDiscovery()
|
||||
for i := 0; i < len(tt.oldAddrs); i++ {
|
||||
timeout := time.NewTicker(1 * time.Second)
|
||||
var newaddr string
|
||||
select {
|
||||
case newaddr = <-provider.GetNacosAddress(tt.oldAddrs[i]):
|
||||
case <-timeout.C:
|
||||
}
|
||||
if newaddr != tt.want[i] {
|
||||
t.Errorf("got %s, want %s", newaddr, tt.want[i])
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
502
registry/nacos/v2/watcher.go
Normal file
502
registry/nacos/v2/watcher.go
Normal file
@@ -0,0 +1,502 @@
|
||||
package v2
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/clients"
|
||||
"github.com/nacos-group/nacos-sdk-go/v2/clients/naming_client"
|
||||
"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/vo"
|
||||
"go.uber.org/atomic"
|
||||
"istio.io/api/networking/v1alpha3"
|
||||
"istio.io/pkg/log"
|
||||
|
||||
apiv1 "github.com/alibaba/higress/api/networking/v1"
|
||||
"github.com/alibaba/higress/pkg/common"
|
||||
provider "github.com/alibaba/higress/registry"
|
||||
"github.com/alibaba/higress/registry/memory"
|
||||
"github.com/alibaba/higress/registry/nacos/address"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultInitTimeout = time.Second * 10
|
||||
DefaultNacosTimeout = 5000
|
||||
DefaultNacosLogLevel = "warn"
|
||||
DefaultNacosLogDir = "log/nacos/log/"
|
||||
DefaultNacosCacheDir = "log/nacos/cache/"
|
||||
DefaultNacosNotLoadCache = true
|
||||
DefaultNacosLogRotateTime = "24h"
|
||||
DefaultNacosLogMaxAge = 3
|
||||
DefaultUpdateCacheWhenEmpty = true
|
||||
DefaultRefreshInterval = time.Second * 30
|
||||
DefaultRefreshIntervalLimit = time.Second * 10
|
||||
DefaultFetchPageSize = 50
|
||||
DefaultJoiner = "@@"
|
||||
)
|
||||
|
||||
type watcher struct {
|
||||
provider.BaseWatcher
|
||||
apiv1.RegistryConfig
|
||||
WatchingServices map[string]bool `json:"watching_services"`
|
||||
RegistryType provider.ServiceRegistryType `json:"registry_type"`
|
||||
Status provider.WatcherStatus `json:"status"`
|
||||
namingClient naming_client.INamingClient
|
||||
updateHandler provider.ServiceUpdateHandler
|
||||
readyHandler provider.ReadyHandler
|
||||
cache memory.Cache
|
||||
mutex *sync.Mutex
|
||||
stop chan struct{}
|
||||
isStop bool
|
||||
addrProvider *address.NacosAddressProvider
|
||||
updateCacheWhenEmpty bool
|
||||
nacosClietConfig *constant.ClientConfig
|
||||
}
|
||||
|
||||
type WatcherOption func(w *watcher)
|
||||
|
||||
func NewWatcher(cache memory.Cache, opts ...WatcherOption) (provider.Watcher, error) {
|
||||
w := &watcher{
|
||||
WatchingServices: make(map[string]bool),
|
||||
RegistryType: provider.Nacos2,
|
||||
Status: provider.UnHealthy,
|
||||
cache: cache,
|
||||
mutex: &sync.Mutex{},
|
||||
stop: make(chan struct{}),
|
||||
}
|
||||
|
||||
w.NacosRefreshInterval = int64(DefaultRefreshInterval)
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(w)
|
||||
}
|
||||
|
||||
log.Infof("new nacos2 watcher with config Name:%s", w.Name)
|
||||
|
||||
w.nacosClietConfig = constant.NewClientConfig(
|
||||
constant.WithTimeoutMs(DefaultNacosTimeout),
|
||||
constant.WithLogLevel(DefaultNacosLogLevel),
|
||||
constant.WithLogDir(DefaultNacosLogDir),
|
||||
constant.WithCacheDir(DefaultNacosCacheDir),
|
||||
constant.WithNotLoadCacheAtStart(DefaultNacosNotLoadCache),
|
||||
constant.WithLogRollingConfig(&constant.ClientLogRollingConfig{
|
||||
MaxAge: DefaultNacosLogMaxAge,
|
||||
}),
|
||||
constant.WithUpdateCacheWhenEmpty(w.updateCacheWhenEmpty),
|
||||
constant.WithNamespaceId(w.NacosNamespaceId),
|
||||
constant.WithAccessKey(w.NacosAccessKey),
|
||||
constant.WithSecretKey(w.NacosSecretKey),
|
||||
)
|
||||
|
||||
initTimer := time.NewTimer(DefaultInitTimeout)
|
||||
if w.NacosAddressServer != "" {
|
||||
w.addrProvider = address.NewNacosAddressProvider(w.NacosAddressServer, w.NacosNamespace)
|
||||
w.Domain = ""
|
||||
select {
|
||||
case w.Domain = <-w.addrProvider.GetNacosAddress(w.Domain):
|
||||
case <-initTimer.C:
|
||||
return nil, errors.New("new nacos2 watcher timeout")
|
||||
}
|
||||
go w.updateNacosClient()
|
||||
}
|
||||
sc := []constant.ServerConfig{
|
||||
*constant.NewServerConfig(w.Domain, uint64(w.Port)),
|
||||
}
|
||||
|
||||
success := make(chan struct{})
|
||||
go func() {
|
||||
namingClient, err := clients.NewNamingClient(vo.NacosClientParam{
|
||||
ClientConfig: w.nacosClietConfig,
|
||||
ServerConfigs: sc,
|
||||
})
|
||||
if err == nil {
|
||||
w.namingClient = namingClient
|
||||
close(success)
|
||||
} else {
|
||||
log.Errorf("can not create naming client, err:%v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
select {
|
||||
case <-initTimer.C:
|
||||
return nil, errors.New("new nacos2 watcher timeout")
|
||||
case <-success:
|
||||
return w, nil
|
||||
}
|
||||
}
|
||||
|
||||
func WithNacosAddressServer(nacosAddressServer string) WatcherOption {
|
||||
return func(w *watcher) {
|
||||
w.NacosAddressServer = nacosAddressServer
|
||||
}
|
||||
}
|
||||
|
||||
func WithNacosAccessKey(nacosAccessKey string) WatcherOption {
|
||||
return func(w *watcher) {
|
||||
w.NacosAccessKey = nacosAccessKey
|
||||
}
|
||||
}
|
||||
|
||||
func WithNacosSecretKey(nacosSecretKey string) WatcherOption {
|
||||
return func(w *watcher) {
|
||||
w.NacosSecretKey = nacosSecretKey
|
||||
}
|
||||
}
|
||||
|
||||
func WithNacosNamespaceId(nacosNamespaceId string) WatcherOption {
|
||||
return func(w *watcher) {
|
||||
w.NacosNamespaceId = nacosNamespaceId
|
||||
}
|
||||
}
|
||||
|
||||
func WithNacosNamespace(nacosNamespace string) WatcherOption {
|
||||
return func(w *watcher) {
|
||||
w.NacosNamespace = nacosNamespace
|
||||
}
|
||||
}
|
||||
|
||||
func WithNacosGroups(nacosGroups []string) WatcherOption {
|
||||
return func(w *watcher) {
|
||||
w.NacosGroups = nacosGroups
|
||||
}
|
||||
}
|
||||
|
||||
func WithNacosRefreshInterval(refreshInterval int64) WatcherOption {
|
||||
return func(w *watcher) {
|
||||
if refreshInterval < int64(DefaultRefreshIntervalLimit) {
|
||||
refreshInterval = int64(DefaultRefreshIntervalLimit)
|
||||
}
|
||||
w.NacosRefreshInterval = refreshInterval
|
||||
}
|
||||
}
|
||||
|
||||
func WithType(t string) WatcherOption {
|
||||
return func(w *watcher) {
|
||||
w.Type = t
|
||||
}
|
||||
}
|
||||
|
||||
func WithName(name string) WatcherOption {
|
||||
return func(w *watcher) {
|
||||
w.Name = name
|
||||
}
|
||||
}
|
||||
|
||||
func WithDomain(domain string) WatcherOption {
|
||||
return func(w *watcher) {
|
||||
w.Domain = domain
|
||||
}
|
||||
}
|
||||
|
||||
func WithPort(port uint32) WatcherOption {
|
||||
return func(w *watcher) {
|
||||
w.Port = port
|
||||
}
|
||||
}
|
||||
|
||||
func WithUpdateCacheWhenEmpty(enable bool) WatcherOption {
|
||||
return func(w *watcher) {
|
||||
w.updateCacheWhenEmpty = enable
|
||||
}
|
||||
}
|
||||
|
||||
func (w *watcher) Run() {
|
||||
ticker := time.NewTicker(time.Duration(w.NacosRefreshInterval))
|
||||
defer ticker.Stop()
|
||||
w.Status = provider.ProbeWatcherStatus(w.Domain, strconv.FormatUint(uint64(w.Port), 10))
|
||||
err := w.fetchAllServices()
|
||||
if err != nil {
|
||||
log.Errorf("first fetch services failed, err:%v", err)
|
||||
} else {
|
||||
w.readyHandler(true)
|
||||
}
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
err := w.fetchAllServices()
|
||||
if err != nil {
|
||||
log.Errorf("fetch services failed, err:%v", err)
|
||||
} else {
|
||||
w.readyHandler(true)
|
||||
}
|
||||
case <-w.stop:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (w *watcher) updateNacosClient() {
|
||||
for {
|
||||
select {
|
||||
case addr := <-w.addrProvider.GetNacosAddress(w.Domain):
|
||||
func() {
|
||||
w.mutex.Lock()
|
||||
defer w.mutex.Unlock()
|
||||
w.Domain = addr
|
||||
namingClient, err := clients.NewNamingClient(vo.NacosClientParam{
|
||||
ClientConfig: w.nacosClietConfig,
|
||||
ServerConfigs: []constant.ServerConfig{
|
||||
*constant.NewServerConfig(addr, uint64(w.Port)),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorf("can not update naming client, err:%v", err)
|
||||
return
|
||||
}
|
||||
w.namingClient = namingClient
|
||||
log.Info("naming client updated")
|
||||
}()
|
||||
case <-w.stop:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (w *watcher) fetchAllServices() error {
|
||||
w.mutex.Lock()
|
||||
defer w.mutex.Unlock()
|
||||
|
||||
if w.isStop {
|
||||
return nil
|
||||
}
|
||||
fetchedServices := make(map[string]bool)
|
||||
var tries int
|
||||
for _, groupName := range w.NacosGroups {
|
||||
for page := 1; ; page++ {
|
||||
ss, err := w.namingClient.GetAllServicesInfo(vo.GetAllServiceInfoParam{
|
||||
GroupName: groupName,
|
||||
PageNo: uint32(page),
|
||||
PageSize: DefaultFetchPageSize,
|
||||
NameSpace: w.NacosNamespace,
|
||||
})
|
||||
if err != nil {
|
||||
if tries > 10 {
|
||||
return err
|
||||
}
|
||||
if w.addrProvider != nil {
|
||||
w.addrProvider.Trigger()
|
||||
}
|
||||
log.Errorf("fetch nacos service list failed, err:%v, pageNo:%d", err, page)
|
||||
page--
|
||||
tries++
|
||||
continue
|
||||
}
|
||||
for _, serviceName := range ss.Doms {
|
||||
fetchedServices[groupName+DefaultJoiner+serviceName] = true
|
||||
}
|
||||
if ss.Count < DefaultFetchPageSize {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for key := range w.WatchingServices {
|
||||
if _, exist := fetchedServices[key]; !exist {
|
||||
s := strings.Split(key, DefaultJoiner)
|
||||
err := w.unsubscribe(s[0], s[1])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
delete(w.WatchingServices, key)
|
||||
}
|
||||
}
|
||||
wg := sync.WaitGroup{}
|
||||
subscribeFailed := atomic.NewBool(false)
|
||||
watchingKeys := make(chan string, len(fetchedServices))
|
||||
for key := range fetchedServices {
|
||||
if _, exist := w.WatchingServices[key]; !exist {
|
||||
s := strings.Split(key, DefaultJoiner)
|
||||
if !shouldSubscribe(s[1]) {
|
||||
continue
|
||||
}
|
||||
wg.Add(1)
|
||||
go func(k string) {
|
||||
err := w.subscribe(s[0], s[1])
|
||||
if err != nil {
|
||||
subscribeFailed.Store(true)
|
||||
log.Errorf("subscribe failed, err:%v, group:%s, service:%s", err, s[0], s[1])
|
||||
} else {
|
||||
watchingKeys <- k
|
||||
}
|
||||
wg.Done()
|
||||
}(key)
|
||||
}
|
||||
}
|
||||
wg.Wait()
|
||||
close(watchingKeys)
|
||||
for key := range watchingKeys {
|
||||
w.WatchingServices[key] = true
|
||||
}
|
||||
if subscribeFailed.Load() {
|
||||
return errors.New("subscribe services failed")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *watcher) subscribe(groupName string, serviceName string) error {
|
||||
log.Debugf("subscribe service, groupName:%s, serviceName:%s", groupName, serviceName)
|
||||
|
||||
err := w.namingClient.Subscribe(&vo.SubscribeParam{
|
||||
ServiceName: serviceName,
|
||||
GroupName: groupName,
|
||||
SubscribeCallback: w.getSubscribeCallback(groupName, serviceName),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
log.Errorf("subscribe service error:%v, groupName:%s, serviceName:%s", err, groupName, serviceName)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *watcher) unsubscribe(groupName string, serviceName string) error {
|
||||
log.Debugf("unsubscribe service, groupName:%s, serviceName:%s", groupName, serviceName)
|
||||
|
||||
err := w.namingClient.Unsubscribe(&vo.SubscribeParam{
|
||||
ServiceName: serviceName,
|
||||
GroupName: groupName,
|
||||
SubscribeCallback: w.getSubscribeCallback(groupName, serviceName),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
log.Errorf("unsubscribe service error:%v, groupName:%s, serviceName:%s", err, groupName, serviceName)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *watcher) getSubscribeCallback(groupName string, serviceName string) func(services []model.Instance, err error) {
|
||||
suffix := strings.Join([]string{groupName, w.NacosNamespace, "nacos"}, common.DotSeparator)
|
||||
suffix = strings.ReplaceAll(suffix, common.Underscore, common.Hyphen)
|
||||
host := strings.Join([]string{serviceName, suffix}, common.DotSeparator)
|
||||
|
||||
return func(services []model.Instance, err error) {
|
||||
defer w.updateHandler()
|
||||
|
||||
//log.Info("callback", "serviceName", serviceName, "suffix", suffix, "details", services)
|
||||
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "hosts is empty") {
|
||||
if w.updateCacheWhenEmpty {
|
||||
w.cache.DeleteServiceEntryWrapper(host)
|
||||
}
|
||||
} else {
|
||||
log.Errorf("callback error:%v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if len(services) > 0 && services[0].Metadata != nil && services[0].Metadata["register-resource"] == "mcp-bridge" {
|
||||
return
|
||||
}
|
||||
serviceEntry := w.generateServiceEntry(host, services)
|
||||
w.cache.UpdateServiceEntryWrapper(host, &memory.ServiceEntryWrapper{
|
||||
ServiceName: serviceName,
|
||||
ServiceEntry: serviceEntry,
|
||||
Suffix: suffix,
|
||||
RegistryType: w.Type,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (w *watcher) generateServiceEntry(host string, services []model.Instance) *v1alpha3.ServiceEntry {
|
||||
portList := make([]*v1alpha3.Port, 0)
|
||||
endpoints := make([]*v1alpha3.WorkloadEntry, 0)
|
||||
|
||||
for _, service := range services {
|
||||
protocol := common.HTTP
|
||||
if service.Metadata != nil && service.Metadata["protocol"] != "" {
|
||||
protocol = common.ParseProtocol(service.Metadata["protocol"])
|
||||
}
|
||||
port := &v1alpha3.Port{
|
||||
Name: protocol.String(),
|
||||
Number: uint32(service.Port),
|
||||
Protocol: protocol.String(),
|
||||
}
|
||||
if len(portList) == 0 {
|
||||
portList = append(portList, port)
|
||||
}
|
||||
endpoint := &v1alpha3.WorkloadEntry{
|
||||
Address: service.Ip,
|
||||
Ports: map[string]uint32{port.Protocol: port.Number},
|
||||
Labels: service.Metadata,
|
||||
}
|
||||
endpoints = append(endpoints, endpoint)
|
||||
}
|
||||
|
||||
se := &v1alpha3.ServiceEntry{
|
||||
Hosts: []string{host},
|
||||
Ports: portList,
|
||||
Location: v1alpha3.ServiceEntry_MESH_INTERNAL,
|
||||
Resolution: v1alpha3.ServiceEntry_STATIC,
|
||||
Endpoints: endpoints,
|
||||
}
|
||||
|
||||
return se
|
||||
}
|
||||
|
||||
func (w *watcher) Stop() {
|
||||
w.mutex.Lock()
|
||||
defer w.mutex.Unlock()
|
||||
if w.addrProvider != nil {
|
||||
w.addrProvider.Stop()
|
||||
}
|
||||
for key := range w.WatchingServices {
|
||||
s := strings.Split(key, DefaultJoiner)
|
||||
err := w.unsubscribe(s[0], s[1])
|
||||
if err == nil {
|
||||
delete(w.WatchingServices, key)
|
||||
}
|
||||
|
||||
// clean the cache
|
||||
suffix := strings.Join([]string{s[0], w.NacosNamespace, w.Type}, common.DotSeparator)
|
||||
suffix = strings.ReplaceAll(suffix, common.Underscore, common.Hyphen)
|
||||
host := strings.Join([]string{s[1], suffix}, common.DotSeparator)
|
||||
w.cache.DeleteServiceEntryWrapper(host)
|
||||
}
|
||||
|
||||
w.isStop = true
|
||||
w.stop <- struct{}{}
|
||||
w.readyHandler(false)
|
||||
}
|
||||
|
||||
func (w *watcher) IsHealthy() bool {
|
||||
return w.Status == provider.Healthy
|
||||
}
|
||||
|
||||
func (w *watcher) GetRegistryType() string {
|
||||
return w.RegistryType.String()
|
||||
}
|
||||
|
||||
func (w *watcher) AppendServiceUpdateHandler(f func()) {
|
||||
w.updateHandler = f
|
||||
}
|
||||
|
||||
func (w *watcher) ReadyHandler(f func(bool)) {
|
||||
w.readyHandler = f
|
||||
}
|
||||
|
||||
func shouldSubscribe(serviceName string) bool {
|
||||
prefixFilters := []string{"consumers:"}
|
||||
fullFilters := []string{""}
|
||||
|
||||
for _, f := range prefixFilters {
|
||||
if strings.HasPrefix(serviceName, f) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
for _, f := range fullFilters {
|
||||
if serviceName == f {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
406
registry/nacos/watcher.go
Normal file
406
registry/nacos/watcher.go
Normal file
@@ -0,0 +1,406 @@
|
||||
package nacos
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"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"
|
||||
"istio.io/api/networking/v1alpha3"
|
||||
versionedclient "istio.io/client-go/pkg/clientset/versioned"
|
||||
"istio.io/pkg/log"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
|
||||
apiv1 "github.com/alibaba/higress/api/networking/v1"
|
||||
"github.com/alibaba/higress/pkg/common"
|
||||
provider "github.com/alibaba/higress/registry"
|
||||
"github.com/alibaba/higress/registry/memory"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultNacosTimeout = 5000
|
||||
DefaultNacosLogLevel = "warn"
|
||||
DefaultNacosLogDir = "log/nacos/log/"
|
||||
DefaultNacosCacheDir = "log/nacos/cache/"
|
||||
DefaultNacosNotLoadCache = true
|
||||
DefaultNacosLogRotateTime = "24h"
|
||||
DefaultNacosLogMaxAge = 3
|
||||
DefaultUpdateCacheWhenEmpty = true
|
||||
DefaultRefreshInterval = time.Second * 30
|
||||
DefaultRefreshIntervalLimit = time.Second * 10
|
||||
DefaultFetchPageSize = 50
|
||||
DefaultJoiner = "@@"
|
||||
)
|
||||
|
||||
type watcher struct {
|
||||
provider.BaseWatcher
|
||||
apiv1.RegistryConfig
|
||||
WatchingServices map[string]bool `json:"watching_services"`
|
||||
RegistryType provider.ServiceRegistryType `json:"registry_type"`
|
||||
Status provider.WatcherStatus `json:"status"`
|
||||
namingClient naming_client.INamingClient
|
||||
updateHandler provider.ServiceUpdateHandler
|
||||
readyHandler provider.ReadyHandler
|
||||
cache memory.Cache
|
||||
mutex *sync.Mutex
|
||||
stop chan struct{}
|
||||
client *versionedclient.Clientset
|
||||
isStop bool
|
||||
updateCacheWhenEmpty bool
|
||||
}
|
||||
|
||||
type WatcherOption func(w *watcher)
|
||||
|
||||
func NewWatcher(cache memory.Cache, opts ...WatcherOption) (provider.Watcher, error) {
|
||||
w := &watcher{
|
||||
WatchingServices: make(map[string]bool),
|
||||
RegistryType: provider.Nacos,
|
||||
Status: provider.UnHealthy,
|
||||
cache: cache,
|
||||
mutex: &sync.Mutex{},
|
||||
stop: make(chan struct{}),
|
||||
}
|
||||
|
||||
config, err := ctrl.GetConfig()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ic, err := versionedclient.NewForConfig(config)
|
||||
if err != nil {
|
||||
log.Errorf("can not new istio client, err:%v", err)
|
||||
return nil, err
|
||||
}
|
||||
w.client = ic
|
||||
|
||||
w.NacosRefreshInterval = int64(DefaultRefreshInterval)
|
||||
|
||||
for _, opt := range opts {
|
||||
opt(w)
|
||||
}
|
||||
|
||||
log.Infof("new nacos watcher with config Name:%s", w.Name)
|
||||
|
||||
cc := constant.NewClientConfig(
|
||||
constant.WithTimeoutMs(DefaultNacosTimeout),
|
||||
constant.WithLogLevel(DefaultNacosLogLevel),
|
||||
constant.WithLogDir(DefaultNacosLogDir),
|
||||
constant.WithCacheDir(DefaultNacosCacheDir),
|
||||
constant.WithNotLoadCacheAtStart(DefaultNacosNotLoadCache),
|
||||
constant.WithRotateTime(DefaultNacosLogRotateTime),
|
||||
constant.WithMaxAge(DefaultNacosLogMaxAge),
|
||||
constant.WithUpdateCacheWhenEmpty(w.updateCacheWhenEmpty),
|
||||
constant.WithNamespaceId(w.NacosNamespaceId),
|
||||
)
|
||||
|
||||
sc := []constant.ServerConfig{
|
||||
*constant.NewServerConfig(w.Domain, uint64(w.Port)),
|
||||
}
|
||||
|
||||
namingClient, err := clients.NewNamingClient(vo.NacosClientParam{
|
||||
ClientConfig: cc,
|
||||
ServerConfigs: sc,
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorf("can not create naming client, err:%v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
w.namingClient = namingClient
|
||||
|
||||
return w, nil
|
||||
}
|
||||
|
||||
func WithNacosNamespaceId(nacosNamespaceId string) WatcherOption {
|
||||
return func(w *watcher) {
|
||||
w.NacosNamespaceId = nacosNamespaceId
|
||||
}
|
||||
}
|
||||
|
||||
func WithNacosNamespace(nacosNamespace string) WatcherOption {
|
||||
return func(w *watcher) {
|
||||
w.NacosNamespace = nacosNamespace
|
||||
}
|
||||
}
|
||||
|
||||
func WithNacosGroups(nacosGroups []string) WatcherOption {
|
||||
return func(w *watcher) {
|
||||
w.NacosGroups = nacosGroups
|
||||
}
|
||||
}
|
||||
|
||||
func WithNacosRefreshInterval(refreshInterval int64) WatcherOption {
|
||||
return func(w *watcher) {
|
||||
if refreshInterval < int64(DefaultRefreshIntervalLimit) {
|
||||
refreshInterval = int64(DefaultRefreshIntervalLimit)
|
||||
}
|
||||
w.NacosRefreshInterval = refreshInterval
|
||||
}
|
||||
}
|
||||
|
||||
func WithType(t string) WatcherOption {
|
||||
return func(w *watcher) {
|
||||
w.Type = t
|
||||
}
|
||||
}
|
||||
|
||||
func WithName(name string) WatcherOption {
|
||||
return func(w *watcher) {
|
||||
w.Name = name
|
||||
}
|
||||
}
|
||||
|
||||
func WithDomain(domain string) WatcherOption {
|
||||
return func(w *watcher) {
|
||||
w.Domain = domain
|
||||
}
|
||||
}
|
||||
|
||||
func WithPort(port uint32) WatcherOption {
|
||||
return func(w *watcher) {
|
||||
w.Port = port
|
||||
}
|
||||
}
|
||||
|
||||
func WithUpdateCacheWhenEmpty(enable bool) WatcherOption {
|
||||
return func(w *watcher) {
|
||||
w.updateCacheWhenEmpty = enable
|
||||
}
|
||||
}
|
||||
|
||||
func (w *watcher) Run() {
|
||||
ticker := time.NewTicker(time.Duration(w.NacosRefreshInterval))
|
||||
defer ticker.Stop()
|
||||
w.Status = provider.ProbeWatcherStatus(w.Domain, strconv.FormatUint(uint64(w.Port), 10))
|
||||
w.fetchAllServices()
|
||||
w.readyHandler(true)
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
w.fetchAllServices()
|
||||
case <-w.stop:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (w *watcher) fetchAllServices() error {
|
||||
w.mutex.Lock()
|
||||
defer w.mutex.Unlock()
|
||||
if w.isStop {
|
||||
return nil
|
||||
}
|
||||
fetchedServices := make(map[string]bool)
|
||||
|
||||
for _, groupName := range w.NacosGroups {
|
||||
for page := 1; ; page++ {
|
||||
ss, err := w.namingClient.GetAllServicesInfo(vo.GetAllServiceInfoParam{
|
||||
GroupName: groupName,
|
||||
PageNo: uint32(page),
|
||||
PageSize: DefaultFetchPageSize,
|
||||
NameSpace: w.NacosNamespace,
|
||||
})
|
||||
if err != nil {
|
||||
log.Errorf("fetch all services error:%v", err)
|
||||
break
|
||||
}
|
||||
for _, serviceName := range ss.Doms {
|
||||
fetchedServices[groupName+DefaultJoiner+serviceName] = true
|
||||
}
|
||||
if ss.Count < DefaultFetchPageSize {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for key := range w.WatchingServices {
|
||||
if _, exist := fetchedServices[key]; !exist {
|
||||
s := strings.Split(key, DefaultJoiner)
|
||||
err := w.unsubscribe(s[0], s[1])
|
||||
if err == nil {
|
||||
delete(w.WatchingServices, key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for key := range fetchedServices {
|
||||
if _, exist := w.WatchingServices[key]; !exist {
|
||||
s := strings.Split(key, DefaultJoiner)
|
||||
if !shouldSubscribe(s[1]) {
|
||||
continue
|
||||
}
|
||||
err := w.subscribe(s[0], s[1])
|
||||
if err == nil {
|
||||
w.WatchingServices[key] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *watcher) subscribe(groupName string, serviceName string) error {
|
||||
log.Debugf("subscribe service, groupName:%s, serviceName:%s", groupName, serviceName)
|
||||
|
||||
err := w.namingClient.Subscribe(&vo.SubscribeParam{
|
||||
ServiceName: serviceName,
|
||||
GroupName: groupName,
|
||||
SubscribeCallback: w.getSubscribeCallback(groupName, serviceName),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
log.Errorf("subscribe service error:%v, groupName:%s, serviceName:%s", err, groupName, serviceName)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *watcher) unsubscribe(groupName string, serviceName string) error {
|
||||
log.Debugf("unsubscribe service, groupName:%s, serviceName:%s", groupName, serviceName)
|
||||
|
||||
err := w.namingClient.Unsubscribe(&vo.SubscribeParam{
|
||||
ServiceName: serviceName,
|
||||
GroupName: groupName,
|
||||
SubscribeCallback: w.getSubscribeCallback(groupName, serviceName),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
log.Errorf("unsubscribe service error:%v, groupName:%s, serviceName:%s", err, groupName, serviceName)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *watcher) getSubscribeCallback(groupName string, serviceName string) func(services []model.SubscribeService, err error) {
|
||||
suffix := strings.Join([]string{groupName, w.NacosNamespace, w.Type}, common.DotSeparator)
|
||||
suffix = strings.ReplaceAll(suffix, common.Underscore, common.Hyphen)
|
||||
host := strings.Join([]string{serviceName, suffix}, common.DotSeparator)
|
||||
|
||||
return func(services []model.SubscribeService, err error) {
|
||||
defer w.updateHandler()
|
||||
|
||||
//log.Info("callback", "serviceName", serviceName, "suffix", suffix, "details", services)
|
||||
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), "hosts is empty") {
|
||||
if w.updateCacheWhenEmpty {
|
||||
w.cache.DeleteServiceEntryWrapper(host)
|
||||
}
|
||||
} else {
|
||||
log.Errorf("callback error:%v", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
if len(services) > 0 && services[0].Metadata != nil && services[0].Metadata["register-resource"] == "mcp-bridge" {
|
||||
return
|
||||
}
|
||||
serviceEntry := w.generateServiceEntry(host, services)
|
||||
w.cache.UpdateServiceEntryWrapper(host, &memory.ServiceEntryWrapper{
|
||||
ServiceName: serviceName,
|
||||
ServiceEntry: serviceEntry,
|
||||
Suffix: suffix,
|
||||
RegistryType: w.Type,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (w *watcher) generateServiceEntry(host string, services []model.SubscribeService) *v1alpha3.ServiceEntry {
|
||||
portList := make([]*v1alpha3.Port, 0)
|
||||
endpoints := make([]*v1alpha3.WorkloadEntry, 0)
|
||||
|
||||
for _, service := range services {
|
||||
protocol := common.HTTP
|
||||
if service.Metadata != nil && service.Metadata["protocol"] != "" {
|
||||
protocol = common.ParseProtocol(service.Metadata["protocol"])
|
||||
} else {
|
||||
service.Metadata = make(map[string]string)
|
||||
}
|
||||
port := &v1alpha3.Port{
|
||||
Name: protocol.String(),
|
||||
Number: uint32(service.Port),
|
||||
Protocol: protocol.String(),
|
||||
}
|
||||
if len(portList) == 0 {
|
||||
portList = append(portList, port)
|
||||
}
|
||||
endpoint := v1alpha3.WorkloadEntry{
|
||||
Address: service.Ip,
|
||||
Ports: map[string]uint32{port.Protocol: port.Number},
|
||||
Labels: service.Metadata,
|
||||
}
|
||||
endpoints = append(endpoints, &endpoint)
|
||||
}
|
||||
|
||||
se := &v1alpha3.ServiceEntry{
|
||||
Hosts: []string{host},
|
||||
Ports: portList,
|
||||
Location: v1alpha3.ServiceEntry_MESH_INTERNAL,
|
||||
Resolution: v1alpha3.ServiceEntry_STATIC,
|
||||
Endpoints: endpoints,
|
||||
}
|
||||
|
||||
return se
|
||||
}
|
||||
|
||||
func (w *watcher) Stop() {
|
||||
w.mutex.Lock()
|
||||
defer w.mutex.Unlock()
|
||||
|
||||
for key := range w.WatchingServices {
|
||||
s := strings.Split(key, DefaultJoiner)
|
||||
err := w.unsubscribe(s[0], s[1])
|
||||
if err == nil {
|
||||
delete(w.WatchingServices, key)
|
||||
}
|
||||
|
||||
// clean the cache
|
||||
suffix := strings.Join([]string{s[0], w.NacosNamespace, w.Type}, common.DotSeparator)
|
||||
suffix = strings.ReplaceAll(suffix, common.Underscore, common.Hyphen)
|
||||
host := strings.Join([]string{s[1], suffix}, common.DotSeparator)
|
||||
w.cache.DeleteServiceEntryWrapper(host)
|
||||
}
|
||||
w.isStop = true
|
||||
w.stop <- struct{}{}
|
||||
w.readyHandler(false)
|
||||
}
|
||||
|
||||
func (w *watcher) IsHealthy() bool {
|
||||
return w.Status == provider.Healthy
|
||||
}
|
||||
|
||||
func (w *watcher) GetRegistryType() string {
|
||||
return w.RegistryType.String()
|
||||
}
|
||||
|
||||
func (w *watcher) AppendServiceUpdateHandler(f func()) {
|
||||
w.updateHandler = f
|
||||
}
|
||||
|
||||
func (w *watcher) ReadyHandler(f func(bool)) {
|
||||
w.readyHandler = f
|
||||
}
|
||||
|
||||
func shouldSubscribe(serviceName string) bool {
|
||||
prefixFilters := []string{"consumers:"}
|
||||
fullFilters := []string{""}
|
||||
|
||||
for _, f := range prefixFilters {
|
||||
if strings.HasPrefix(serviceName, f) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
for _, f := range fullFilters {
|
||||
if serviceName == f {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
Reference in New Issue
Block a user