迁移后 cron 和队列必须作为两套独立机制分别处理:只复制 crontab 文件不足以恢复 cron,还要核对运行用户、PATH 和时区;队列 worker 要重装进程守护配置并执行重启,否则旧代码会一直常驻内存。权威的验证方式是建立一个每分钟写入时间戳的探针任务,观察 5 分钟,再核对队列积压量。
迁移后 cron 为什么没跑起来?
结论:cron 失效集中出现在三个点上——运行用户不一致、PATH 环境变量缺失、时区不一致,与 crontab 文件内容通常关系不大。
第一是用户维度。crontab -l 默认只列当前用户的任务,root 的 crontab 和 www 用户、php-fpm 用户的 crontab 是彼此独立的三份。迁移脚本经常只备份了 root 那一份,业务用户下的任务直接丢失。导出时要逐个用户来:
crontab -l -u www > cron_www.bak
crontab -l -u root > cron_root.bak
crontab cron_www.bak # 导入
systemctl status cron # Debian/Ubuntu;CentOS 是 crond
第二是 PATH。cron 执行时的 PATH 通常只有 /usr/bin:/bin,php、node、composer 常常装在 /usr/local/bin,于是任务静默失败,日志里一行报错都没有。解决办法是把命令写成绝对路径,比如 /usr/bin/php /www/app/artisan schedule:run,或在脚本首行 source /etc/profile。
第三是时区。宿主机 /etc/timezone、PHP 的 date.timezone、MySQL 的 time_zone 三者不一致时,「每日 0 点重置进度」这类任务会在错误时间点触发。迁移后统一核对 timedatectl 与 SELECT NOW(); 的输出。
队列迁移后为什么要单独验证?
结论:队列并不由 cron 驱动,它靠常驻进程工作,迁移后必须重新拉起 worker 并确认连接配置,否则任务只会堆积、不会执行。
以 Laravel 为例,worker 一般由 supervisor 托管,迁移后要重装配置:
supervisorctl reread
supervisorctl update
supervisorctl status
php artisan queue:restart # 每次部署必跑,否则老代码常驻内存
php artisan queue:work --queue=default,mail --tries=3 --timeout=90
queue:restart 这一步最容易漏。worker 是常驻进程,改了代码不重启,队列里跑的还是旧逻辑。同时要检查三件事:队列连接(Redis 还是 database)、storage/logs 目录权限、失败任务表是否随数据库一起迁移。查看积压和失败任务:
redis-cli -n 0 llen queues:default
php artisan queue:failed
php artisan queue:retry all
积压量持续上涨而 worker 状态正常,八成是连接配置指向了旧实例的 Redis,或 QUEUE_CONNECTION 还写的是 sync。
怎么验证迁移后的定时任务真的生效?
结论:不要只看「进程在跑」,要用业务副作用反推验证。
最直接的做法是建一个每分钟执行一次的探针任务,把时间戳追加到文件,观察 5 分钟,有 5 行以上新记录才算 cron 通了。队列同理,投递一条测试任务,确认 30 秒内执行完成。
更关键的是业务验证。以本站为例,签到奖励发放(连续 7 天额外 +10 金币、连续 30 天额外 +30 金币)、积分任务每日 0 点重置进度、用户等级自动升降后的站内通知、版块关注与作者关注的新帖推送(单帖推送上限 100 人,超限靠队列分批),全部依赖 cron 或队列。迁移后如果这些没验证,症状是「签到显示成功但金币没到账」「等级升了没收到通知」,用户端很难联想到是迁移遗留问题。
迁移 checklist 怎么列?
结论:按「导出—核对—拉起—验证—观察 24 小时」五步走,不要跳过最后一步。
- 多用户导出 crontab,导入后用
crontab -l -u 用户名 逐个核对;
- 核对 PATH、PHP/Node 版本、时区三处一致性;
- 恢复 supervisor 配置,
queue:restart 后确认 worker 状态为 RUNNING;
- 用探针任务 + 测试队列任务验证链路;
- 观察 24 小时,完整覆盖一次每日任务周期(签到、积分重置、通知推送这些节点都在凌晨)。
服务器迁移的坑,往往要到第二天凌晨才暴露。cron 和队列这两块,宁可多花 20 分钟做探针验证,也别等用户来报「签到不加金币」。上线后第一件事就是盯日志和积压量,看到 failed_jobs 表为空、队列长度归零,才算真正迁移完成。