laravel模型在workerman中无法直接使用,因为workerman是纯cli常驻进程,不触发laravel http生命周期,导致服务容器未引导、门面未绑定、数据库连接未初始化;必须在onworkerstart中手动加载app.php并bootstrap应用,才能安全调用模型和db。

能共享 Laravel 的模型和数据库配置,但必须手动触发 Laravel 应用启动流程,不能直接在 Worker 实例中调用 DB::table() 或 User::find() —— 否则会报 Target class [db] does not exist 或连接未初始化。
为什么 Laravel 模型在 Workerman 里直接用不了
Workerman 是纯 CLI 进程,不走 Laravel 的 HTTP 生命周期(比如 Kernel::handle()),app 容器没被完整引导,服务提供者没注册,DB、Eloquent、Config 等门面都不可用。即使 use Illuminate\Support\Facades\DB; 成功,调用时也会因底层连接未初始化而失败。
常见错误现象:
-
Class 'DB' not found(没引入门面或自动加载失效) -
Target class [db] does not exist(门面存在,但服务容器里没绑定db) -
PDOException: SQLSTATE[HY000] [2002] Connection refused(配置读取失败,连到了默认 localhost:3306,而非 .env 里的真实配置)
如何安全复用 Laravel 的数据库与模型
核心思路:在 onWorkerStart 回调中,手动加载 Laravel 引导文件,生成并绑定完整的 $app 实例到全局或闭包作用域。
实操建议:
- 在命令类的
handle()中,不要直接 new Worker 后立刻 runAll;先确保bootstrap/app.php被 require,并用它创建一个可用的$app - 把
$app存进Worker::$app静态属性,或通过use ($app)传入回调,避免每次 onMessage 都重复加载 - 务必在
onWorkerStart里做初始化,而不是onConnect或onMessage—— 后者每请求一次就执行一次,开销大且易出错 - 数据库连接默认是短生命周期的,Workerman 长驻进程下需手动重连或启用持久连接;推荐在
onWorkerStart初始化连接,在onWorkerStop关闭
简短示例(关键片段):
$worker = new Worker('websocket://0.0.0.0:2346');
$worker->onWorkerStart = function ($worker) {
// 手动启动 Laravel 应用
$app = require __DIR__.'/../../bootstrap/app.php';
$app->make(Illuminate\Contracts\Console\Kernel::class)->bootstrap();
// 将 app 绑定到 worker 实例,供后续回调使用
$worker->app = $app;
};
$worker->onMessage = function ($connection, $data) use ($worker) {
// 此时可安全使用模型
$user = $worker->app->make('App\Models\User')::find(1);
$connection->send($user ? $user->name : 'not found');
};
配置项与环境变量怎么生效
Laravel 的 .env 在 Workerman 进程中不会自动加载,因为 Dotenv 只在 bootstrap/app.php 开头被调用一次,而该文件通常只在 HTTP 请求入口(public/index.php)中加载。
所以你必须确保:
- 在
onWorkerStart之前,Dotenv::createImmutable(base_path())已执行,或直接 requirebootstrap/app.php(它内部已处理) - 不要依赖
$_ENV或getenv()直接读取,应统一走config('database.default')或Config::get() - 如果用了
php artisan config:cache,记得部署后重新生成缓存,否则 Workerman 读不到最新配置
容易被忽略的连接泄漏与性能点
Workerman 进程常驻内存,Laravel 默认的数据库连接在每次请求后会关闭,但在 Workerman 里不会自动释放 —— 多次 onMessage 调用同一连接可能触发 MySQL 的 Too many connections。
更稳妥的做法:
- 在
onWorkerStart中调用DB::reconnect()或DB::purge()清空旧连接 - 改用
DB::connection()->getPdo()做健康检查,失败时主动 reconnect - 避免在
onMessage中新建 Eloquent 模型实例后不做任何操作(如只 new 不 save),这会悄悄占用连接 - 若业务允许,用原生查询
DB::select()替代模型,减少 ORM 层开销
真正麻烦的不是“能不能用”,而是“什么时候初始化、在哪里释放、出错了怎么感知”。这些细节不显眼,但上线后扛不住并发时,第一个崩的往往是数据库连接池。











