laravel queue:work 不会自动发钉钉/微信告警,因其无进程崩溃监听机制,仅执行任务与记日志;需通过 supervisor eventlistener 或消费者内 try/catch + register_shutdown_function 主动上报,且告警必须直连 webhook。

消费者进程崩溃后,Laravel 本身不会自动触发告警 —— 必须由外部监控或消费者自身主动上报。
为什么 Laravel 的 queue:work 不会自动发钉钉/微信告警
Laravel 的 queue:work 命令是长生命周期 PHP 进程,它只负责拉取消息、执行 handle()、记录日志。一旦因内存溢出、未捕获异常、SIGKILL 或 RabbitMQ 连接中断而退出,进程就静默终止,supervisor 或 systemd 可能重启它,但 Laravel 框架层完全不感知“挂了”,更不会调用任何通知逻辑。
- 没有内置钩子监听 worker crash
-
failed_jobs表只记录任务执行失败(即handle()抛异常),不记录进程退出 - 即使配置了
retry_after和max_attempts,也只对单条消息有效,无法覆盖进程级故障
推荐做法:用 supervisor 的 eventlistener 发送钉钉告警
这是最轻量、最可靠的方式 —— 把告警责任从 Laravel 移交给进程管理器。Supervisor 支持 eventlistener 监听 PROCESS_STATE_EXITED 事件,只要消费者进程非正常退出(exit code ≠ 0),就能触发脚本。
- 在
supervisord.conf中启用eventlistener,例如:
[eventlistener:rq-consumer-alert] command=python3 /opt/scripts/supervisor_alert.py events=PROCESS_STATE_EXITED redirect_stderr=true stdout_logfile=/var/log/supervisor/alert.log
-
/opt/scripts/supervisor_alert.py脚本需解析环境变量SUPERVISOR_PROCESS_NAME和SUPERVISOR_GROUP_NAME,判断是否为你的 RabbitMQ 消费者(如queue:rabbitmq:tweet) - 使用 requests 调用钉钉 Webhook,发送包含
process_name、from_state、pid、exitstatus和当前时间的卡片 - 务必加
try/except和日志,避免 alert 脚本自身失败导致循环告警
备选方案:消费者内部 try/catch + register_shutdown_function
如果无法控制 supervisor 配置(如云托管环境),可在消费者启动入口手动埋点。注意这不是兜底方案,而是补充手段。
- 在
App\Jobs\ConsumeRabbitMQJob的handle()外层包一层try/catch,捕获所有Throwable,记录错误并调用Http::post()推送钉钉 - 同时在
artisan queue:work启动时注册register_shutdown_function,检查error_get_last()是否为 fatal error,并触发告警 - ⚠️ 关键限制:
register_shutdown_function在 SIGTERM/SIGKILL 下不执行;OOM killer 杀掉进程时也无效 - 必须禁用
retry_after超长设置(如 1 小时),否则一次卡死会掩盖真实崩溃点
钉钉 Webhook 调用必须绕过 Laravel 队列本身
告警消息绝不能走 RabbitMQ 发送 —— 消费者挂了,队列通道大概率已不可用。必须用同步 HTTP 请求直连钉钉。
- 不要用
Notification::route('dingtalk', $webhook)->notify(new ConsumerDownAlert()),这仍依赖 Laravel 通知通道和队列驱动 - 直接用
Http::timeout(5)->post($webhook, [...]),并检查响应状态码是否为 200 - Webhook 地址应从
config('services.dingtalk.webhook')读取,而非硬编码;密钥(如存在)需通过config('services.dingtalk.secret')获取并参与签名计算 - 若使用签名认证,注意钉钉要求 timestamp + secret 拼接后做 HmacSHA256,再 base64 编码 —— 时间戳误差超过 1 小时即被拒绝
真正难的不是发一次告警,而是区分“临时抖动”和“持续不可用”。比如 supervisor 5 秒内重启 3 次,该合并成一条“频繁崩溃”告警,而不是刷屏 3 条。这个聚合逻辑得放在 alert 脚本里,而不是 Laravel 应用中。











