Files
laralog/app/Console/Commands/WorkermanServe.php
T
ak 43dc8f9856
CI / test (push) Failing after 7s
CI / deploy (push) Skipped
docs: 安装/导入文档 + 部署文档刷新 + workerman 进程文件收归 storage
- docs/install.md:全新安装指南(环境要求/.env/验收清单/常见问题)
- docs/import.md:Sablog 导入全解(命令参数表/数据映射/附件短代码/301 兼容/S3 同步/幂等重跑/检查清单)
- docs/deploy.md:补生产迁移顺序、上线前检查清单、调度器实际内容
- Workerman 进程文件(pid/status/log)统一写入 storage/framework 与 storage/logs,不再出现在项目根目录;.gitignore 排除并清理根目录残留
- README 文档导航补 install/import
2026-08-13 05:00:22 +08:00

94 lines
3.6 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Contracts\Debug\ExceptionHandler;
use Illuminate\Queue\Worker as QueueWorker;
use Illuminate\Queue\WorkerOptions;
use Illuminate\Support\Facades\Log;
use Workerman\Connection\TcpConnection;
use Workerman\Timer;
use Workerman\Worker;
class WorkermanServe extends Command
{
protected $signature = 'workerman:serve {action=start : start/stop/restart/reload}';
protected $description = '启动 Workerman 常驻服务:队列消费者(database/redis+ WebSocket 进度推送';
public function handle(): int
{
// 进程文件统一放 storage/framework/,不污染项目根目录
Worker::$pidFile = storage_path('framework/workerman.pid');
Worker::$statusFile = storage_path('framework/workerman.status');
Worker::$logFile = storage_path('logs/workerman.log');
$this->info('Workerman 启动中('.config('workerman.name').'...');
// 队列消费者:复用 Laravel 队列 Workerdatabase / redis 统一支持),
// 常驻进程内框架只启动一次,省去每个任务的启动开销
$queueWorker = new Worker();
$queueWorker->name = 'laralog-queue';
$queueWorker->count = (int) config('workerman.queue_workers', 2);
$queueWorker->onWorkerStart = function ($worker) {
$this->info("队列消费者 {$worker->id} 启动(连接:".config('workerman.queue_connection').',队列:'.config('workerman.queues').'');
Timer::add(1, function () {
try {
$this->consumeNextJob();
} catch (\Throwable $e) {
Log::error('Workerman 任务执行失败', ['error' => $e->getMessage()]);
}
});
};
// WebSocket:向后台推送任务进度
$ws = new Worker('websocket://'.config('workerman.websocket_host', '0.0.0.0').':'.config('workerman.websocket_port', 8787));
$ws->name = 'laralog-ws';
$ws->onConnect = function (TcpConnection $connection) {
\App\Blog\Support\WorkermanBroadcaster::add($connection);
$connection->send(json_encode(['event' => 'connected']));
};
$ws->onClose = fn (TcpConnection $connection) => \App\Blog\Support\WorkermanBroadcaster::remove($connection);
Worker::runAll();
return self::SUCCESS;
}
/**
* 消费一个任务。使用 Laravel 队列 Worker
* - 支持 database / redis 等所有驱动(redis 的 reserved/重试/超时语义由驱动处理)
* - sleep=0 避免阻塞 Workerman event loop(由 1s Timer 驱动轮询)
* - 任务 handle() 的依赖(如 LlmClient)由容器自动注入
*/
private function consumeNextJob(): void
{
$connection = (string) config('workerman.queue_connection', config('queue.default'));
// Worker 内部按逗号分隔解析多队列(如 "default,ai"
$queues = (string) config('workerman.queues', 'default');
$queues = $queues === '' ? 'default' : $queues;
$worker = new QueueWorker(
app('queue'),
app('events'),
app(ExceptionHandler::class),
fn () => app()->isDownForMaintenance(),
);
$options = new WorkerOptions(
name: 'laralog',
backoff: 0,
memory: 128,
timeout: (int) config('workerman.timeout', 60),
sleep: 0,
maxTries: (int) config('workerman.max_tries', 3),
);
$worker->runNextJob($connection, $queues, $options);
}
}