Files
laralog/app/Console/Commands/WorkermanServe.php
T

91 lines
3.4 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
{
Worker::$pidFile = storage_path('framework/workerman.pid');
$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);
}
}