Command Dispatcher(命令分发器)和 Command Bus(命令总线)是架构模式,属于应用层架构设计的范畴,不在 GoF 23 种设计模式之列。
注意:“Command Dispatcher” 是开发者社区的口语化称呼,没有权威模式书籍正式定义。对应的正式模式名是 EIP(企业集成模式)中的 Content-Based Router(见下文)。
两者不是同一个模式,Command Bus 是在 Content-Based Router 基础上增加了中间件管道的演化版本,核心差异在于中间件管道。
| Command Dispatcher | Command Bus | |
|---|---|---|
| 核心机制 | ID → Handler 直接路由 | ID → 中间件链 → Handler |
| 中间件支持 | 无 | 有(日志、验证、事务等) |
| 复杂度 | 简单 | 较复杂 |
| 典型场景 | IoT 协议解析、游戏指令 | CQRS、DDD 应用层 |
Command Dispatcher
核心结构:
收到消息 → 解析 ID → registry.get(id) → handler.Handle(data)
服务初始化时将每种指令的处理类注册到一个 Map。运行时收到消息后,解析出消息类型 ID,从注册表查找对应处理器并直接调用。
// Handler 接口,每种指令实现一个
type Handler interface {
Handle(data []byte) error
}
// Dispatcher 维护 id -> handler 的注册表
type Dispatcher struct {
handlers map[uint8]Handler
}
func NewDispatcher() *Dispatcher {
return &Dispatcher{handlers: make(map[uint8]Handler)}
}
func (d *Dispatcher) Register(id uint8, h Handler) {
d.handlers[id] = h
}
func (d *Dispatcher) Dispatch(id uint8, data []byte) error {
h, ok := d.handlers[id]
if !ok {
return fmt.Errorf("unknown command id: 0x%02X", id)
}
return h.Handle(data)
}
// --- 具体指令处理类 ---
type LoginHandler struct{}
func (h *LoginHandler) Handle(data []byte) error {
fmt.Printf("处理登录指令: %x\n", data)
return nil
}
type LocationReportHandler struct{}
func (h *LocationReportHandler) Handle(data []byte) error {
fmt.Printf("处理位置上报: %x\n", data)
return nil
}
// --- 初始化注册 ---
func main() {
d := NewDispatcher()
d.Register(0x01, &LoginHandler{})
d.Register(0x02, &LocationReportHandler{})
// 收到一条原始消息,解析出 id=0x02
d.Dispatch(0x02, []byte{0x01, 0x02, 0x03})
}
Command Bus
在 Dispatcher 的基础上增加了中间件管道,每条命令在到达 Handler 之前会依次经过所有中间件。
发送命令 → middleware1 → middleware2 → ... → handler.Handle(cmd)
中间件可以在命令执行前后插入横切逻辑(日志、参数校验、事务、限流等),且对 Handler 完全透明。
// Command 是命令的抽象接口
type Command interface{}
// CommandHandler 处理具体命令
type CommandHandler interface {
Handle(cmd Command) error
}
// Middleware 是中间件函数类型
// next 是调用链中的下一个处理函数
type Middleware func(cmd Command, next func(Command) error) error
// CommandBus 持有 handler 注册表和中间件链
type CommandBus struct {
handlers map[string]CommandHandler
middlewares []Middleware
}
func NewCommandBus() *CommandBus {
return &CommandBus{handlers: make(map[string]CommandHandler)}
}
func (b *CommandBus) Use(m Middleware) {
b.middlewares = append(b.middlewares, m)
}
func (b *CommandBus) Register(name string, h CommandHandler) {
b.handlers[name] = h
}
func (b *CommandBus) Dispatch(name string, cmd Command) error {
h, ok := b.handlers[name]
if !ok {
return fmt.Errorf("no handler for: %s", name)
}
// 构建中间件调用链
final := func(c Command) error { return h.Handle(c) }
for i := len(b.middlewares) - 1; i >= 0; i-- {
m := b.middlewares[i]
next := final
final = func(c Command) error { return m(c, next) }
}
return final(cmd)
}
// --- 中间件示例 ---
func LoggingMiddleware(cmd Command, next func(Command) error) error {
fmt.Printf("[LOG] 开始处理: %T\n", cmd)
err := next(cmd)
fmt.Printf("[LOG] 处理完成: %T, err=%v\n", cmd, err)
return err
}
func ValidationMiddleware(cmd Command, next func(Command) error) error {
// 校验逻辑...
fmt.Printf("[VALID] 校验通过: %T\n", cmd)
return next(cmd)
}
// --- 具体命令和处理器 ---
type CreateOrderCommand struct {
UserID int
Product string
}
type CreateOrderHandler struct{}
func (h *CreateOrderHandler) Handle(cmd Command) error {
c := cmd.(CreateOrderCommand)
fmt.Printf("创建订单: user=%d, product=%s\n", c.UserID, c.Product)
return nil
}
// --- 初始化 ---
func main() {
bus := NewCommandBus()
bus.Use(LoggingMiddleware)
bus.Use(ValidationMiddleware)
bus.Register("CreateOrder", &CreateOrderHandler{})
bus.Dispatch("CreateOrder", CreateOrderCommand{UserID: 42, Product: "keyboard"})
}
执行顺序:
LoggingMiddleware (前) → ValidationMiddleware (前) → CreateOrderHandler → ValidationMiddleware (后) → LoggingMiddleware (后)
Content-Based Router(正式模式名)
Content-Based Router 是《Enterprise Integration Patterns》(EIP)中定义的消息路由模式,由 Gregor Hohpe & Bobby Woolf 于 2003 年出版的同名书籍中正式命名。
官网:enterpriseintegrationpatterns.com/ContentBasedRouter
问题场景:同一个入口会收到多种不同类型的消息,每种消息需要由不同的处理组件处理,如何在不耦合各处理组件的情况下将消息分发到正确目标?
解决方案:插入一个路由组件,检查消息内容(通常是某个字段或头信息),根据内容决定将消息转发到哪个处理通道/处理器。
incoming message
↓
[Content-Based Router]
├── type=login → LoginHandler
├── type=location → LocationHandler
└── type=alarm → AlarmHandler
核心特征:
- 路由器不修改消息内容,只决定消息的去向
- 路由规则集中在路由器中维护,各处理器对路由逻辑完全无感知
- 新增消息类型时,只需修改路由器,其他组件不受影响
在 Apache Camel、Spring Integration、MassTransit 等主流集成框架中都有原生支持。
与 GoF Command Pattern 的关系
GoF 的 Command Pattern 意图是将请求封装为对象,支持撤销、队列、日志等操作。Command Dispatcher / Command Bus 是对其的延伸和演化:
| GoF Command Pattern | Command Dispatcher | Command Bus | |
|---|---|---|---|
| 意图 | 将请求封装为可操作对象 | 按 ID 路由到处理器 | 路由 + 中间件管道 |
| 关注点 | 撤销、队列、事务 | 动态分发、解耦 | 横切关注点、CQRS |
常见应用场景
- CQRS 架构中的 Command Bus
- 消息驱动架构中的 Message Dispatcher
- IoT / 车联网协议解析服务:每种指令 ID 对应一个处理类,注册到分发器后统一路由
- 企业集成模式(EIP)中的 Message Router
与策略模式的区别
两者代码结构相似(公共接口 + 多实现 + 运行时选择),但意图不同:
| 策略模式 | Command Dispatcher | |
|---|---|---|
| 核心意图 | 对同一件事选择不同算法 | 对不同的事路由到不同处理器 |
| 典型例子 | 排序可换快排/归并/堆排 | 指令 0x01 → 登录处理器,0x02 → 上报处理器 |