codeigniter(ci3/ci4)原生不提供redis/rabbitmq队列服务,无laravel风格的queue:work或dispatch()接口,其任务调度依赖cli+cron定时执行,非长运行消费者。

CodeIgniter 本身没有内置队列系统
直接说结论:CodeIgniter(包括 CI3 和 CI4)原生不提供 Redis/RabbitMQ 驱动的队列服务,也没有 queue:work 或 dispatch() 这类 Laravel 风格的队列接口。它的任务调度模型是「CLI + Cron」组合,本质是定时触发单次执行的脚本,不是长运行的队列消费者。
用 exec() 启动后台 CLI 进程要注意信号和僵尸进程
常见做法是让 Web 请求触发 exec() 调用 CLI 命令,例如:
$command = "php " . FCPATH . "index.php cron daily_backup > /dev/null 2>&1 &"; exec($command);
但这里容易踩坑:
-
&后台运行后,父进程(Web PHP)不会等待子进程结束,如果子进程崩溃或卡住,会变成僵尸进程 -
exec()默认受max_execution_time限制,超时后可能中断命令启动 - 没做 PID 管理,无法判断任务是否已在运行,重复触发会导致资源争抢
-
/dev/null丢弃输出,出错时完全无日志可查
CI3 中实现轻量级「伪队列」的关键是状态标记 + CLI 控制器
不引入第三方组件也能支撑中低频后台任务,核心逻辑是:
- 用数据库表(如
jobs)记录任务状态:pending/running/failed/done - 每个 CLI 控制器方法(如
cron::process_queue())启动时先查一条pending任务,更新为running,再执行;完成后更新状态并写日志 - Cron 每分钟调用一次该方法,相当于一个极简轮询消费者
- 避免并发:加数据库行锁(
SELECT ... FOR UPDATE)或用WHERE status = 'pending' AND locked_at IS NULL LIMIT 1+ 更新locked_at
示例片段(CI3 模型中):
$job = $this->db->where('status', 'pending')
->where('locked_at IS NULL')
->limit(1)
->get('jobs')
->row();
if ($job) {
$this->db->where('id', $job->id)->update('jobs', ['locked_at' => date('Y-m-d H:i:s')]);
// 执行实际任务逻辑
$result = $this->do_something($job->payload);
$this->db->where('id', $job->id)->update('jobs', [
'status' => $result ? 'done' : 'failed',
'finished_at' => date('Y-m-d H:i:s')
]);
}
CI4 的 spark 命令 + CodeIgniter\Commands\BaseCommand 更适合封装长期任务
CI4 提供了更规范的 CLI 入口机制,推荐用 spark 命令替代手写 exec():
- 新建命令类继承
CodeIgniter\Commands\BaseCommand,重写run() - 在
run()中处理参数、调用服务、捕获异常并退出码控制(exit(EXIT_SUCCESS)) - 通过
spark mycommand:process --delay=5方式调用,比拼接字符串安全得多 - 若需守护进程,可用
supervisord管理spark mycommand:worker,避免自己 fork 和信号处理
关键点:CI4 的 CLI 不再依赖 is_cli_request() 手动判断,spark 自动确保只在 CLI 下运行,且自动加载环境配置和依赖注入容器。
真正复杂的队列场景(高吞吐、延迟任务、失败重试、优先级)仍建议外接 Redis + predis/predis 或使用 Laravel Horizon 替代方案;CI 的优势在于轻量可控,别强行把它变成长驻队列框架。











