2026-05-05 09:42:15 +08:00
|
|
|
package discover
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"net/http"
|
|
|
|
|
|
|
|
|
|
"apigo.cc/go/log"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// AppClient 用于管理单个请求的重试和负载均衡状态
|
|
|
|
|
type AppClient struct {
|
2026-05-05 13:59:03 +08:00
|
|
|
excludes map[string]bool // 本次请求已排除的节点
|
|
|
|
|
attempts int // 本次请求的重试次数
|
|
|
|
|
Logger *log.Logger // 用于日志记录的 Logger
|
|
|
|
|
App string // 目标应用名称
|
|
|
|
|
Method string // 请求方法
|
|
|
|
|
Path string // 请求路径
|
|
|
|
|
Data map[string]any // 请求数据
|
|
|
|
|
Headers map[string]string // 请求头
|
2026-05-05 09:42:15 +08:00
|
|
|
}
|
|
|
|
|
|
2026-05-05 13:59:03 +08:00
|
|
|
// logError 记录 Discover 客户端错误
|
|
|
|
|
func (ac *AppClient) logError(msg string, extra ...any) {
|
2026-05-05 09:42:15 +08:00
|
|
|
if ac.Logger == nil {
|
|
|
|
|
ac.Logger = log.DefaultLogger
|
|
|
|
|
}
|
2026-05-05 13:59:03 +08:00
|
|
|
ac.Logger.Error("Discover Client: "+msg, extra...)
|
2026-05-05 09:42:15 +08:00
|
|
|
}
|
|
|
|
|
|
2026-05-05 13:59:03 +08:00
|
|
|
// Next 获取下一个可用节点
|
2026-05-05 09:42:15 +08:00
|
|
|
func (ac *AppClient) Next(app string, request *http.Request) *NodeInfo {
|
|
|
|
|
return ac.NextWithNode(app, "", request)
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-05 13:59:03 +08:00
|
|
|
// CheckApp 检查并尝试添加应用
|
2026-05-05 09:42:15 +08:00
|
|
|
func (ac *AppClient) CheckApp(app string) bool {
|
|
|
|
|
nodes := getAppNodes(app)
|
|
|
|
|
if nodes == nil {
|
|
|
|
|
if !addApp(app, "", true) {
|
|
|
|
|
ac.logError("app not found", "app", app, "calls", Config.Calls)
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-05 13:59:03 +08:00
|
|
|
// NextWithNode 获取下一个可用节点,支持指定节点
|
2026-05-05 09:42:15 +08:00
|
|
|
func (ac *AppClient) NextWithNode(app, withNode string, request *http.Request) *NodeInfo {
|
|
|
|
|
if ac.excludes == nil {
|
|
|
|
|
ac.excludes = make(map[string]bool)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
allNodes := getAppNodes(app)
|
|
|
|
|
if len(allNodes) == 0 {
|
|
|
|
|
ac.logError("node not found", "app", app)
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-05 13:59:03 +08:00
|
|
|
ac.attempts++
|
2026-05-05 09:42:15 +08:00
|
|
|
if withNode != "" {
|
|
|
|
|
ac.excludes[withNode] = true
|
|
|
|
|
return allNodes[withNode]
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-05 13:59:03 +08:00
|
|
|
readyNodes := make([]*NodeInfo, 0, len(allNodes))
|
2026-05-05 09:42:15 +08:00
|
|
|
for _, node := range allNodes {
|
2026-05-05 13:59:03 +08:00
|
|
|
if ac.excludes[node.Addr] || node.FailedTimes.Load() >= int32(Config.CallRetryTimes) {
|
2026-05-05 09:42:15 +08:00
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
readyNodes = append(readyNodes, node)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if len(readyNodes) == 0 {
|
|
|
|
|
// 如果没有可用节点,尝试已经失败但未被本次请求排除的节点
|
|
|
|
|
for _, node := range allNodes {
|
|
|
|
|
if !ac.excludes[node.Addr] {
|
|
|
|
|
readyNodes = append(readyNodes, node)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var node *NodeInfo
|
|
|
|
|
if len(readyNodes) > 0 {
|
|
|
|
|
node = settedLoadBalancer.Next(ac, readyNodes, request)
|
|
|
|
|
if node != nil {
|
|
|
|
|
ac.excludes[node.Addr] = true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if node == nil {
|
2026-05-05 13:59:03 +08:00
|
|
|
ac.logError("no available node", "app", app, "attempts", ac.attempts)
|
2026-05-05 09:42:15 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return node
|
|
|
|
|
}
|