info('Workerman 启动中('.config('workerman.name').')...'); // 队列消费者:复用 Laravel 队列 Worker(database / 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); } }