Compare commits

...

4 Commits

Author SHA1 Message Date
as2252258 0adfe14a1f eee 2026-07-07 22:59:28 +08:00
as2252258 3ca39d2205 eee 2026-07-03 14:25:26 +08:00
as2252258 908c8719d3 eee 2026-06-24 20:17:54 +08:00
as2252258 69b845d924 eee 2026-06-12 23:57:18 +08:00
4 changed files with 180 additions and 24 deletions
+1 -1
View File
@@ -74,7 +74,7 @@ class OnServerWorker extends Kiri\Server\Abstracts\Server
try { try {
$this->dispatch->dispatch(new OnBeforeWorkerStart($server, $workerId)); $this->dispatch->dispatch(new OnBeforeWorkerStart($server, $workerId));
if ($workerId < $server->setting['worker_num']) { if ($workerId < $server->setting['worker_num']) {
CoordinatorManager::utility(Coordinator::WORKER_START)->waite(); CoordinatorManager::utility(Coordinator::WORKER_START)->wait();
$this->dispatch->dispatch(new OnWorkerStart($server, $workerId)); $this->dispatch->dispatch(new OnWorkerStart($server, $workerId));
} else { } else {
+6 -2
View File
@@ -133,9 +133,13 @@ class ServerCommand extends Command
protected function start(InputInterface $input): int protected function start(InputInterface $input): int
{ {
$this->asyncServer->addProcess(config('process', [])); $this->asyncServer->addProcess(config('process', []));
if (\config('servers.reload.hot', false) === true) { $hotReload = \config('servers.reload.hot', false) === true;
if ($hotReload) {
$this->asyncServer->addProcess([FileWatcher::class]); $this->asyncServer->addProcess([FileWatcher::class]);
} else { }
if (!$hotReload) {
// 非热更模式可在 Master 进程预扫描,Worker 通过 fork 继承扫描结果。
// 热更模式必须让新 Worker 自己加载业务类,避免继承旧类定义后无法重新声明。
di(Router::class)->scan_build_route(); di(Router::class)->scan_build_route();
} }
$this->asyncServer->initCoreServers(config('servers.server', []), (int)$input->getOption('daemon')); $this->asyncServer->initCoreServers(config('servers.server', []), (int)$input->getOption('daemon'));
+157 -5
View File
@@ -31,9 +31,13 @@ class State extends Component
*/ */
public function isRunner(): bool public function isRunner(): bool
{ {
if ($this->getPidFilePid() !== null || $this->getNamedServerPids() !== []) {
return true;
}
$ports = $this->sortService($this->servers); $ports = $this->sortService($this->servers);
foreach ($ports as $config) { foreach ($ports as $config) {
if (checkPortIsAlready($config['port'])) { if (checkPortIsAlready($config->port)) {
return true; return true;
} }
} }
@@ -47,13 +51,161 @@ class State extends Component
*/ */
public function exit($port): void public function exit($port): void
{ {
if (!($pid = checkPortIsAlready($port))) { $pids = $this->collectStopPids((int)$port);
if ($pids === []) {
return; return;
} }
while (checkPortIsAlready($port)) {
Process::kill($pid, 0) && Process::kill($pid, SIGTERM); $this->terminatePids($pids, SIGTERM, 30);
usleep(300);
$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'];
}
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);
}
} }
+1 -1
View File
@@ -9,7 +9,7 @@
} }
], ],
"require": { "require": {
"php": ">=8.4", "php": ">=8.5",
"ext-json": "*", "ext-json": "*",
"composer-runtime-api": "^2.0", "composer-runtime-api": "^2.0",
"psr/http-server-middleware": "^1.0", "psr/http-server-middleware": "^1.0",