diff --git a/src/ZM/Command/Daemon/DaemonCommand.php b/src/ZM/Command/Daemon/DaemonCommand.php
index 4b18750e..8bf9ec8e 100644
--- a/src/ZM/Command/Daemon/DaemonCommand.php
+++ b/src/ZM/Command/Daemon/DaemonCommand.php
@@ -7,22 +7,17 @@ namespace ZM\Command\Daemon;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
-use ZM\Utils\DataProvider;
+use ZM\Framework;
abstract class DaemonCommand extends Command
{
protected $daemon_file = null;
protected function execute(InputInterface $input, OutputInterface $output): int {
- $pid_path = DataProvider::getWorkingDir() . "/.daemon_pid";
- if (!file_exists($pid_path)) {
- $output->writeln("没有检测到正在运行的守护进程或框架进程!");
- die();
- }
- $file = json_decode(file_get_contents($pid_path), true);
- if ($file === null || posix_getsid(intval($file["pid"])) === false) {
+ $file = Framework::getProcessState(ZM_PROCESS_MASTER);
+ if ($file === false || posix_getsid(intval($file["pid"])) === false) {
$output->writeln("未检测到正在运行的守护进程或框架进程!");
- unlink($pid_path);
+ Framework::removeProcessState(ZM_PROCESS_MASTER);
die();
}
$this->daemon_file = $file;
diff --git a/src/ZM/Command/Daemon/DaemonStopCommand.php b/src/ZM/Command/Daemon/DaemonStopCommand.php
index 959def97..8e95aa56 100644
--- a/src/ZM/Command/Daemon/DaemonStopCommand.php
+++ b/src/ZM/Command/Daemon/DaemonStopCommand.php
@@ -6,7 +6,7 @@ namespace ZM\Command\Daemon;
use Swoole\Process;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
-use ZM\Utils\DataProvider;
+use ZM\Framework;
class DaemonStopCommand extends DaemonCommand
{
@@ -20,7 +20,7 @@ class DaemonStopCommand extends DaemonCommand
parent::execute($input, $output);
Process::kill(intval($this->daemon_file["pid"]), SIGTERM);
$i = 10;
- while (file_exists(DataProvider::getWorkingDir() . "/.daemon_pid") && $i > 0) {
+ while (Framework::getProcessState(ZM_PROCESS_MASTER) !== false && $i > 0) {
sleep(1);
--$i;
}
diff --git a/src/ZM/Event/SwooleEvent/OnManagerStop.php b/src/ZM/Event/SwooleEvent/OnManagerStop.php
index e79b3bdc..e6f7926e 100644
--- a/src/ZM/Event/SwooleEvent/OnManagerStop.php
+++ b/src/ZM/Event/SwooleEvent/OnManagerStop.php
@@ -8,7 +8,8 @@ use Swoole\Process;
use ZM\Annotation\Swoole\SwooleHandler;
use ZM\Console\Console;
use ZM\Event\SwooleEvent;
-use ZM\Utils\DataProvider;
+use ZM\Framework;
+use ZM\Utils\Manager\ProcessManager;
/**
* Class OnManagerStop
@@ -17,15 +18,12 @@ use ZM\Utils\DataProvider;
*/
class OnManagerStop implements SwooleEvent
{
- public function onCall() {
- if (OnManagerStart::$process !== null) {
- if (Process::kill(OnManagerStart::$process->pid, 0)) {
- Process::kill(OnManagerStart::$process->pid, SIGTERM);
- }
+ public function onCall()
+ {
+ foreach (ProcessManager::$user_process as $v) {
+ if (posix_getsid($v->pid) !== false) Process::kill($v->pid, SIGTERM);
}
Console::verbose("进程 Manager 已停止!");
- if (file_exists(DataProvider::getWorkingDir()."/.daemon_pid")) {
- unlink(DataProvider::getWorkingDir()."/.daemon_pid");
- }
+ Framework::removeProcessState(ZM_PROCESS_MANAGER);
}
}
\ No newline at end of file
diff --git a/src/ZM/Event/SwooleEvent/OnShutdown.php b/src/ZM/Event/SwooleEvent/OnShutdown.php
index b997618b..367390a2 100644
--- a/src/ZM/Event/SwooleEvent/OnShutdown.php
+++ b/src/ZM/Event/SwooleEvent/OnShutdown.php
@@ -8,6 +8,7 @@ use Swoole\Server;
use ZM\Annotation\Swoole\SwooleHandler;
use ZM\Console\Console;
use ZM\Event\SwooleEvent;
+use ZM\Framework;
use ZM\Utils\DataProvider;
/**
@@ -19,9 +20,9 @@ class OnShutdown implements SwooleEvent
{
public function onCall(Server $server) {
Console::verbose("正在关闭 Master 进程,pid=" . posix_getpid());
- $pid_path = DataProvider::getWorkingDir() . "/.daemon_pid";
- if (file_exists($pid_path)) {
- unlink($pid_path);
+ Framework::removeProcessState(ZM_PROCESS_MASTER);
+ if (DataProvider::scanDirFiles(_zm_pid_dir()) == []) {
+ rmdir(_zm_pid_dir());
}
}
}
\ No newline at end of file
diff --git a/src/ZM/Event/SwooleEvent/OnStart.php b/src/ZM/Event/SwooleEvent/OnStart.php
index 400a8383..b4f8c881 100644
--- a/src/ZM/Event/SwooleEvent/OnStart.php
+++ b/src/ZM/Event/SwooleEvent/OnStart.php
@@ -25,13 +25,9 @@ class OnStart implements SwooleEvent
if (!Framework::$argv["disable-safe-exit"]) {
SignalListener::signalMaster($server);
}
- $daemon_data = json_encode([
- "pid" => $server->master_pid,
- "stdout" => ZMConfig::get("global")["swoole"]["log_file"],
- "daemon" => (bool)Framework::$argv["daemon"]
- ], 128 | 256);
- file_put_contents(DataProvider::getWorkingDir() . "/.daemon_pid", $daemon_data);
+ Framework::saveProcessState(ZM_PROCESS_MASTER, $server->master_pid, [
+ 'stdout' => ZMConfig::get("global")["swoole"]["log_file"],
+ 'daemon' => (bool)Framework::$argv["daemon"]
+ ]);
}
-
-
}
\ No newline at end of file
diff --git a/src/ZM/Event/SwooleEvent/OnWorkerStart.php b/src/ZM/Event/SwooleEvent/OnWorkerStart.php
index 9cbd57bb..7fe16caf 100644
--- a/src/ZM/Event/SwooleEvent/OnWorkerStart.php
+++ b/src/ZM/Event/SwooleEvent/OnWorkerStart.php
@@ -43,13 +43,15 @@ use ZM\Utils\SignalListener;
*/
class OnWorkerStart implements SwooleEvent
{
- public function onCall(Server $server, $worker_id) {
+ public function onCall(Server $server, $worker_id)
+ {
Console::debug("Calling onWorkerStart event(1)");
if (!Framework::$argv["disable-safe-exit"]) {
SignalListener::signalWorker($server, $worker_id);
}
unset(Context::$context[Coroutine::getCid()]);
if ($server->taskworker === false) {
+ Framework::saveProcessState(ZM_PROCESS_WORKER, $server->worker_pid, ['worker_id' => $worker_id]);
zm_atomic("_#worker_" . $worker_id)->set($server->worker_pid);
if (LightCacheInside::get("wait_api", "wait_api") !== null) {
LightCacheInside::unset("wait_api", "wait_api");
@@ -105,10 +107,13 @@ class OnWorkerStart implements SwooleEvent
Console::error(zm_internal_errcode("E00030") . "PHP Error: " . $e->getMessage() . " in " . $e->getFile() . " on line " . $e->getLine());
Console::error("Maybe it caused by your own code if in your own Module directory.");
Console::log($e->getTraceAsString(), 'gray');
- Process::kill($server->master_pid, SIGTERM);
+ if (!Framework::$argv["watch"]) { // 在热更新模式下不能退出
+ Process::kill($server->master_pid, SIGTERM);
+ }
}
} else {
// 这里是TaskWorker初始化的内容部分
+ Framework::saveProcessState(ZM_PROCESS_TASKWORKER, $server->worker_pid, ['worker_id' => $worker_id]);
try {
Framework::$server = $server;
$this->loadAnnotations();
@@ -131,13 +136,15 @@ class OnWorkerStart implements SwooleEvent
* @throws ReflectionException
* @throws Exception
*/
- private function loadAnnotations() {
+ private function loadAnnotations()
+ {
if (Framework::$instant_mode) goto skip;
//加载各个模块的注解类,以及反射
Console::debug("Mapping annotations");
$parser = new AnnotationParser();
$composer = json_decode(file_get_contents(DataProvider::getSourceRootDir() . "/composer.json"), true);
- foreach ($composer["autoload"]["psr-4"] as $k => $v) {
+ $merge = array_merge($composer["autoload"]["psr-4"] ?? [], $composer["autoload-dev"]["psr-4"] ?? []);
+ foreach ($merge as $k => $v) {
if (is_dir(DataProvider::getSourceRootDir() . "/" . $v)) {
if (in_array(trim($k, "\\") . "\\", $composer["extra"]["exclude_annotate"] ?? [])) continue;
if (trim($k, "\\") == "ZM") continue;
@@ -150,12 +157,12 @@ class OnWorkerStart implements SwooleEvent
$plugin_enable_hotload = ZMConfig::get("global", "module_loader")["enable_hotload"] ?? false;
if ($plugin_enable_hotload) {
$list = ModuleManager::getPackedModules();
- foreach($list as $k => $v) {
- if (\server()->worker_id === 0) Console::info("Loading packed module: ".$k);
+ foreach ($list as $k => $v) {
+ if (\server()->worker_id === 0) Console::info("Loading packed module: " . $k);
require_once $v["phar-path"];
- $func = "loader".$v["generated-id"];
+ $func = "loader" . $v["generated-id"];
$func();
- $parser->addRegisterPath("phar://".$v["phar-path"]."/".$v["module-root-path"], $v["namespace"]);
+ $parser->addRegisterPath("phar://" . $v["phar-path"] . "/" . $v["module-root-path"], $v["namespace"]);
}
}
@@ -189,7 +196,8 @@ class OnWorkerStart implements SwooleEvent
}
}
- private function initMySQLPool() {
+ private function initMySQLPool()
+ {
if (SqlPoolStorage::$sql_pool !== null) {
SqlPoolStorage::$sql_pool->close();
SqlPoolStorage::$sql_pool = null;
diff --git a/src/ZM/Event/SwooleEvent/OnWorkerStop.php b/src/ZM/Event/SwooleEvent/OnWorkerStop.php
index 0cb0f179..dba5c1bf 100644
--- a/src/ZM/Event/SwooleEvent/OnWorkerStop.php
+++ b/src/ZM/Event/SwooleEvent/OnWorkerStop.php
@@ -9,7 +9,9 @@ use ZM\Annotation\Swoole\SwooleHandler;
use ZM\Config\ZMConfig;
use ZM\Console\Console;
use ZM\Event\SwooleEvent;
+use ZM\Framework;
use ZM\Store\LightCache;
+use ZM\Utils\DataProvider;
/**
* Class OnWorkerStop
@@ -22,6 +24,7 @@ class OnWorkerStop implements SwooleEvent
if ($worker_id == (ZMConfig::get("worker_cache")["worker"] ?? 0)) {
LightCache::savePersistence();
}
- Console::verbose(($server->taskworker ? "Task" : "") . "Worker #$worker_id 已停止: ".$server->getWorkerStatus($worker_id));
+ Console::verbose(($server->taskworker ? "Task" : "") . "Worker #$worker_id 已停止 (Worker 状态码: ".$server->getWorkerStatus($worker_id).")");
+ Framework::removeProcessState($server->taskworker ? ZM_PROCESS_TASKWORKER : ZM_PROCESS_WORKER, $worker_id);
}
}
\ No newline at end of file
diff --git a/src/ZM/Framework.php b/src/ZM/Framework.php
index 2b05ec70..1e67a644 100644
--- a/src/ZM/Framework.php
+++ b/src/ZM/Framework.php
@@ -12,6 +12,7 @@ use Throwable;
use ZM\Config\ZMConfig;
use ZM\ConnectionManager\ManagerGM;
use ZM\Console\TermColor;
+use ZM\Exception\ZMKnownException;
use ZM\Store\LightCache;
use ZM\Store\LightCacheInside;
use ZM\Store\Lock\SpinLock;
@@ -78,7 +79,8 @@ class Framework
public static $instant_mode = false;
- public function __construct($args = [], $instant_mode = false) {
+ public function __construct($args = [], $instant_mode = false)
+ {
$tty_width = $this->getTtyWidth();
self::$instant_mode = $instant_mode;
self::$argv = $args;
@@ -129,9 +131,9 @@ class Framework
$this->server_set["log_level"] = SWOOLE_LOG_DEBUG;
$add_port = ZMConfig::get("global", "remote_terminal")["status"] ?? false;
- if ($instant_mode) {
- $this->loadServerEvents();
- }
+ //if ($instant_mode) {
+ $this->loadServerEvents();
+ //}
$this->parseCliArgs(self::$argv, $add_port);
@@ -332,7 +334,8 @@ class Framework
}
}
- private static function printMotd($tty_width) {
+ private static function printMotd($tty_width)
+ {
if (file_exists(DataProvider::getSourceRootDir() . "/config/motd.txt")) {
$motd = file_get_contents(DataProvider::getSourceRootDir() . "/config/motd.txt");
} else {
@@ -344,7 +347,118 @@ class Framework
echo $motd;
}
- public function start() {
+ /**
+ * 将各进程的pid写入文件,以备后续崩溃及僵尸进程处理使用
+ * @param int $type
+ * @param int|string $pid
+ * @param array $data
+ * @internal
+ */
+ public static function saveProcessState(int $type, $pid, array $data = [])
+ {
+ switch ($type) {
+ case ZM_PROCESS_MASTER:
+ $file = _zm_pid_dir() . '/master.json';
+ $json = [
+ 'pid' => intval($pid),
+ 'stdout' => $data['stdout'],
+ 'daemon' => $data['daemon']
+ ];
+ file_put_contents($file, json_encode($json, JSON_UNESCAPED_UNICODE));
+ return;
+ case ZM_PROCESS_MANAGER:
+ $file = _zm_pid_dir() . '/manager.pid';
+ file_put_contents($file, strval($pid));
+ return;
+ case ZM_PROCESS_WORKER:
+ $file = _zm_pid_dir() . '/worker.' . $data['worker_id'] . '.pid';
+ file_put_contents($file, strval($pid));
+ return;
+ case ZM_PROCESS_USER:
+ $file = _zm_pid_dir() . '/user.' . $data['process_name'] . '.pid';
+ file_put_contents($file, strval($pid));
+ return;
+ case ZM_PROCESS_TASKWORKER:
+ $file = _zm_pid_dir() . '/taskworker.' . $data['worker_id'] . '.pid';
+ file_put_contents($file, strval($pid));
+ return;
+ }
+ }
+
+ /**
+ * 用于框架内部获取多进程运行状态的函数
+ * @param int $type
+ * @param null $id_or_name
+ * @return false|int|mixed
+ * @throws ZMKnownException
+ * @internal
+ */
+ public static function getProcessState(int $type, $id_or_name = null)
+ {
+ $file = _zm_pid_dir();
+ switch ($type) {
+ case ZM_PROCESS_MASTER:
+ if (!file_exists($file . '/master.json')) return false;
+ $json = json_decode(file_get_contents($file . '/master.json'), true);
+ if ($json !== null) return $json;
+ else return false;
+ case ZM_PROCESS_MANAGER:
+ if (!file_exists($file . '/manager.pid')) return false;
+ return intval(file_get_contents($file . '/manager.pid'));
+ case ZM_PROCESS_WORKER:
+ if (!is_int($id_or_name)) throw new ZMKnownException('E99999', 'worker_id必须为整数');
+ if (!file_exists($file . '/worker.' . $id_or_name . '.pid')) return false;
+ return intval(file_get_contents($file . '/worker.' . $id_or_name . '.pid'));
+ case ZM_PROCESS_USER:
+ if (!is_string($id_or_name)) throw new ZMKnownException('E99999', 'process_name必须为字符串');
+ if (!file_exists($file . '/user.' . $id_or_name . '.pid')) return false;
+ return intval(file_get_contents($file . '/user.' . $id_or_name . '.pid'));
+ case ZM_PROCESS_TASKWORKER:
+ if (!is_int($id_or_name)) throw new ZMKnownException('E99999', 'worker_id必须为整数');
+ if (!file_exists($file . '/taskworker.' . $id_or_name . '.pid')) return false;
+ return intval(file_get_contents($file . '/taskworker.' . $id_or_name . '.pid'));
+ default:
+ return false;
+ }
+ }
+
+ /**
+ * @param int $type
+ * @param null $id_or_name
+ * @throws ZMKnownException
+ * @internal
+ */
+ public static function removeProcessState(int $type, $id_or_name = null)
+ {
+ switch ($type) {
+ case ZM_PROCESS_MASTER:
+ $file = _zm_pid_dir() . '/master.json';
+ if (file_exists($file)) unlink($file);
+ return;
+ case ZM_PROCESS_MANAGER:
+ $file = _zm_pid_dir() . '/manager.pid';
+ if (file_exists($file)) unlink($file);
+ return;
+ case ZM_PROCESS_WORKER:
+ if (!is_int($id_or_name)) throw new ZMKnownException('E99999', 'worker_id必须为整数');
+ $file = _zm_pid_dir() . '/worker.' . $id_or_name . '.pid';
+ if (file_exists($file)) unlink($file);
+ return;
+ case ZM_PROCESS_USER:
+ if (!is_string($id_or_name)) throw new ZMKnownException('E99999', 'process_name必须为字符串');
+ $file = _zm_pid_dir() . '/user.' . $id_or_name . '.pid';
+ if (file_exists($file)) unlink($file);
+ return;
+ case ZM_PROCESS_TASKWORKER:
+ if (!is_int($id_or_name)) throw new ZMKnownException('E99999', 'worker_id必须为整数');
+ $file = _zm_pid_dir() . '/taskworker.' . $id_or_name . '.pid';
+ if (file_exists($file)) unlink($file);
+ return;
+ }
+ }
+
+ public function start()
+ {
try {
self::$loaded_files = get_included_files();
self::$server->start();
@@ -358,7 +472,8 @@ class Framework
/**
* @noinspection PhpIncludeInspection
*/
- private function loadServerEvents() {
+ private function loadServerEvents()
+ {
if (Phar::running() !== "") {
ob_start();
include_once DataProvider::getFrameworkRootDir() . "/src/ZM/script_setup_loader.php";
@@ -383,7 +498,8 @@ class Framework
* @throws ReflectionException
* @throws ReflectionException
*/
- private function registerServerEvents() {
+ private function registerServerEvents()
+ {
$reader = new AnnotationReader();
$all = ZMUtil::getClassesPsr4(FRAMEWORK_ROOT_DIR . "/src/ZM/Event/SwooleEvent/", "ZM\\Event\\SwooleEvent");
foreach ($all as $v) {
@@ -400,6 +516,7 @@ class Framework
}
foreach (($this->setup_events["setup"] ?? []) as $v) {
+ Console::debug('Calling @OnSetup: ' . $v["class"]);
$c = ZMUtil::getModInstance($v["class"]);
$method = $v["method"];
$c->$method();
@@ -417,7 +534,8 @@ class Framework
* @param $args
* @param $add_port
*/
- private function parseCliArgs($args, &$add_port) {
+ private function parseCliArgs($args, &$add_port)
+ {
$coroutine_mode = true;
global $terminal_id;
$terminal_id = uuidgen();
@@ -494,7 +612,8 @@ class Framework
else Runtime::enableCoroutine(false, SWOOLE_HOOK_ALL);
}
- private static function writeNoDouble($k, $v, &$line_data, &$line_width, &$current_line, $colorful, $max_border) {
+ private static function writeNoDouble($k, $v, &$line_data, &$line_width, &$current_line, $colorful, $max_border)
+ {
$tmp_line = $k . ": " . $v;
//Console::info("写入[".$tmp_line."]");
if (strlen($tmp_line) > $line_width[$current_line]) { //输出的内容太多了,以至于一行都放不下一个,要折行
@@ -538,7 +657,8 @@ class Framework
}
}
- public static function printProps($out, $tty_width, $colorful = true) {
+ public static function printProps($out, $tty_width, $colorful = true)
+ {
$max_border = $tty_width < 65 ? $tty_width : 65;
if (LOAD_MODE == 0) echo Console::setColor("* Framework started with source mode.\n", $colorful ? "yellow" : "");
echo str_pad("", $max_border, "=") . PHP_EOL;
@@ -580,20 +700,23 @@ class Framework
echo str_pad("", $max_border, "=") . PHP_EOL;
}
- public static function getTtyWidth(): string {
+ public static function getTtyWidth(): string
+ {
$size = exec("stty size");
if (empty($size)) return 65;
return explode(" ", trim($size))[1];
}
- public static function loadFrameworkState() {
+ public static function loadFrameworkState()
+ {
if (!file_exists(DataProvider::getDataFolder() . ".state.json")) return [];
$r = json_decode(file_get_contents(DataProvider::getDataFolder() . ".state.json"), true);
if ($r === null) $r = [];
return $r;
}
- public static function saveFrameworkState($data) {
+ public static function saveFrameworkState($data)
+ {
return file_put_contents(DataProvider::getDataFolder() . ".state.json", json_encode($data, 64 | 128 | 256));
}
}