直接用 session 存进度会失效,因 cli 模式不共享 web 的 session 生命周期,且无持久存储;应统一用 redis 等外部缓存,配合原子操作、节流更新、状态兜底和过期策略保障可靠性。

为什么直接用 session 存进度在 ThinkPHP 异步任务里会失效
因为 CLI 模式(如 php think task:run)不共享 Web 请求的 session 生命周期,session_start() 在命令行下默认不生效,即使手动开启也无持久存储——你写进 $_SESSION 的进度,在 Web 端根本读不到。
真正能跨进程通信的只有外部存储。实操建议如下:
- 优先用
cache驱动(如redis或file),调用Cache::set('task_123_progress', 65),Web 端用相同键名读取 - 避免用
config('cache.default')动态切驱动,而应在config/cache.php中明确配置default为redis,否则 CLI 和 Web 可能走不同后端 - 若必须用数据库,不要手写
Db::table('task_status'),改用模型 + 全局作用域,确保主键和更新时间字段统一
think-queue 里怎么安全更新任务进度而不丢数据
原生 think-queue 不提供进度回调钩子,强行在 handle() 里高频写缓存会导致 Redis 压力大、Web 端轮询拉取时看到跳变值。关键在于控制更新节奏和原子性:
- 用
Cache::inc('task_123_progress', 1)替代set,避免并发覆盖(但需初始化为 0) - 每处理 5 条记录才更新一次进度,而不是每条都写:用计数器
$i++,if ($i % 5 === 0) { Cache::set(...); } - 任务结束前务必补全到 100%,且加锁防止重复执行:用
Cache::store('redis')->lock('task_123_done', 10)->get()包裹最终写入
前端轮询 /api/task/progress?task_id=123 返回什么结构最稳妥
别返回裸数字或简单数组。Web 端 JS 容易因字段缺失崩溃,后端也要预留扩展空间。实际响应应固定包含三要素:
- 必须有
status字段:'running'/'success'/'failed',不能只靠progress推断状态 -
progress值限定在 0–100 整数,Cache::get('task_123_progress', 0)后强制min(max($val, 0), 100) - 带上
message字段,失败时填错误原因(如"文件不存在: /data/abc.csv"),成功时可为空字符串
示例响应:
{"status":"running","progress":72,"message":""}
为什么用 Redis 而不用 File 缓存存进度
File 缓存在高并发下会因文件锁阻塞,且 CLI 和 Web 进程可能因 umask 或 open_basedir 限制无法读写同一目录;Redis 天然支持原子操作、过期自动清理、多进程无锁读写。
但要注意两个硬伤:
- 如果 Redis 连接超时,
Cache::get()默认返回null,前端会收到progress: null—— 必须在控制器里做兜底:$progress = Cache::get($key, 0) ?: 0; - Redis 内存不是无限的,对长期运行的任务,必须设过期时间:
Cache::set($key, $val, 3600),别依赖永久缓存
进度不是日志,不需要保留超过任务生命周期,超时自动消失比手动清理更可靠。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











