70 lines
2.4 KiB
Go
70 lines
2.4 KiB
Go
package accounts
|
|
|
|
import "github.com/wangjia/pay/config"
|
|
|
|
// Router 是 gateway 面向的 Picker 实现:取候选(EnabledFor)→ 按 hint 排除 → 派发策略。
|
|
// 策略实例进程内单例(round_robin 计数器需长存);未配置/未知策略默认 round_robin。
|
|
type Router struct {
|
|
reg *Registry
|
|
strategies map[string]string // channel -> 策略名
|
|
byName map[string]Strategy
|
|
def Strategy
|
|
}
|
|
|
|
// NewRouter 装配路由。routing:channel→策略名(缺省/未知→round_robin);usage=nil→NopUsage。
|
|
// limit_aware 复合 round_robin 作为合格集内的均摊基础策略。
|
|
func NewRouter(reg *Registry, routing map[string]string, usage UsageSource) *Router {
|
|
rr := NewRoundRobin()
|
|
byName := map[string]Strategy{
|
|
StrategyRoundRobin: rr,
|
|
StrategyWeighted: NewWeighted(),
|
|
StrategyLimitAware: NewLimitAware(rr, usage),
|
|
}
|
|
cp := map[string]string{}
|
|
for k, v := range routing {
|
|
cp[k] = v
|
|
}
|
|
return &Router{reg: reg, strategies: cp, byName: byName, def: rr}
|
|
}
|
|
|
|
func (r *Router) strategyFor(channel string) Strategy {
|
|
if name, ok := r.strategies[channel]; ok {
|
|
if s, ok := r.byName[name]; ok {
|
|
return s
|
|
}
|
|
}
|
|
return r.def // 缺省 / 未知策略名 → round_robin(见计划 D3)
|
|
}
|
|
|
|
// Pick 实现 accounts.Picker。
|
|
// 注:cands 顺序 = EnabledFor 保留的 config.Accounts 声明顺序,原样传给策略——
|
|
// weighted 的桶按候选顺序累加边界(见 weighted.go),故 config.yaml 里账户顺序变了
|
|
// 等价于重新分桶(reorder = repin),同一 OutTradeNo 命中的账户可能跟着变,不是 bug。
|
|
func (r *Router) Pick(channel, region string, hint PickHint) (config.AccountConfig, error) {
|
|
cands := r.reg.EnabledFor(channel, region)
|
|
if len(cands) == 0 {
|
|
return config.AccountConfig{}, ErrNoAccount
|
|
}
|
|
if len(hint.ExcludeAccounts) > 0 {
|
|
if filtered := excludeAccounts(cands, hint.ExcludeAccounts); len(filtered) > 0 {
|
|
cands = filtered // 过滤到空则保留全集:单账户渠道 retry 仍可用(见计划 D4)
|
|
}
|
|
}
|
|
return r.strategyFor(channel).Pick(channel+"|"+region, cands, hint)
|
|
}
|
|
|
|
func excludeAccounts(cands []config.AccountConfig, exclude []string) []config.AccountConfig {
|
|
skip := make(map[string]struct{}, len(exclude))
|
|
for _, id := range exclude {
|
|
skip[id] = struct{}{}
|
|
}
|
|
out := make([]config.AccountConfig, 0, len(cands))
|
|
for _, c := range cands {
|
|
if _, ok := skip[c.AccountID]; ok {
|
|
continue
|
|
}
|
|
out = append(out, c)
|
|
}
|
|
return out
|
|
}
|