原生观察者模式不支持权重,因其契约要求通知无序、平等、松耦合;go中可通过map[string]struct{obs observer; weight int}实现分级调度,高权重要同步执行,低权重异步分发,避免排序开销与顺序幻觉。

为什么原生观察者模式不支持权重?
Go 语言没有内置 Observer 或事件总线,所有实现都基于接口 + 切片/Map + 手动调度。而“权重”本质是通知顺序或执行优先级的控制逻辑,不在观察者模式契约内——Observer.Update() 被设计为无序、并行、平等调用。强行在 Notify() 里按权重排序,会破坏松耦合,也违背“观察者不应感知彼此存在”的原则。
用 map[string]Observer + 权重字段实现分级调度
最轻量可行的做法:不改接口,只在 Subject 内部结构上增加权重维度。把观察者存进 map 时附带权重值,通知时按需分批或限流,而不是排序后串行调用(那会拖慢整个流程)。
-
Subject字段从observers []Observer改为observers map[string]struct{ obs Observer; weight int },用字符串 ID 做键,避免接口比较不可靠的问题 - 注册时显式传入权重:
s.Attach("logger", loggerObs, 10);高权重(如监控告警)设为 90,低权重(如调试日志)设为 10 -
Notify()不直接遍历全部,而是拆成两步:先收集weight >= 50的关键观察者同步调用(保证不丢),再把其余观察者异步分发到 goroutine 池中
示例片段:
func (s *Subject) Notify(event interface{}) {
s.mu.RLock()
critical := make([]Observer, 0)
others := make([]Observer, 0)
for _, entry := range s.observers {
if entry.weight >= 50 {
critical = append(critical, entry.obs)
} else {
others = append(others, entry.obs)
}
}
s.mu.RUnlock()
// 同步执行高权重
for _, o := range critical {
o.Update(event)
}
// 异步分发低权重(可加 worker pool 控制并发)
for _, o := range others {
go o.Update(event)
}
}
别用 sort.Slice 给观察者排序通知
常见错误是想“让权重高的先收到”,于是每次 Notify() 都对观察者切片做 sort.Slice,再 for-range 调用。这会导致三个实际问题:
立即学习“go语言免费学习笔记(深入)”;
- 每次通知都要分配新切片 + 排序,GC 压力上升,QPS 高时毛刺明显
- 如果某个
Observer.Update()内部调用了Detach(),而你正在遍历排序后的切片,可能 panic 或漏掉后续项 - 权重不是实时优先级——它反映的是重要性,不是执行时序。网络延迟、goroutine 调度本身就不保证顺序,硬保“谁先谁后”没意义
真正需要权重的场景,该换 EventBus + topic
如果你发现业务里频繁出现“这个事件必须先通知 A,等 A 成功后再通知 B,C 只在失败时触发”,那已经超出观察者模式能力边界。这不是权重问题,是**事件编排**问题。
- 此时应转向
EventBus设计:按topic分类(如"user.created.high"/"user.created.low"),不同权重观察者订阅不同 topic - 用 channel + select 实现优先消费:高权重 topic 的 channel 设更小缓冲或更高 select 优先级
- 或者直接引入轻量 workflow 库(如
temporal-goclient),把“权重”落地为状态机分支
观察者模式的干净,恰恰在于它不做决策。一旦开始纠结“谁该先跑”,就该停下来问一句:我是不是在拿锤子砸螺丝?










