单二进制三角色 + Dock 适配器 + Swarm stack 达到可部署态;mgr1 实测订阅经 central-proxy bootstrap,探活 alive=41/52。 Co-authored-by: Cursor <cursoragent@cursor.com>
125 lines
3.1 KiB
Go
125 lines
3.1 KiB
Go
// registry.go:适配器注册表 + Health 聚合 + init 并发探活。
|
||
// 能力路由数据源(scheduler 侧按 intent+region+render 标签匹配)。
|
||
package dock
|
||
|
||
import (
|
||
"context"
|
||
"fmt"
|
||
"sync"
|
||
|
||
"onesvm.com/onesvm/browser-server/internal/contract"
|
||
)
|
||
|
||
// Registry 适配器注册表(map 名字 → 适配器;并发安全只读)。
|
||
type Registry struct {
|
||
mu sync.RWMutex
|
||
items map[string]contract.DockAdapter
|
||
order []string // 稳定遍历序(路由优先级依赖)
|
||
}
|
||
|
||
// NewRegistry 空注册表。
|
||
func NewRegistry() *Registry {
|
||
return &Registry{items: map[string]contract.DockAdapter{}}
|
||
}
|
||
|
||
// Register 注册(重名覆盖)。
|
||
func (r *Registry) Register(a contract.DockAdapter) {
|
||
r.mu.Lock()
|
||
defer r.mu.Unlock()
|
||
name := adapterDisplayName(a)
|
||
if _, exists := r.items[name]; !exists {
|
||
r.order = append(r.order, name)
|
||
}
|
||
r.items[name] = a
|
||
}
|
||
|
||
// Get 按名取。
|
||
func (r *Registry) Get(name string) (contract.DockAdapter, bool) {
|
||
r.mu.RLock()
|
||
defer r.mu.RUnlock()
|
||
a, ok := r.items[name]
|
||
return a, ok
|
||
}
|
||
|
||
// Names 全部名字(注册稳定序)。
|
||
func (r *Registry) Names() []string {
|
||
r.mu.RLock()
|
||
defer r.mu.RUnlock()
|
||
out := make([]string, len(r.order))
|
||
copy(out, r.order)
|
||
return out
|
||
}
|
||
|
||
// All 全部适配器(注册稳定序)。
|
||
func (r *Registry) All() []contract.DockAdapter {
|
||
r.mu.RLock()
|
||
defer r.mu.RUnlock()
|
||
out := make([]contract.DockAdapter, 0, len(r.order))
|
||
for _, k := range r.order {
|
||
out = append(out, r.items[k])
|
||
}
|
||
return out
|
||
}
|
||
|
||
// InitAll 并发 Init(探活;全部完成才返回,单适配器失败不阻塞其它——Health 摘除)。
|
||
func (r *Registry) InitAll(ctx context.Context) map[string]error {
|
||
var wg sync.WaitGroup
|
||
errs := make(map[string]error)
|
||
var mu sync.Mutex
|
||
for _, a := range r.All() {
|
||
wg.Add(1)
|
||
go func(a contract.DockAdapter) {
|
||
defer wg.Done()
|
||
err := a.Init(ctx)
|
||
mu.Lock()
|
||
if err != nil {
|
||
errs[adapterDisplayName(a)] = err
|
||
}
|
||
mu.Unlock()
|
||
}(a)
|
||
}
|
||
wg.Wait()
|
||
return errs
|
||
}
|
||
|
||
// HealthAll 聚合健康:名字 → Health。
|
||
func (r *Registry) HealthAll() map[string]contract.Health {
|
||
out := map[string]contract.Health{}
|
||
for _, a := range r.All() {
|
||
out[adapterDisplayName(a)] = a.Health()
|
||
}
|
||
return out
|
||
}
|
||
|
||
// HealthyAdapters 按 intent 过滤出健康适配器名(Health().ok=false 摘除走降级,
|
||
// design §4.5 降级条款 2)。
|
||
func (r *Registry) HealthyAdapters(intent string) []string {
|
||
var out []string
|
||
for _, a := range r.All() {
|
||
caps := a.Capabilities()
|
||
for _, i := range caps.Intents {
|
||
if i == intent && a.Health().OK {
|
||
out = append(out, adapterDisplayName(a))
|
||
break
|
||
}
|
||
}
|
||
}
|
||
return out
|
||
}
|
||
|
||
// adapterDisplayName 适配器展示名(Caps 无名字字段,由接口可选扩展携带;
|
||
// 默认回退 intent 首个 + 序列稳定名)。
|
||
func adapterDisplayName(a contract.DockAdapter) string {
|
||
if n, ok := a.(interface{ Name() string }); ok {
|
||
return n.Name()
|
||
}
|
||
return fmt.Sprintf("adapter-%p", a)
|
||
}
|
||
|
||
// TeardownAll 逆序优雅停机。
|
||
func (r *Registry) TeardownAll(ctx context.Context) {
|
||
all := r.All()
|
||
for i := len(all) - 1; i >= 0; i-- {
|
||
_ = all[i].Teardown(ctx)
|
||
}
|
||
}
|