Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0adfe14a1f | |||
| 3ca39d2205 | |||
| 908c8719d3 | |||
| 69b845d924 |
@@ -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
@@ -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'));
|
||||||
|
|||||||
@@ -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
@@ -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",
|
||||||
|
|||||||
Reference in New Issue
Block a user