2022-01-09 03:49:02 +08:00
|
|
|
<?php
|
|
|
|
|
|
2022-01-10 11:39:55 +08:00
|
|
|
namespace Kiri\Server;
|
2022-01-09 03:49:02 +08:00
|
|
|
|
|
|
|
|
use Kiri\Abstracts\Component;
|
2022-06-16 17:49:06 +08:00
|
|
|
use Kiri\Server\Abstracts\TraitServer;
|
2022-01-09 03:49:02 +08:00
|
|
|
use Swoole\Process;
|
2023-12-12 10:56:42 +08:00
|
|
|
use function config;
|
2022-01-09 03:49:02 +08:00
|
|
|
|
|
|
|
|
class State extends Component
|
|
|
|
|
{
|
|
|
|
|
|
2023-12-12 15:35:34 +08:00
|
|
|
use TraitServer;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public array $servers = [];
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return void
|
|
|
|
|
*/
|
|
|
|
|
public function init(): void
|
|
|
|
|
{
|
2025-12-18 15:39:40 +08:00
|
|
|
$this->servers = config('servers.server.ports');
|
2023-12-12 15:35:34 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @return bool
|
|
|
|
|
* @throws
|
|
|
|
|
*/
|
|
|
|
|
public function isRunner(): bool
|
|
|
|
|
{
|
2026-07-07 22:59:28 +08:00
|
|
|
if ($this->getPidFilePid() !== null || $this->getNamedServerPids() !== []) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2023-12-12 15:35:34 +08:00
|
|
|
$ports = $this->sortService($this->servers);
|
|
|
|
|
foreach ($ports as $config) {
|
2026-07-07 22:59:28 +08:00
|
|
|
if (checkPortIsAlready($config->port)) {
|
2023-12-12 15:35:34 +08:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param $port
|
|
|
|
|
* @throws
|
|
|
|
|
*/
|
|
|
|
|
public function exit($port): void
|
|
|
|
|
{
|
2026-07-07 22:59:28 +08:00
|
|
|
$pids = $this->collectStopPids((int)$port);
|
|
|
|
|
if ($pids === []) {
|
2023-12-12 15:35:34 +08:00
|
|
|
return;
|
|
|
|
|
}
|
2026-07-07 22:59:28 +08:00
|
|
|
|
|
|
|
|
$this->terminatePids($pids, SIGTERM, 30);
|
|
|
|
|
|
|
|
|
|
$remaining = array_values(array_filter($pids, [$this, 'isProcessAlive']));
|
|
|
|
|
if ($remaining !== []) {
|
|
|
|
|
$this->terminatePids($remaining, SIGKILL, 10);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!checkPortIsAlready((int)$port)) {
|
|
|
|
|
@unlink(storage('.swoole.pid'));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private function collectStopPids(int $port): array
|
|
|
|
|
{
|
|
|
|
|
$pids = [];
|
|
|
|
|
|
|
|
|
|
$pidFilePid = $this->getPidFilePid();
|
|
|
|
|
if ($pidFilePid !== null) {
|
|
|
|
|
$pids[] = $pidFilePid;
|
|
|
|
|
$pids = array_merge($pids, $this->getDescendantPids($pidFilePid));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$pids = array_merge($pids, $this->getNamedServerPids());
|
|
|
|
|
|
|
|
|
|
$portPid = checkPortIsAlready($port);
|
|
|
|
|
if ($portPid !== false) {
|
|
|
|
|
$pids[] = (int)$portPid;
|
|
|
|
|
$pids = array_merge($pids, $this->getDescendantPids((int)$portPid));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$currentPid = getmypid();
|
|
|
|
|
$pids = array_values(array_unique(array_filter(array_map('intval', $pids), function (int $pid) use ($currentPid) {
|
|
|
|
|
return $pid > 1 && $pid !== $currentPid;
|
|
|
|
|
})));
|
|
|
|
|
rsort($pids);
|
|
|
|
|
|
|
|
|
|
return $pids;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private function terminatePids(array $pids, int $signal, int $rounds): void
|
|
|
|
|
{
|
|
|
|
|
for ($i = 0; $i < $rounds; $i++) {
|
|
|
|
|
$alive = false;
|
|
|
|
|
foreach ($pids as $pid) {
|
|
|
|
|
if (!$this->isProcessAlive($pid)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
$alive = true;
|
|
|
|
|
@Process::kill($pid, $signal);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!$alive) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
usleep(100000);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private function getPidFilePid(): ?int
|
|
|
|
|
{
|
|
|
|
|
$pidFile = storage('.swoole.pid');
|
|
|
|
|
if (!is_file($pidFile)) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$pid = (int)trim((string)file_get_contents($pidFile));
|
|
|
|
|
if ($pid <= 1 || !$this->isProcessAlive($pid)) {
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $pid;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private function getDescendantPids(int $parentPid): array
|
|
|
|
|
{
|
|
|
|
|
$processes = $this->listProcesses();
|
|
|
|
|
$children = [];
|
|
|
|
|
foreach ($processes as $process) {
|
|
|
|
|
$children[(int)$process['ppid']][] = (int)$process['pid'];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$result = [];
|
|
|
|
|
$stack = [$parentPid];
|
|
|
|
|
while ($stack !== []) {
|
|
|
|
|
$pid = array_pop($stack);
|
|
|
|
|
foreach ($children[$pid] ?? [] as $childPid) {
|
|
|
|
|
$result[] = $childPid;
|
|
|
|
|
$stack[] = $childPid;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private function getNamedServerPids(): array
|
|
|
|
|
{
|
|
|
|
|
$siteId = '[' . config('site.id', 'system-service') . ']';
|
|
|
|
|
$pids = [];
|
|
|
|
|
foreach ($this->listProcesses() as $process) {
|
|
|
|
|
$command = $process['command'];
|
|
|
|
|
if (!str_contains($command, $siteId)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (!preg_match('/\\]\\..+\\[[0-9]+\\]/', $command)) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
$pids[] = (int)$process['pid'];
|
2023-12-12 15:35:34 +08:00
|
|
|
}
|
2026-07-07 22:59:28 +08:00
|
|
|
|
|
|
|
|
return $pids;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private function listProcesses(): array
|
|
|
|
|
{
|
|
|
|
|
$lines = [];
|
|
|
|
|
exec('ps -eo pid=,ppid=,args=', $lines);
|
|
|
|
|
|
|
|
|
|
$processes = [];
|
|
|
|
|
foreach ($lines as $line) {
|
|
|
|
|
$line = trim($line);
|
|
|
|
|
if ($line === '') {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$parts = preg_split('/\s+/', $line, 3);
|
|
|
|
|
if (count($parts) < 3) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$processes[] = [
|
|
|
|
|
'pid' => (int)$parts[0],
|
|
|
|
|
'ppid' => (int)$parts[1],
|
|
|
|
|
'command' => $parts[2],
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $processes;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private function isProcessAlive(int $pid): bool
|
|
|
|
|
{
|
|
|
|
|
return $pid > 1 && @Process::kill($pid, 0);
|
2023-12-12 15:35:34 +08:00
|
|
|
}
|
2022-01-09 03:49:02 +08:00
|
|
|
|
|
|
|
|
}
|