2023-01-04 12:18:22 +00:00
|
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
namespace App\Console\Commands\Cluster;
|
|
|
|
|
|
2023-01-14 17:21:12 +00:00
|
|
|
|
use App\Support\ClusterSupport;
|
2023-01-04 12:18:22 +00:00
|
|
|
|
use Illuminate\Console\Command;
|
2023-01-04 15:57:36 +00:00
|
|
|
|
use Illuminate\Support\Facades\Artisan;
|
2023-01-04 18:29:35 +00:00
|
|
|
|
use Illuminate\Support\Str;
|
2023-01-04 12:18:22 +00:00
|
|
|
|
use Symfony\Component\Console\Command\Command as CommandAlias;
|
|
|
|
|
|
|
|
|
|
class Work extends Command
|
|
|
|
|
{
|
|
|
|
|
/**
|
|
|
|
|
* The name and signature of the console command.
|
|
|
|
|
*
|
|
|
|
|
* @var string
|
|
|
|
|
*/
|
2023-01-04 19:22:12 +00:00
|
|
|
|
protected $signature = 'works';
|
2023-01-04 12:18:22 +00:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* The console command description.
|
|
|
|
|
*
|
|
|
|
|
* @var string
|
|
|
|
|
*/
|
2023-01-04 19:21:18 +00:00
|
|
|
|
protected $description = '启动此应用程序。';
|
2023-01-04 12:18:22 +00:00
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Execute the console command.
|
|
|
|
|
*
|
|
|
|
|
* @return int
|
|
|
|
|
*/
|
|
|
|
|
public function handle(): int
|
|
|
|
|
{
|
2023-01-04 19:17:18 +00:00
|
|
|
|
// 检测目录下是否有 rr
|
2023-02-07 09:03:47 +00:00
|
|
|
|
if (!file_exists(base_path('rr'))) {
|
2023-01-04 19:17:18 +00:00
|
|
|
|
$this->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');
|
|
|
|
|
|
2023-01-10 13:42:27 +00:00
|
|
|
|
$download_link = "https://github.sakurapuare.com/roadrunner-server/roadrunner/releases/download/v$version/roadrunner-$version-$os-$arch.tar.gz";
|
2023-01-04 19:17:18 +00:00
|
|
|
|
|
|
|
|
|
$save_name = 'rr_download.tar.gz';
|
|
|
|
|
|
|
|
|
|
// 下载(wget)
|
2023-01-10 13:42:27 +00:00
|
|
|
|
exec("wget $download_link -O $save_name");
|
|
|
|
|
exec("tar -zxvf $save_name");
|
2023-01-04 19:17:18 +00:00
|
|
|
|
|
|
|
|
|
// 删除下载的压缩包
|
2023-01-10 13:42:27 +00:00
|
|
|
|
exec("rm $save_name");
|
2023-01-04 19:17:18 +00:00
|
|
|
|
|
|
|
|
|
// 提取解压目录下的 rr 文件
|
2023-01-10 13:42:27 +00:00
|
|
|
|
exec("mv roadrunner-$version-$os-$arch/rr rr");
|
2023-01-04 19:17:18 +00:00
|
|
|
|
|
|
|
|
|
// 删除解压目录
|
2023-01-10 13:42:27 +00:00
|
|
|
|
exec("rm -rf roadrunner-$version-$os-$arch");
|
2023-01-04 19:17:18 +00:00
|
|
|
|
|
|
|
|
|
// 设置 rr 可执行权限
|
2023-01-30 16:14:07 +00:00
|
|
|
|
exec('chmod +x rr');
|
2023-01-04 19:17:18 +00:00
|
|
|
|
}
|
|
|
|
|
|
2023-01-04 19:49:35 +00:00
|
|
|
|
// 关闭 Octane
|
|
|
|
|
Artisan::call('octane:stop');
|
|
|
|
|
|
2023-02-07 09:03:47 +00:00
|
|
|
|
if (!config('settings.node.ip')) {
|
2023-01-04 18:20:04 +00:00
|
|
|
|
$this->error('请先配置节点 IP。');
|
2023-01-30 16:14:07 +00:00
|
|
|
|
|
2023-01-04 18:20:04 +00:00
|
|
|
|
return CommandAlias::FAILURE;
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-04 18:29:35 +00:00
|
|
|
|
// 刷新配置缓存
|
|
|
|
|
$this->info('正在刷新配置缓存。');
|
|
|
|
|
Artisan::call('config:cache');
|
2023-01-18 11:20:22 +00:00
|
|
|
|
Artisan::call('route:cache');
|
2023-01-04 18:29:35 +00:00
|
|
|
|
|
|
|
|
|
// redis 创建一个 hash
|
|
|
|
|
$this->info('正在注册节点。');
|
2023-01-14 17:21:12 +00:00
|
|
|
|
ClusterSupport::registerThisNode();
|
2023-01-04 18:29:35 +00:00
|
|
|
|
|
|
|
|
|
$this->info('初始化完成。');
|
|
|
|
|
|
|
|
|
|
$this->info('正在启动集群协调任务。');
|
2023-01-04 19:17:18 +00:00
|
|
|
|
|
2023-01-04 15:57:36 +00:00
|
|
|
|
$pid = pcntl_fork();
|
|
|
|
|
if ($pid === -1) {
|
|
|
|
|
$this->error('无法创建子进程。');
|
2023-01-30 16:14:07 +00:00
|
|
|
|
|
2023-01-04 15:57:36 +00:00
|
|
|
|
return CommandAlias::FAILURE;
|
2023-02-07 09:03:47 +00:00
|
|
|
|
} else if ($pid === 0) {
|
2023-01-04 19:17:18 +00:00
|
|
|
|
// 再打开一个,负责 octane
|
|
|
|
|
$pid = pcntl_fork();
|
|
|
|
|
if ($pid === -1) {
|
|
|
|
|
$this->error('无法创建子进程。');
|
2023-01-30 16:14:07 +00:00
|
|
|
|
|
2023-01-04 19:17:18 +00:00
|
|
|
|
return CommandAlias::FAILURE;
|
2023-02-07 09:03:47 +00:00
|
|
|
|
} else if ($pid === 0) {
|
2023-01-04 19:17:18 +00:00
|
|
|
|
// 子进程
|
|
|
|
|
$this->info('正在启动 Web。');
|
|
|
|
|
|
|
|
|
|
$command = 'php artisan octane:start --host=0.0.0.0 --rpc-port=6001 --port=8000';
|
2023-01-05 05:20:09 +00:00
|
|
|
|
$this->pipeCommand($command);
|
2023-01-04 19:17:18 +00:00
|
|
|
|
} else {
|
|
|
|
|
$this->report();
|
|
|
|
|
}
|
2023-01-04 15:57:36 +00:00
|
|
|
|
} else {
|
|
|
|
|
// 父进程
|
|
|
|
|
$this->work();
|
|
|
|
|
}
|
|
|
|
|
|
2023-01-04 12:18:22 +00:00
|
|
|
|
return CommandAlias::SUCCESS;
|
|
|
|
|
}
|
2023-01-04 15:57:36 +00:00
|
|
|
|
|
2023-01-05 14:12:47 +00:00
|
|
|
|
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) {
|
2023-01-14 17:21:12 +00:00
|
|
|
|
ClusterSupport::publish('system_usage', [
|
2023-01-05 14:12:47 +00:00
|
|
|
|
'cpu' => $cpu,
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
sleep(1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private function getCpuUsage(): float
|
|
|
|
|
{
|
|
|
|
|
// 获取 CPU 使用率
|
|
|
|
|
$cpu = sys_getloadavg();
|
2023-01-30 16:14:07 +00:00
|
|
|
|
|
2023-01-05 14:12:47 +00:00
|
|
|
|
return $cpu[0];
|
|
|
|
|
}
|
2023-01-04 15:57:36 +00:00
|
|
|
|
|
|
|
|
|
private function work(): void
|
|
|
|
|
{
|
|
|
|
|
$this->info('正在监听任务。');
|
|
|
|
|
|
2023-01-14 17:21:12 +00:00
|
|
|
|
ClusterSupport::publish('node.online');
|
|
|
|
|
ClusterSupport::listen('*', function ($event, $message) {
|
2023-01-04 15:57:36 +00:00
|
|
|
|
$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('配置文件更新完成。');
|
2023-01-04 19:38:46 +00:00
|
|
|
|
},
|
|
|
|
|
'cluster.restart.web' => function () {
|
|
|
|
|
$this->info('正在重启 Web。');
|
|
|
|
|
|
2023-01-05 05:24:49 +00:00
|
|
|
|
exec('php artisan octane:reload');
|
2023-01-05 05:20:09 +00:00
|
|
|
|
|
2023-01-14 17:21:12 +00:00
|
|
|
|
ClusterSupport::publish('cluster.restarted.web');
|
2023-01-04 19:43:59 +00:00
|
|
|
|
|
2023-01-04 19:38:46 +00:00
|
|
|
|
$this->info('Web 重启完成。');
|
|
|
|
|
},
|
|
|
|
|
'cluster.restart.all' => function () {
|
|
|
|
|
$this->info('正在重启整个莱云。');
|
|
|
|
|
|
2023-01-05 05:20:09 +00:00
|
|
|
|
$this->pipeCommand('supervisorctl restart all');
|
2023-01-04 19:38:46 +00:00
|
|
|
|
|
2023-01-14 17:21:12 +00:00
|
|
|
|
ClusterSupport::publish('cluster.restarted.all');
|
2023-01-04 19:43:59 +00:00
|
|
|
|
|
2023-01-04 19:38:46 +00:00
|
|
|
|
$this->info('整个莱云 重启完成。');
|
|
|
|
|
},
|
2023-01-04 15:57:36 +00:00
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
if (isset($events[$event])) {
|
2023-01-10 13:42:27 +00:00
|
|
|
|
$this->warn("正在处理 $event 事件。");
|
2023-01-04 15:57:36 +00:00
|
|
|
|
$events[$event]($message);
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-01-04 12:18:22 +00:00
|
|
|
|
}
|