symfony异步任务核心是messenger组件,通过“发消息→存队列→后台消费”实现,需显式配置transport和路由才能异步;消息与处理器解耦,推荐redis/amqp生产环境,doctrine仅作开发备选。

Symfony 做异步任务,核心是用 Messenger 组件实现“发消息→存队列→后台消费”的流程,不是靠手动启进程或轮询。它不依赖外部服务也能跑(比如用数据库当队列),但生产环境推荐 Redis 或 AMQP。
配置传输层(让消息真正异步)
默认安装后 messenger 是同步执行的——这是新手最常踩的坑。必须显式配置 transport 并路由消息类,否则 dispatch() 会立刻调用 handler,毫无异步效果。
- 在
config/packages/messenger.yaml中定义 transport,例如用 Doctrine(需已装doctrine/doctrine-bundle):async: 'doctrine://default?queue_name=async' - 同时配置路由,把特定消息发到这个 transport:
routing:<br> 'App\Message\SendEmailNotification': async
- 首次使用 Doctrine transport 时,运行
php bin/console doctrine:migrations:generate创建messenger_messages表;表不存在,消息会静默丢失
定义消息与处理器
消息是轻量、可序列化的 PHP 类,只承载数据;处理器专注执行逻辑,两者完全解耦。
- 消息类示例(
src/Message/SendEmailNotification.php):
属性用public或标准 getter/setter,避免闭包、资源、未声明私有属性;PHP 8.2+ 的readonly类需加[Serializable](PHP 8.3+ 支持) - 处理器加
#[AsMessageHandler]属性,方法签名接收对应消息类型:public function __invoke(SendEmailNotification $message): void { ... } - 不要在消息里放业务对象或 Doctrine 实体——只传 ID 或原始数据,由 handler 再查库
发送与消费消息
控制器中通过 MessageBusInterface 发送,后台用命令行消费者持续拉取处理。
- 发送:
$bus->dispatch(new SendEmailNotification('user@example.com', 'Hello')); - 启动消费者(开发调试):
php bin/console messenger:consume async - 生产环境必须加参数防异常:
--limit=50 --time-limit=3600 --failure-limit=3 --sleep-on-empty=3
并用 Supervisor 或 systemd 管理为守护进程 - 失败消息自动进
failed队列(需配置failure_transport),可用messenger:failed:show和messenger:failed:retry排查重试
增强可靠性与可观测性
仅能跑通还不够,真实项目需要重试、延迟、日志和监控。
- 重试策略:在
messenger.yaml中为 transport 配置retry_strategy,支持指数退避 - 延迟投递:发送时加
DelayStamp,例如$bus->dispatch($msg, [new DelayStamp(60000)])(延后 60 秒) - 关键中间件:启用
SendFailedMessageForRetryMiddleware和DispatchAfterCurrentBusMiddleware,配合自定义日志中间件追踪每条消息生命周期 - 检查状态:查数据库
messenger_messages表,或用messenger:stats查各队列积压量











