在hyperf 3.1中,异步队列任务执行完毕后触发事件需在job的handle()末尾手动dispatch事件或用事件监听器包装流程,并配置redisdriver实现跨进程事件同步,同时校验重试机制、异常抛出及监听器注册。

要在Hyperf 3.1中让异步队列任务执行完成后自动触发事件,比如订单处理完立刻广播状态变更、邮件发送成功后更新用户积分,必须绕过“先发事件再投任务”的常见误用——事件若在主协程中同步 dispatch,而任务尚未真正执行,监听方拿到的就是空数据或旧快照。
让队列任务执行完毕后主动触发事件
方法一:在 Job 的 handle() 方法末尾手动 dispatch 事件
打开你的 Job 类(如 App\Job\OrderProcessJob),在逻辑完成且无异常抛出后,调用 $this->container->get(EventDispatcherInterface::class)->dispatch(...)。
这一步不能用 Event::dispatch() 静态调用——它会跳过容器作用域,导致依赖注入失败,尤其当事件监听器需要注入 Service 或 DB 连接时直接报错。
方法二:用事件监听器包装 Job 执行流程(推荐)
创建一个事件类 OrderCreated,再写一个监听器 ProcessOrderWithQueueListener,在 listen() 方法里调用 $this->queue->push(new OrderProcessJob($order));接着为 OrderProcessed 事件单独注册监听器,负责后续通知、缓存刷新等动作。
【关键前提】 必须确保 OrderProcessJob 的 handle() 方法内部最后一步是 Event::dispatch(new OrderProcessed($this->orderId)),否则下游监听器收不到信号。
避免事件重复触发与丢失的三步校验
第一步:确认 async-queue 配置中 retry_seconds 和 max_attempts 已显式设置
默认配置可能不启用重试,一旦任务因网络抖动或 Redis 瞬断失败,事件就永久丢失。在 config/autoload/async_queue.php 中强制补全:
'retry_seconds' => 5,'max_attempts' => 3,
第二步:Job 类中禁止在 try/catch 内吞掉所有异常
如果 catch 块里只写 logger()->error(...) 却不 re-throw,async-queue 组件会认为任务已成功完成,不再重试,事件也不会再次 dispatch。正确做法是 catch 后记录日志 + throw $e。
第三步:检查事件监听器是否被标记为 #[AsEventListener] 或已在 config/autoload/listeners.php 中注册
Hyperf 3.1 默认不自动扫描监听器,未注册的类即使存在 listen() 方法也不会被调用。漏配会导致事件“发出去了却没人收”。
跨进程事件同步:当消费者不在主 Swoole 进程时
Hyperf 的 async-queue 消费者是独立的 php bin/hyperf.php async-queue:consume 进程,它和 HTTP 请求进程不共享内存或事件循环。此时 EventDispatcherInterface 的默认实现仅限当前进程生效。
必须切换为支持进程间通信的事件驱动——改用 hyperf/event + hyperf/redis 组合,配置 RedisDriver 作为事件广播后端。
在 config/autoload/event.php 中替换 driver:
'driver' => Hyperf\Event\Driver\RedisDriver::class,'redis' => ['pool' => 'default'],
这一步不做,你在 consume 进程里 dispatch 的事件,HTTP 进程里的监听器永远收不到。











