2026-05-05 09:42:15 +08:00
|
|
|
|
package discover
|
|
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
|
"fmt"
|
|
|
|
|
|
"net"
|
|
|
|
|
|
"net/http"
|
|
|
|
|
|
"os"
|
|
|
|
|
|
"os/signal"
|
|
|
|
|
|
"regexp"
|
|
|
|
|
|
"strings"
|
|
|
|
|
|
"sync"
|
|
|
|
|
|
"syscall"
|
|
|
|
|
|
"time"
|
|
|
|
|
|
|
|
|
|
|
|
"apigo.cc/go/cast"
|
|
|
|
|
|
"apigo.cc/go/config"
|
|
|
|
|
|
"apigo.cc/go/id"
|
|
|
|
|
|
"apigo.cc/go/log"
|
|
|
|
|
|
"apigo.cc/go/redis"
|
|
|
|
|
|
)
|
|
|
|
|
|
|
2026-05-05 14:27:15 +08:00
|
|
|
|
// Discoverer 发现服务实例
|
|
|
|
|
|
type Discoverer struct {
|
|
|
|
|
|
Config ConfigStruct
|
|
|
|
|
|
|
2026-05-05 09:42:15 +08:00
|
|
|
|
serverRedisPool *redis.Redis
|
|
|
|
|
|
clientRedisPool *redis.Redis
|
|
|
|
|
|
pubsubRedisPool *redis.Redis
|
2026-05-05 14:27:15 +08:00
|
|
|
|
isServer bool
|
|
|
|
|
|
isClient bool
|
|
|
|
|
|
daemonRunning bool
|
|
|
|
|
|
myAddr string
|
|
|
|
|
|
logger *log.Logger
|
|
|
|
|
|
inited bool
|
2026-05-05 09:42:15 +08:00
|
|
|
|
|
|
|
|
|
|
daemonStopChan chan bool
|
|
|
|
|
|
appLock sync.RWMutex
|
2026-05-05 14:27:15 +08:00
|
|
|
|
calls map[string]*callInfoType
|
|
|
|
|
|
appNodes map[string]map[string]*NodeInfo
|
|
|
|
|
|
appSubscribed map[string]bool
|
2026-05-05 09:42:15 +08:00
|
|
|
|
|
2026-05-05 14:27:15 +08:00
|
|
|
|
settedRoute func(*AppClient, *http.Request)
|
|
|
|
|
|
settedLoadBalancer LoadBalancer
|
|
|
|
|
|
}
|
2026-05-05 09:42:15 +08:00
|
|
|
|
|
|
|
|
|
|
type callInfoType struct {
|
|
|
|
|
|
Timeout time.Duration
|
|
|
|
|
|
HttpVersion int
|
|
|
|
|
|
Token string
|
|
|
|
|
|
SSL bool
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-05 14:27:15 +08:00
|
|
|
|
// DefaultDiscoverer 默认的全局发现服务实例
|
|
|
|
|
|
var DefaultDiscoverer = NewDiscoverer()
|
|
|
|
|
|
|
|
|
|
|
|
// NewDiscoverer 创建一个新的发现服务实例
|
|
|
|
|
|
func NewDiscoverer() *Discoverer {
|
|
|
|
|
|
return &Discoverer{
|
|
|
|
|
|
Config: ConfigStruct{
|
|
|
|
|
|
Weight: 100,
|
|
|
|
|
|
CallRetryTimes: 10,
|
|
|
|
|
|
},
|
|
|
|
|
|
logger: log.DefaultLogger,
|
|
|
|
|
|
calls: make(map[string]*callInfoType),
|
|
|
|
|
|
appNodes: make(map[string]map[string]*NodeInfo),
|
|
|
|
|
|
appSubscribed: make(map[string]bool),
|
|
|
|
|
|
settedLoadBalancer: &DefaultLoadBalancer{},
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-05 13:59:03 +08:00
|
|
|
|
// IsServer 返回当前节点是否作为服务端运行
|
2026-05-05 14:27:15 +08:00
|
|
|
|
func (d *Discoverer) IsServer() bool { return d.isServer }
|
2026-05-05 13:59:03 +08:00
|
|
|
|
|
|
|
|
|
|
// IsClient 返回当前节点是否作为客户端运行
|
2026-05-05 14:27:15 +08:00
|
|
|
|
func (d *Discoverer) IsClient() bool { return d.isClient }
|
2026-05-05 09:42:15 +08:00
|
|
|
|
|
2026-05-05 14:27:15 +08:00
|
|
|
|
func (d *Discoverer) logError(msg string, extra ...any) {
|
|
|
|
|
|
d.logger.Error("Discover: "+msg, append(extra, "app", d.Config.App, "addr", d.myAddr)...)
|
2026-05-05 09:42:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-05 14:27:15 +08:00
|
|
|
|
func (d *Discoverer) logInfo(msg string, extra ...any) {
|
|
|
|
|
|
d.logger.Info("Discover: "+msg, append(extra, "app", d.Config.App, "addr", d.myAddr)...)
|
2026-05-05 09:42:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-05 13:59:03 +08:00
|
|
|
|
// SetLogger 设置 Discover 使用的全局 Logger
|
2026-05-05 14:27:15 +08:00
|
|
|
|
func (d *Discoverer) SetLogger(logger *log.Logger) {
|
|
|
|
|
|
d.logger = logger
|
2026-05-05 09:42:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-05 14:27:15 +08:00
|
|
|
|
// Init 初始化 Discover 配置
|
|
|
|
|
|
func (d *Discoverer) Init() {
|
|
|
|
|
|
d.appLock.Lock()
|
|
|
|
|
|
defer d.appLock.Unlock()
|
|
|
|
|
|
if d.inited {
|
2026-05-05 09:42:15 +08:00
|
|
|
|
return
|
|
|
|
|
|
}
|
2026-05-05 14:27:15 +08:00
|
|
|
|
d.inited = true
|
|
|
|
|
|
// 如果是默认实例,尝试加载配置
|
|
|
|
|
|
if d == DefaultDiscoverer {
|
|
|
|
|
|
_ = config.Load(&d.Config, "discover")
|
|
|
|
|
|
Config = d.Config // 保持 Config 变量同步
|
|
|
|
|
|
}
|
2026-05-05 09:42:15 +08:00
|
|
|
|
|
2026-05-05 14:27:15 +08:00
|
|
|
|
if d.Config.CallRetryTimes <= 0 {
|
|
|
|
|
|
d.Config.CallRetryTimes = 10
|
2026-05-05 09:42:15 +08:00
|
|
|
|
}
|
2026-05-05 14:27:15 +08:00
|
|
|
|
if d.Config.Weight <= 0 {
|
|
|
|
|
|
d.Config.Weight = 100
|
2026-05-05 09:42:15 +08:00
|
|
|
|
}
|
2026-05-05 14:27:15 +08:00
|
|
|
|
if d.Config.Registry == "" {
|
|
|
|
|
|
d.Config.Registry = DefaultRegistry
|
2026-05-05 09:42:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-05 14:27:15 +08:00
|
|
|
|
if d.logger == log.DefaultLogger || d.logger == nil {
|
|
|
|
|
|
d.logger = log.New(id.MakeID(12))
|
|
|
|
|
|
}
|
2026-05-05 09:42:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-05 13:59:03 +08:00
|
|
|
|
// Start 启动服务发现,指定当前节点的外部访问地址
|
2026-05-05 14:27:15 +08:00
|
|
|
|
func (d *Discoverer) Start(addr string) bool {
|
|
|
|
|
|
d.Init()
|
|
|
|
|
|
d.myAddr = addr
|
|
|
|
|
|
|
|
|
|
|
|
d.isServer = d.Config.App != "" && d.Config.Weight > 0
|
|
|
|
|
|
if d.isServer && d.Config.Registry != "" {
|
|
|
|
|
|
d.serverRedisPool = redis.GetRedis(d.Config.Registry, d.logger)
|
|
|
|
|
|
if d.serverRedisPool.Error != nil {
|
|
|
|
|
|
d.logError(d.serverRedisPool.Error.Error())
|
2026-05-05 09:42:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 注册节点
|
2026-05-05 14:27:15 +08:00
|
|
|
|
if d.serverRedisPool.Do("HSET", d.Config.App, addr, d.Config.Weight).Error == nil {
|
|
|
|
|
|
d.serverRedisPool.Do("SETEX", d.Config.App+"_"+addr, 10, "1")
|
|
|
|
|
|
d.logInfo("registered")
|
|
|
|
|
|
d.serverRedisPool.PUBLISH("CH_"+d.Config.App, fmt.Sprintf("%s %d", addr, d.Config.Weight))
|
|
|
|
|
|
d.daemonRunning = true
|
|
|
|
|
|
d.daemonStopChan = make(chan bool)
|
|
|
|
|
|
go d.daemon()
|
2026-05-05 09:42:15 +08:00
|
|
|
|
} else {
|
2026-05-05 14:27:15 +08:00
|
|
|
|
d.logError("register failed")
|
2026-05-05 09:42:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-05 14:27:15 +08:00
|
|
|
|
calls := d.getCalls()
|
2026-05-05 09:42:15 +08:00
|
|
|
|
if len(calls) > 0 {
|
|
|
|
|
|
for app, conf := range calls {
|
2026-05-05 14:27:15 +08:00
|
|
|
|
d.addApp(app, conf, false)
|
2026-05-05 09:42:15 +08:00
|
|
|
|
}
|
2026-05-05 14:27:15 +08:00
|
|
|
|
if !d.startSub() {
|
2026-05-05 09:42:15 +08:00
|
|
|
|
return false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return true
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-05 14:27:15 +08:00
|
|
|
|
func (d *Discoverer) daemon() {
|
|
|
|
|
|
d.logInfo("daemon thread started")
|
2026-05-05 13:59:03 +08:00
|
|
|
|
ticker := time.NewTicker(5 * time.Second)
|
2026-05-05 09:42:15 +08:00
|
|
|
|
defer ticker.Stop()
|
|
|
|
|
|
|
2026-05-05 14:27:15 +08:00
|
|
|
|
for d.daemonRunning {
|
2026-05-05 09:42:15 +08:00
|
|
|
|
<-ticker.C
|
2026-05-05 14:27:15 +08:00
|
|
|
|
if !d.daemonRunning {
|
2026-05-05 09:42:15 +08:00
|
|
|
|
break
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-05 14:27:15 +08:00
|
|
|
|
if d.isServer && d.serverRedisPool != nil {
|
|
|
|
|
|
if !d.serverRedisPool.Do("HEXISTS", d.Config.App, d.myAddr).Bool() {
|
|
|
|
|
|
d.logInfo("lost app registered info, re-registering")
|
|
|
|
|
|
if d.serverRedisPool.Do("HSET", d.Config.App, d.myAddr, d.Config.Weight).Error == nil {
|
|
|
|
|
|
d.serverRedisPool.Do("SETEX", d.Config.App+"_"+d.myAddr, 10, "1")
|
|
|
|
|
|
d.serverRedisPool.PUBLISH("CH_"+d.Config.App, fmt.Sprintf("%s %d", d.myAddr, d.Config.Weight))
|
2026-05-05 09:42:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
} else {
|
2026-05-05 14:27:15 +08:00
|
|
|
|
d.serverRedisPool.Do("SETEX", d.Config.App+"_"+d.myAddr, 10, "1")
|
2026-05-05 09:42:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-05-05 14:27:15 +08:00
|
|
|
|
d.logInfo("daemon thread stopped")
|
|
|
|
|
|
if d.daemonStopChan != nil {
|
|
|
|
|
|
d.daemonStopChan <- true
|
2026-05-05 09:42:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-05 14:27:15 +08:00
|
|
|
|
func (d *Discoverer) startSub() bool {
|
|
|
|
|
|
if d.Config.Registry == "" {
|
2026-05-05 09:42:15 +08:00
|
|
|
|
return true
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-05 14:27:15 +08:00
|
|
|
|
d.appLock.Lock()
|
|
|
|
|
|
if d.clientRedisPool == nil {
|
|
|
|
|
|
d.clientRedisPool = redis.GetRedis(d.Config.Registry, d.logger)
|
2026-05-05 09:42:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-05 14:27:15 +08:00
|
|
|
|
if d.pubsubRedisPool == nil {
|
|
|
|
|
|
d.pubsubRedisPool = redis.GetRedis(d.Config.Registry, d.logger.New(id.MakeID(12)))
|
2026-05-05 09:42:15 +08:00
|
|
|
|
// 订阅所有已注册的应用
|
2026-05-05 14:27:15 +08:00
|
|
|
|
for app := range d.appSubscribed {
|
|
|
|
|
|
d.subscribeAppUnderLock(app)
|
2026-05-05 09:42:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
// 必须在释放锁之前完成配置,但在释放锁之后启动,避免死锁
|
2026-05-05 14:27:15 +08:00
|
|
|
|
d.appLock.Unlock()
|
|
|
|
|
|
d.pubsubRedisPool.Start()
|
|
|
|
|
|
d.appLock.Lock()
|
2026-05-05 09:42:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-05 14:27:15 +08:00
|
|
|
|
d.isClient = true
|
|
|
|
|
|
d.appLock.Unlock()
|
2026-05-05 09:42:15 +08:00
|
|
|
|
return true
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-05 14:27:15 +08:00
|
|
|
|
func (d *Discoverer) subscribeAppUnderLock(app string) {
|
|
|
|
|
|
d.pubsubRedisPool.Subscribe("CH_"+app, func() {
|
|
|
|
|
|
d.fetchApp(app)
|
2026-05-05 09:42:15 +08:00
|
|
|
|
}, func(data []byte) {
|
|
|
|
|
|
a := strings.Split(string(data), " ")
|
|
|
|
|
|
addr := a[0]
|
|
|
|
|
|
weight := 0
|
|
|
|
|
|
if len(a) == 2 {
|
|
|
|
|
|
weight = cast.Int(a[1])
|
|
|
|
|
|
}
|
2026-05-05 14:27:15 +08:00
|
|
|
|
d.logInfo("received node update", "app", app, "addr", addr, "weight", weight)
|
|
|
|
|
|
d.pushNode(app, addr, weight)
|
2026-05-05 09:42:15 +08:00
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-05 13:59:03 +08:00
|
|
|
|
// Stop 停止 Discover 并从注册中心注销当前节点
|
2026-05-05 14:27:15 +08:00
|
|
|
|
func (d *Discoverer) Stop() {
|
|
|
|
|
|
d.appLock.Lock()
|
|
|
|
|
|
if d.isClient && d.pubsubRedisPool != nil {
|
|
|
|
|
|
d.pubsubRedisPool.Stop()
|
|
|
|
|
|
d.isClient = false
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if d.isServer {
|
|
|
|
|
|
d.daemonRunning = false
|
|
|
|
|
|
if d.serverRedisPool != nil {
|
|
|
|
|
|
d.serverRedisPool.Do("HDEL", d.Config.App, d.myAddr)
|
|
|
|
|
|
d.serverRedisPool.Do("DEL", d.Config.App+"_"+d.myAddr)
|
|
|
|
|
|
d.serverRedisPool.PUBLISH("CH_"+d.Config.App, fmt.Sprintf("%s %d", d.myAddr, 0))
|
2026-05-05 09:42:15 +08:00
|
|
|
|
}
|
2026-05-05 14:27:15 +08:00
|
|
|
|
d.isServer = false
|
2026-05-05 09:42:15 +08:00
|
|
|
|
}
|
2026-05-05 14:27:15 +08:00
|
|
|
|
d.appLock.Unlock()
|
2026-05-05 09:42:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-05 13:59:03 +08:00
|
|
|
|
// Wait 等待守护进程退出
|
2026-05-05 14:27:15 +08:00
|
|
|
|
func (d *Discoverer) Wait() {
|
|
|
|
|
|
if d.daemonStopChan != nil {
|
|
|
|
|
|
<-d.daemonStopChan
|
|
|
|
|
|
d.daemonStopChan = nil
|
2026-05-05 09:42:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-05 13:59:03 +08:00
|
|
|
|
// EasyStart 自动根据环境变量和本地网卡信息启动 Discover
|
2026-05-05 14:27:15 +08:00
|
|
|
|
func (d *Discoverer) EasyStart() (string, int) {
|
|
|
|
|
|
d.Init()
|
2026-05-05 09:42:15 +08:00
|
|
|
|
port := 0
|
|
|
|
|
|
if listen := os.Getenv("DISCOVER_LISTEN"); listen != "" {
|
|
|
|
|
|
if _, p, err := net.SplitHostPort(listen); err == nil {
|
|
|
|
|
|
port = cast.Int(p)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
port = cast.Int(listen)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ln, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
|
|
|
|
|
|
if err != nil {
|
2026-05-05 14:27:15 +08:00
|
|
|
|
d.logError("failed to listen", "err", err)
|
2026-05-05 09:42:15 +08:00
|
|
|
|
return "", 0
|
|
|
|
|
|
}
|
|
|
|
|
|
addrInfo := ln.Addr().(*net.TCPAddr)
|
|
|
|
|
|
_ = ln.Close()
|
|
|
|
|
|
port = addrInfo.Port
|
|
|
|
|
|
|
|
|
|
|
|
ip := addrInfo.IP
|
|
|
|
|
|
if !ip.IsGlobalUnicast() {
|
|
|
|
|
|
addrs, _ := net.InterfaceAddrs()
|
|
|
|
|
|
for _, a := range addrs {
|
|
|
|
|
|
if an, ok := a.(*net.IPNet); ok {
|
|
|
|
|
|
ip4 := an.IP.To4()
|
|
|
|
|
|
if ip4 == nil || !ip4.IsGlobalUnicast() {
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
2026-05-05 14:27:15 +08:00
|
|
|
|
if d.Config.IpPrefix != "" && strings.HasPrefix(ip4.String(), d.Config.IpPrefix) {
|
2026-05-05 09:42:15 +08:00
|
|
|
|
ip = ip4
|
|
|
|
|
|
break
|
|
|
|
|
|
}
|
|
|
|
|
|
if !strings.HasPrefix(ip4.String(), "172.17.") {
|
|
|
|
|
|
ip = ip4
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
addr := fmt.Sprintf("%s:%d", ip.String(), port)
|
2026-05-05 14:27:15 +08:00
|
|
|
|
if !d.Start(addr) {
|
2026-05-05 09:42:15 +08:00
|
|
|
|
return "", 0
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
sigChan := make(chan os.Signal, 1)
|
|
|
|
|
|
signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
|
|
|
|
|
|
go func() {
|
|
|
|
|
|
<-sigChan
|
2026-05-05 14:27:15 +08:00
|
|
|
|
d.Stop()
|
2026-05-05 09:42:15 +08:00
|
|
|
|
}()
|
|
|
|
|
|
|
|
|
|
|
|
return ip.String(), port
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-05 13:59:03 +08:00
|
|
|
|
// AddExternalApp 动态添加需要发现的外部应用
|
2026-05-05 14:27:15 +08:00
|
|
|
|
func (d *Discoverer) AddExternalApp(app, callConf string) bool {
|
|
|
|
|
|
if d.addApp(app, callConf, true) {
|
|
|
|
|
|
if !d.isClient {
|
|
|
|
|
|
d.startSub()
|
2026-05-05 09:42:15 +08:00
|
|
|
|
} else {
|
2026-05-05 14:27:15 +08:00
|
|
|
|
d.appLock.Lock()
|
|
|
|
|
|
d.subscribeAppUnderLock(app)
|
|
|
|
|
|
d.appLock.Unlock()
|
|
|
|
|
|
d.fetchApp(app) // 同步拉取一次
|
2026-05-05 09:42:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
return true
|
|
|
|
|
|
}
|
|
|
|
|
|
return false
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-05 14:27:15 +08:00
|
|
|
|
// SetNode 手动设置某个服务的节点信息
|
|
|
|
|
|
func (d *Discoverer) SetNode(app, addr string, weight int) {
|
|
|
|
|
|
d.pushNode(app, addr, weight)
|
2026-05-05 09:42:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-05 14:27:15 +08:00
|
|
|
|
func (d *Discoverer) getCallInfo(app string) *callInfoType {
|
|
|
|
|
|
d.appLock.RLock()
|
|
|
|
|
|
defer d.appLock.RUnlock()
|
|
|
|
|
|
return d.calls[app]
|
2026-05-05 09:42:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var numberMatcher = regexp.MustCompile(`^\d+(s|ms|us|µs|ns?)?$`)
|
|
|
|
|
|
|
2026-05-05 14:27:15 +08:00
|
|
|
|
func (d *Discoverer) addApp(app, callConf string, fetch bool) bool {
|
|
|
|
|
|
d.appLock.Lock()
|
|
|
|
|
|
if d.Config.Calls == nil {
|
|
|
|
|
|
d.Config.Calls = make(map[string]string)
|
2026-05-05 09:42:15 +08:00
|
|
|
|
}
|
2026-05-05 14:27:15 +08:00
|
|
|
|
if d.Config.Calls[app] == callConf && d.appNodes[app] != nil {
|
|
|
|
|
|
d.appLock.Unlock()
|
2026-05-05 09:42:15 +08:00
|
|
|
|
return false
|
|
|
|
|
|
}
|
2026-05-05 14:27:15 +08:00
|
|
|
|
d.Config.Calls[app] = callConf
|
2026-05-05 09:42:15 +08:00
|
|
|
|
|
|
|
|
|
|
callInfo := &callInfoType{
|
|
|
|
|
|
Timeout: 10 * time.Second,
|
|
|
|
|
|
HttpVersion: 2,
|
|
|
|
|
|
SSL: false,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for _, v := range cast.Split(callConf, ":") {
|
|
|
|
|
|
switch v {
|
|
|
|
|
|
case "1":
|
|
|
|
|
|
callInfo.HttpVersion = 1
|
|
|
|
|
|
case "2":
|
|
|
|
|
|
callInfo.HttpVersion = 2
|
|
|
|
|
|
case "s", "https":
|
|
|
|
|
|
callInfo.SSL = true
|
|
|
|
|
|
callInfo.HttpVersion = 2
|
|
|
|
|
|
case "http":
|
|
|
|
|
|
callInfo.SSL = false
|
|
|
|
|
|
callInfo.HttpVersion = 1
|
|
|
|
|
|
case "h2c":
|
|
|
|
|
|
callInfo.SSL = false
|
|
|
|
|
|
callInfo.HttpVersion = 2
|
|
|
|
|
|
default:
|
|
|
|
|
|
if numberMatcher.MatchString(v) {
|
|
|
|
|
|
callInfo.Timeout = cast.Duration(v)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
callInfo.Token = v
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-05 14:27:15 +08:00
|
|
|
|
d.calls[app] = callInfo
|
|
|
|
|
|
if d.appNodes[app] == nil {
|
|
|
|
|
|
d.appNodes[app] = make(map[string]*NodeInfo)
|
2026-05-05 09:42:15 +08:00
|
|
|
|
}
|
2026-05-05 14:27:15 +08:00
|
|
|
|
d.appSubscribed[app] = true
|
|
|
|
|
|
d.appLock.Unlock()
|
2026-05-05 09:42:15 +08:00
|
|
|
|
|
2026-05-05 14:27:15 +08:00
|
|
|
|
if fetch && d.isClient {
|
|
|
|
|
|
d.fetchApp(app)
|
2026-05-05 09:42:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
return true
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-05 14:27:15 +08:00
|
|
|
|
func (d *Discoverer) fetchApp(app string) {
|
|
|
|
|
|
d.appLock.RLock()
|
|
|
|
|
|
pool := d.clientRedisPool
|
|
|
|
|
|
d.appLock.RUnlock()
|
2026-05-05 09:42:15 +08:00
|
|
|
|
if pool == nil {
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
results := pool.Do("HGETALL", app).ResultMap()
|
2026-05-05 14:27:15 +08:00
|
|
|
|
|
2026-05-05 09:42:15 +08:00
|
|
|
|
// 检查存活
|
|
|
|
|
|
for addr := range results {
|
|
|
|
|
|
if !pool.Do("EXISTS", app+"_"+addr).Bool() {
|
|
|
|
|
|
pool.Do("HDEL", app, addr)
|
|
|
|
|
|
delete(results, addr)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-05 14:27:15 +08:00
|
|
|
|
currentNodes := d.getAppNodes(app)
|
2026-05-05 09:42:15 +08:00
|
|
|
|
if currentNodes != nil {
|
|
|
|
|
|
for addr := range currentNodes {
|
|
|
|
|
|
if _, ok := results[addr]; !ok {
|
2026-05-05 14:27:15 +08:00
|
|
|
|
d.pushNode(app, addr, 0)
|
2026-05-05 09:42:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for addr, res := range results {
|
2026-05-05 14:27:15 +08:00
|
|
|
|
d.pushNode(app, addr, res.Int())
|
2026-05-05 09:42:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-05 14:27:15 +08:00
|
|
|
|
func (d *Discoverer) getAppNodes(app string) map[string]*NodeInfo {
|
|
|
|
|
|
d.appLock.RLock()
|
|
|
|
|
|
defer d.appLock.RUnlock()
|
|
|
|
|
|
if d.appNodes[app] == nil {
|
2026-05-05 09:42:15 +08:00
|
|
|
|
return nil
|
|
|
|
|
|
}
|
|
|
|
|
|
nodes := make(map[string]*NodeInfo)
|
2026-05-05 14:27:15 +08:00
|
|
|
|
for k, v := range d.appNodes[app] {
|
2026-05-05 09:42:15 +08:00
|
|
|
|
nodes[k] = v
|
|
|
|
|
|
}
|
|
|
|
|
|
return nodes
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-05 14:27:15 +08:00
|
|
|
|
func (d *Discoverer) getCalls() map[string]string {
|
|
|
|
|
|
d.appLock.RLock()
|
|
|
|
|
|
defer d.appLock.RUnlock()
|
2026-05-05 09:42:15 +08:00
|
|
|
|
calls := make(map[string]string)
|
2026-05-05 14:27:15 +08:00
|
|
|
|
for k, v := range d.Config.Calls {
|
2026-05-05 09:42:15 +08:00
|
|
|
|
calls[k] = v
|
|
|
|
|
|
}
|
|
|
|
|
|
return calls
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-05 13:59:03 +08:00
|
|
|
|
// GetAppNodes 获取某个应用的所有节点列表
|
2026-05-05 14:27:15 +08:00
|
|
|
|
func (d *Discoverer) GetAppNodes(app string) map[string]*NodeInfo {
|
|
|
|
|
|
return d.getAppNodes(app)
|
2026-05-05 09:42:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-05 14:27:15 +08:00
|
|
|
|
func (d *Discoverer) pushNode(app, addr string, weight int) {
|
|
|
|
|
|
d.appLock.Lock()
|
|
|
|
|
|
defer d.appLock.Unlock()
|
2026-05-05 09:42:15 +08:00
|
|
|
|
|
|
|
|
|
|
if weight <= 0 {
|
2026-05-05 14:27:15 +08:00
|
|
|
|
if d.appNodes[app] != nil {
|
|
|
|
|
|
delete(d.appNodes[app], addr)
|
2026-05-05 09:42:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
return
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-05 14:27:15 +08:00
|
|
|
|
if d.appNodes[app] == nil {
|
|
|
|
|
|
d.appNodes[app] = make(map[string]*NodeInfo)
|
2026-05-05 09:42:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-05-05 14:27:15 +08:00
|
|
|
|
if node, ok := d.appNodes[app][addr]; ok {
|
2026-05-05 09:42:15 +08:00
|
|
|
|
if node.Weight != weight {
|
2026-05-05 13:59:03 +08:00
|
|
|
|
used := node.UsedTimes.Load()
|
|
|
|
|
|
node.UsedTimes.Store(uint64(float64(used) / float64(node.Weight) * float64(weight)))
|
2026-05-05 09:42:15 +08:00
|
|
|
|
node.Weight = weight
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
var avgUsed uint64 = 0
|
2026-05-05 14:27:15 +08:00
|
|
|
|
if len(d.appNodes[app]) > 0 {
|
2026-05-05 09:42:15 +08:00
|
|
|
|
var totalScore float64
|
2026-05-05 14:27:15 +08:00
|
|
|
|
for _, n := range d.appNodes[app] {
|
2026-05-05 13:59:03 +08:00
|
|
|
|
totalScore += float64(n.UsedTimes.Load()) / float64(n.Weight)
|
2026-05-05 09:42:15 +08:00
|
|
|
|
}
|
2026-05-05 14:27:15 +08:00
|
|
|
|
avgUsed = uint64(totalScore / float64(len(d.appNodes[app])) * float64(weight))
|
2026-05-05 09:42:15 +08:00
|
|
|
|
}
|
2026-05-05 13:59:03 +08:00
|
|
|
|
node := &NodeInfo{
|
|
|
|
|
|
Addr: addr,
|
|
|
|
|
|
Weight: weight,
|
2026-05-05 09:42:15 +08:00
|
|
|
|
}
|
2026-05-05 13:59:03 +08:00
|
|
|
|
node.UsedTimes.Store(avgUsed)
|
2026-05-05 14:27:15 +08:00
|
|
|
|
d.appNodes[app][addr] = node
|
2026-05-05 09:42:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-05-05 14:27:15 +08:00
|
|
|
|
|
|
|
|
|
|
// 以下是包级别 API,通过转发给 DefaultDiscoverer 实现兼容性
|
|
|
|
|
|
|
|
|
|
|
|
func IsServer() bool { return DefaultDiscoverer.IsServer() }
|
|
|
|
|
|
func IsClient() bool { return DefaultDiscoverer.IsClient() }
|
|
|
|
|
|
|
|
|
|
|
|
func logError(msg string, extra ...any) {
|
|
|
|
|
|
DefaultDiscoverer.logError(msg, extra...)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func logInfo(msg string, extra ...any) {
|
|
|
|
|
|
DefaultDiscoverer.logInfo(msg, extra...)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func SetLogger(logger *log.Logger) {
|
|
|
|
|
|
DefaultDiscoverer.SetLogger(logger)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func Init() {
|
|
|
|
|
|
DefaultDiscoverer.Init()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func Start(addr string) bool {
|
|
|
|
|
|
return DefaultDiscoverer.Start(addr)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func Stop() {
|
|
|
|
|
|
DefaultDiscoverer.Stop()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func Wait() {
|
|
|
|
|
|
DefaultDiscoverer.Wait()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func EasyStart() (string, int) {
|
|
|
|
|
|
return DefaultDiscoverer.EasyStart()
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func AddExternalApp(app, callConf string) bool {
|
|
|
|
|
|
return DefaultDiscoverer.AddExternalApp(app, callConf)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func SetNode(app, addr string, weight int) {
|
|
|
|
|
|
DefaultDiscoverer.SetNode(app, addr, weight)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
func GetAppNodes(app string) map[string]*NodeInfo {
|
|
|
|
|
|
return DefaultDiscoverer.GetAppNodes(app)
|
|
|
|
|
|
}
|