CI4队列任务中必须通过服务容器获取实例,禁止直接new;任务类须继承BaseJob且构造函数不可带参;handle()中用service()或container->get()动态获取依赖,避免长时连接超时。

队列任务里不能直接 new Service(),必须走服务容器
CI4 的队列任务(比如用 CodeIgniter\Commands\Queue\WorkCommand 启动的 worker)运行在 CLI 环境下,但和普通命令行脚本不同:它不经过完整的 HTTP 生命周期,却仍依赖服务容器提供依赖注入。直接 new UserService() 会绕过容器,导致配置未加载、数据库连接未初始化、日志器缺失等问题。
正确做法是通过 $this->container 或静态 service() 辅助函数获取实例:
-
$userService = service('userservice')—— 推荐,简洁且自动处理单例 -
$userService = $this->container->get('UserService')—— 更底层,适合需要控制解析逻辑时 - 避免用
new \App\Services\UserService(),除非你手动补全所有依赖(几乎不可维护)
自定义队列任务类必须继承 BaseJob,且构造函数不能带参数
CI4 官方队列机制(spark queue:work)要求任务类必须实现 CodeIgniter\Queue\Jobs\BaseJob,它的反序列化过程不支持带参构造函数——否则会报 Uncaught Error: Cannot instantiate abstract class 或参数缺失错误。
所以依赖只能在 handle() 方法里动态获取:
namespace App\Jobs;
use CodeIgniter\Queue\Jobs\BaseJob;
class SendWelcomeEmail extends BaseJob
{
public function handle()
{
// ✅ 正确:运行时从容器取
$mailer = service('email');
$userModel = model('UserModel');
// ❌ 错误:构造函数里传 service,反序列化失败
// public function __construct(Email $mailer) { ... }
}
}
服务容器在队列里可能缺配置或环境变量
CLI 下的队列 worker 默认读取 .env,但若你用 systemd 或 supervisor 管理进程,容易忽略工作目录和环境变量传递。常见现象是 service('database') 返回 null,或 service('cache') 报连接 refused。
排查要点:
- 确认
spark queue:work是在项目根目录下执行的(否则.env找不到) - 检查
.env中CI_ENVIRONMENT = development是否生效,生产环境可能禁用某些服务 - 若用 Redis 驱动队列,确保
redis.host和redis.port在app/Config/Cache.php和app/Config/Queue.php中一致 - worker 进程启动后不会自动重载
.env变更,需重启spark queue:work
数据库连接在长时队列任务里会超时
CI4 的默认数据库连接在 CLI 模式下不会自动重连,一个长时间运行的队列 worker(比如处理大批量导入)可能遇到 MySQL server has gone away。
解决方案不是手动 $db->reconnect(),而是利用容器的“作用域”特性:
- 每次
handle()中调用model('SomethingModel')都会获得新实例,其内部 DB 连接是 fresh 的 - 避免在类属性里缓存模型或 DB 实例:
private $model;→ 会在整个 worker 生命周期复用,风险高 - 如需复用逻辑,封装成无状态函数或服务类,靠
service()每次取新实例
.env 变更热更新,改完配置必须 kill 掉老进程再 restart。


















