邮件/短信发送必须异步化,因其逻辑固定、可重试、不阻塞主流程;同步执行时SMTP或短信网关延迟(300–800ms)会拖垮接口RT,导致PHP-FPM进程占用高、QPS断崖下跌。

邮件/短信发送类任务必须异步化
用户注册后触发欢迎邮件、订单支付成功后发短信通知——这类任务逻辑固定、失败可重试、不阻塞主流程,是最典型的改造对象。同步执行时,SMTP 或短信网关响应慢(常见 300–800ms)会直接拖垮接口 RT,PHP-FPM 进程被长时间占用,QPS 断崖下跌。
实操建议:
- 用
think-queue或php-amqplib封装发送逻辑,控制器里只调用Job::dispatch()或$channel->basic_publish() - 避免在队列任务中复用请求上下文(如
$_SESSION、$_SERVER),所有必要参数必须显式序列化传入 - 短信类任务建议加
delay(如 2 秒后执行),避开支付回调与通知的瞬时并发高峰
报表生成与批量导出不能放在线程里
后台点击「导出近 30 天订单汇总」,若同步查库 + 渲染 Excel + 写文件,单次可能耗时 5–20 秒。PHP-FPM 默认 max_execution_time=30,超时后连接中断,前端白屏,用户反复点击又加剧 DB 压力。
关键判断点:
立即学习“PHP免费学习笔记(深入)”;
- 耗时 > 1.5 秒且结果可缓存(如日报 PDF、统计图表 PNG)——适合丢进队列,完成后通过 WebSocket 或轮询通知前端下载地址
- 导出需实时返回(如「导出当前页数据」)——仍可异步,但要用
job_id关联临时文件,前端轮询/api/export/status?job_id=xxx - 切忌在队列任务中用
set_time_limit(0)硬扛,应拆分 chunk 查询(如每次查 1000 条),配合$job->delete()或$job->release()控制生命周期
第三方 API 调用要警惕“雪崩依赖”
比如电商系统对接快递鸟查物流、微信模板消息推送、支付宝账单拉取——这些服务稳定性不可控,一个超时或报错就卡死整个下单链路。PHP 8.3 的 Fiber 或 Swoole\Coroutine 能缓解,但根本解法是解耦。
容易踩的坑:
- 没设超时:cURL 默认无 timeout,
file_get_contents更危险,必须显式配置stream_context_create(['http' => ['timeout' => 3]]) - 没做降级:队列消费失败 3 次后,应写入
failed_jobs表并触发告警,而不是无限重试拖垮 Redis 内存 - 误用本地缓存:别在 Worker 进程里用
apcu_store()存令牌(如微信 access_token),多进程下不同 Worker 缓存不一致,要用 Redis 全局锁 +SETNX更新
用户行为埋点与日志聚合别走主请求流
页面 PV/UV 统计、按钮点击上报、异常堆栈收集——这些数据不要求强一致性,但量大(每秒数千条)、写入频繁。若同步写 MySQL 或 Elasticsearch,极易引发慢查询或连接池打满。
推荐做法:
- 前端用 Beacon API 发送,后端接收后立刻
$redis->lpush('track_queue', $payload),不校验、不响应 - 消费者用
blpop批量拉取(如一次取 100 条),合并写入 ClickHouse 或 Kafka,降低 I/O 次数 - PHP 8.3 下注意:若用
debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS)收集堆栈,务必加DEBUG_BACKTRACE_PROVIDE_OBJECT避免丢失对象上下文,否则日志里看不到触发异常的具体 Controller 实例
$pdo = null,连接数会缓慢爬升直至 MySQL 报 Too many connections。必须在任务末尾显式释放,或改用连接池管理。



















