mirror of
https://github.com/zhamao-robot/zhamao-framework.git
synced 2026-03-18 05:04:51 +08:00
enhancement for process state
This commit is contained in:
parent
e95925c129
commit
3c87abc6e8
@ -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("<comment>没有检测到正在运行的守护进程或框架进程!</comment>");
|
||||
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("<comment>未检测到正在运行的守护进程或框架进程!</comment>");
|
||||
unlink($pid_path);
|
||||
Framework::removeProcessState(ZM_PROCESS_MASTER);
|
||||
die();
|
||||
}
|
||||
$this->daemon_file = $file;
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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"]
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -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;
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user