CodeIgniter默认无队列系统是因其“按需加载、贴近原生PHP”的设计哲学,将队列视为可选基础设施而非框架必需;需手动集成数据库表、Redis或第三方服务实现异步任务。

要在CodeIgniter中实现后台任务异步执行,比如邮件发送、日志归档或图片压缩,必须借助队列机制——但CodeIgniter原生不提供队列驱动,需手动集成第三方组件或自建轻量方案,否则所有耗时操作都会阻塞HTTP响应。
为什么CodeIgniter默认没有队列系统
CodeIgniter 4的核心设计哲学是“按需加载”和“贴近原生PHP”,【不内置队列服务是刻意为之】。它把消息队列视为可选基础设施,而非Web框架必需能力。Laravel用Horizon管理Redis队列、Symfony用Messenger封装多种传输器,而CI选择把这部分交由开发者自行选型:你可以用数据库表模拟队列,也能接入Beanstalkd、RabbitMQ或Amazon SQS。
这带来直接后果:项目初期零配置开箱即用,但一旦需要高并发任务调度,就必须补上外部依赖和调度守护进程(如supervisord),否则任务只会堆积在内存里或直接失败。
用数据库表实现最简队列(推荐中小型项目)
适合日均任务量低于5000条、无严格时效要求、服务器资源有限的场景。无需安装额外服务,兼容共享主机。
第一步:在数据库中新建jobs表,字段至少包含id、job_name、payload(TEXT)、attempts(TINYINT)、reserved_at(DATETIME NULL)、available_at(DATETIME)、created_at(DATETIME)。
第二步:创建app/Commands/QueueWorker.php命令类,继承CodeIgniter\CLI\BaseCommand,编写run()方法——从jobs表查available_at ≤ NOW()且reserved_at为NULL的记录,更新reserved_at为当前时间,执行payload反序列化后的任务逻辑,成功则DELETE,失败则increment attempts并设置available_at = NOW() + 60秒重试延迟。
第三步:通过CLI运行php spark queue:work → 它会持续轮询jobs表,每处理完一条就sleep 1秒避免CPU空转。注意【必须用nohup或supervisord守护该进程,否则SSH断开即终止】。
对接Redis实现高性能队列(适合中大型项目)
方法一:使用predis/predis Composer包(CI4支持Composer自动加载)
执行composer require predis/predis → 在app/Config/Database.php同级新建app/Config/Redis.php,配置host/port/database → 在app/Libraries/RedisQueue.php中封装push()、pop()、ack()方法,用LPUSH/RPOP+DEL模拟FIFO队列。
方法二:复用CI4已内置的Cache驱动扩展
修改app/Config/Cache.php,将handler设为'redis' → 编写app/Services/QueueService.php,利用cache()->save('queue:jobs', $jobData, 3600)写入,再用cache()->get('queue:jobs')读取并删除 → 这种方式牺牲原子性换取部署简易性,适用于非关键任务。
方法三:直接调用Redis原生命令(需启用phpredis扩展)
在控制器中用$redis = new Redis(); $redis->connect('127.0.0.1', 6379); $redis->lPush('jobs', json_encode($payload)); → 消费端用$redis->rPop('jobs')获取任务 → 【务必检查rPop返回值是否为FALSE,否则可能误删空队列导致逻辑中断】。
避免队列积压的三个硬性约束
① 所有队列任务类必须放在app/Commands/或app/Services/下,且不能依赖$_GET/$_POST等请求上下文——因为执行环境是CLI,无HTTP输入。
② payload字段严禁存resource类型(如数据库连接句柄、文件指针),序列化后会失效,只允许数组、字符串、数字、对象(该对象需实现__sleep/__wakeup)。
③ 每个任务执行时间必须控制在max_execution_time内(默认30秒),超时会导致reserved_at滞留,后续轮询时被重复获取。


















