warn('未找到 rr 文件,将自动下载。'); // 获取操作系统是 darwin 还是 linux $os = Str::contains(PHP_OS, 'Darwin') ? 'darwin' : 'linux'; // 获取操作系统是 arm64 还是 amd64 $arch = Str::contains(php_uname('m'), ['aarch64', 'arm64']) ? 'arm64' : 'amd64'; // 下载 rr $this->info('正在下载 rr。'); $version = config('settings.roadrunner.version'); $download_link = "https://github.sakurapuare.com/roadrunner-server/roadrunner/releases/download/v$version/roadrunner-$version-$os-$arch.tar.gz"; $save_name = 'rr_download.tar.gz'; // 下载(wget) exec("wget $download_link -O $save_name"); exec("tar -zxvf $save_name"); // 删除下载的压缩包 exec("rm $save_name"); // 提取解压目录下的 rr 文件 exec("mv roadrunner-$version-$os-$arch/rr rr"); // 删除解压目录 exec("rm -rf roadrunner-$version-$os-$arch"); // 设置 rr 可执行权限 exec('chmod +x rr'); } // 关闭 Octane Artisan::call('octane:stop'); if (! config('settings.node.ip')) { $this->error('请先配置节点 IP。'); return CommandAlias::FAILURE; } // 刷新配置缓存 $this->info('正在刷新配置缓存。'); Artisan::call('config:cache'); Artisan::call('route:cache'); // redis 创建一个 hash $this->info('正在注册节点。'); ClusterSupport::registerThisNode(); $this->info('初始化完成。'); $this->info('正在启动集群协调任务。'); $pid = pcntl_fork(); if ($pid === -1) { $this->error('无法创建子进程。'); return CommandAlias::FAILURE; } elseif ($pid === 0) { // 再打开一个,负责 octane $pid = pcntl_fork(); if ($pid === -1) { $this->error('无法创建子进程。'); return CommandAlias::FAILURE; } elseif ($pid === 0) { // 子进程 $this->info('正在启动 Web。'); $command = 'php artisan octane:start --host=0.0.0.0 --rpc-port=6001 --port=8000'; $this->pipeCommand($command); } else { $this->report(); } } else { // 父进程 $this->work(); } return CommandAlias::SUCCESS; } private function pipeCommand($command): void { $descriptor_spec = [ 0 => ['pipe', 'r'], 1 => ['pipe', 'w'], 2 => ['pipe', 'w'], ]; $pipes = []; $process = proc_open($command, $descriptor_spec, $pipes); if (is_resource($process)) { while ($s = fgets($pipes[1])) { echo $s; } } // 命令结束后,关闭管道 fclose($pipes[0]); fclose($pipes[1]); fclose($pipes[2]); // 关闭进程 proc_close($process); } private function report(): void { $this->info('正在报告此系统,请保持此命令一直运行。'); Artisan::call('config:cache'); $cpu = $this->getCpuUsage(); while (1) { ClusterSupport::publish('system_usage', [ 'cpu' => $cpu, ]); sleep(1); } } private function getCpuUsage(): float { // 获取 CPU 使用率 $cpu = sys_getloadavg(); return $cpu[0]; } private function work(): void { $this->info('正在监听任务。'); ClusterSupport::publish('node.online'); ClusterSupport::listen('*', function ($event, $message) { $this->dispatchEvent($event, $message); }, false); } private function dispatchEvent($event, $message = []): void { $events = [ 'config.updated' => function () { $this->info('正在更新配置文件。'); Artisan::call('cluster:sync', [ '--force' => 'true', ]); $this->info('配置文件更新完成。'); }, 'cluster.restart.web' => function () { $this->info('正在重启 Web。'); exec('php artisan octane:reload'); ClusterSupport::publish('cluster.restarted.web'); $this->info('Web 重启完成。'); }, 'cluster.restart.all' => function () { $this->info('正在重启整个莱云。'); $this->pipeCommand('supervisorctl restart all'); ClusterSupport::publish('cluster.restarted.all'); $this->info('整个莱云 重启完成。'); }, ]; if (isset($events[$event])) { $this->warn("正在处理 $event 事件。"); $events[$event]($message); } } }