symfony messenger 的信封(envelope)是专为消息元数据管理设计的核心机制,包含消息体和可扩展的邮票(stamp),用于传递上下文信息而非标记邮件。

Symfony 的信封(Envelope)不是用来“标记邮件”的工具,而是 Messenger 组件中专为**消息元数据管理**设计的核心机制。它不处理电子邮件头或视觉标签,而是给一条消息附加上下文信息,比如来源、优先级、追踪 ID 或业务标识,供后续处理器、中间件或监听器使用。
信封本质:消息的“外包装”
每条消息在被发送进 Messenger 总线前,都会被自动包装成一个 Envelope 对象。这个对象包含两部分:
-
消息体(Message):你的实际业务类,比如
SendWelcomeEmail或ProcessOrder -
邮票(Stamp):一组可添加/移除的元数据对象,例如
DelayStamp、DispatchAfterCurrentBusStamp、ReceivedStamp等
你可以把信封理解成一封实体信——信纸内容不变(消息体),但信封上可以贴不同功能的邮票(控制行为)、盖时间戳(追踪)、写备注(业务上下文)。
怎么往信封里加自定义信息?用 Stamp
直接修改消息类不推荐,也不符合解耦原则。正确做法是创建自定义 Stamp 类,并在发送时把它塞进信封:
- 新建一个 PHP 类,实现
StampInterface(空接口,仅作类型标记) - 构造时传入你需要携带的数据,比如
postId、triggeredBy、correlationId - 发送消息时用
Envelope::withStamps()包装
示例:
$message = new SendSmsNotification($phone, $text);
$envelope = new Envelope(
$message,
[
new PostIdStamp($post->getId()),
new PriorityStamp(9),
]
);
$this->bus->dispatch($envelope);
如何在监听器或处理器里读取这些信息?
在中间件、消息处理器或事件监听器中,可通过 $envelope->getStamps(PostIdStamp::class) 获取对应邮票:
- 在自定义中间件中判断是否跳过日志记录
- 在
WorkerMessageHandledEvent监听器中提取PostIdStamp并写入数据库日志表 - 在路由中间件中根据
EnvironmentStamp把消息发往不同传输器(如 dev 环境走 log,prod 走 amqp)
注意:getStamps() 返回数组,即使只加了一个,也要用 [0] 取值或遍历处理。
常见误区提醒
- 别和 Mailer 的 “X-Mailgun-Tag” 混淆:那是 SMTP 邮件头字段,属于 Symfony/Mailer 层,和 Messenger 的 Envelope 无关
- Envelope 不影响消息序列化内容:邮票不会被序列化进消息体本身,只存在于运行时内存中(除非你手动持久化)
-
不能靠 Stamp 改变消息逻辑流向:它只是“说明”,真正做路由或延迟,得配合对应中间件(如
RoutingMiddleware、DispatchAfterCurrentBusMiddleware)











