diff --git a/src/ZM/Framework.php b/src/ZM/Framework.php index 27667850..09062e58 100644 --- a/src/ZM/Framework.php +++ b/src/ZM/Framework.php @@ -18,14 +18,14 @@ use ZM\Annotation\Swoole\SwooleHandler; use ZM\Config\ZMConfig; use ZM\ConnectionManager\ManagerGM; use ZM\Console\Console; -use ZM\Console\TermColor; use ZM\Exception\ConfigException; -use ZM\Exception\ZMKnownException; +use ZM\Logger\TablePrinter; use ZM\Store\LightCache; use ZM\Store\LightCacheInside; use ZM\Store\Lock\SpinLock; use ZM\Store\ZMAtomic; use ZM\Utils\DataProvider; +use ZM\Utils\Manager\ProcessManager; use ZM\Utils\Terminal; use ZM\Utils\ZMUtil; @@ -84,9 +84,9 @@ class Framework */ public function __construct(array $args = [], bool $instant_mode = false) { - $tty_width = $this->getTtyWidth(); self::$instant_mode = $instant_mode; self::$argv = $args; + Runtime::enableCoroutine(false); // 初始化配置 ZMConfig::setDirectory(DataProvider::getSourceRootDir() . '/config'); @@ -152,7 +152,7 @@ class Framework } // 解析命令行参数 - $this->parseCliArgs(self::$argv, $add_port); + $coroutine_mode = $this->parseCliArgs(self::$argv, $add_port); // 设置默认最长等待时间 if (!isset($this->swoole_server_config['max_wait_time'])) { @@ -180,7 +180,7 @@ class Framework } $out['environment'] = ($args['env'] ?? null) === null ? 'default' : $args['env']; $out['log_level'] = Console::getLevel(); - $out['version'] = ZM_VERSION . (LOAD_MODE === 0 ? (' (build ' . ZM_VERSION_ID . ')') : ''); + $out['version'] = Framework::VERSION . (LOAD_MODE === 0 ? (' (build ' . ZM_VERSION_ID . ')') : ''); $out['master_pid'] = posix_getpid(); if (APP_VERSION !== 'unknown') { $out['app_version'] = APP_VERSION; @@ -212,7 +212,12 @@ class Framework $conf = ZMConfig::get('global', 'remote_terminal'); $out['terminal'] = $conf['host'] . ':' . $conf['port']; } - self::printProps($out, $tty_width, $args['log-theme'] === null); + if (LOAD_MODE === 0) { + echo Console::setColor("* Framework started with source mode.\n", $args['log-theme'] === null ? 'yellow' : ''); + } + // $this->mapOutput($out); // 汉化操作 + $printer = new TablePrinter($out); + $printer->setValueColor('random')->printAll(); } // 预览模式则直接提出 @@ -312,7 +317,14 @@ class Framework // 非静默模式下,打印欢迎信息 if (!self::$argv['private-mode']) { - self::printMotd($tty_width); + self::printMotd(isset($printer) ? $printer->fetchTerminalSize() : 79); + } + + $global_hook = ZMConfig::get('global', 'runtime')['swoole_coroutine_hook_flags'] ?? (SWOOLE_HOOK_ALL & (~SWOOLE_HOOK_CURL)); + if ($coroutine_mode) { + Runtime::enableCoroutine(true, $global_hook); + } else { + Runtime::enableCoroutine(false, SWOOLE_HOOK_ALL); } global $asd; @@ -374,157 +386,21 @@ class Framework } catch (Exception $e) { Console::error('框架初始化出现异常,请检查!'); Console::error(zm_internal_errcode('E00010') . $e->getMessage()); + if (strpos($e->getMessage(), 'Address already in use') !== false) { + if (!ProcessManager::isStateEmpty()) { + Console::error('检测到可能残留框架的工作进程,请先通过命令杀死:server:stop --force'); + } + } Console::debug($e); exit; } } - /** - * 将各进程的pid写入文件,以备后续崩溃及僵尸进程处理使用 - * - * @param int|string $pid - * @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 mixed $id_or_name - * @throws ZMKnownException - * @return false|int|mixed - * @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; - } - 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 null|int|string $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(); + LightCacheInside::set('tmp_kv', 'start_time', microtime(true)); self::$server->start(); zm_atomic('server_is_stopped')->set(1); if (!self::$argv['private-mode']) { @@ -535,63 +411,6 @@ class Framework } } - public static function printProps($out, $tty_width, $colorful = true) - { - $max_border = min($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; - - $current_line = 0; - $line_width = []; - $line_data = []; - foreach ($out as $k => $v) { - start: - if (!isset($line_width[$current_line])) { - $line_width[$current_line] = $max_border - 2; - } - // Console::info("行宽[$current_line]:".$line_width[$current_line]); - if ($max_border >= 57) { // 很宽的时候,一行能放两个短行 - if ($line_width[$current_line] === ($max_border - 2)) { // 空行 - self::writeNoDouble($k, $v, $line_data, $line_width, $current_line, $colorful, $max_border); - } else { // 不是空行,已经有东西了 - $tmp_line = $k . ': ' . $v; - // Console::info("[$current_line]即将插入后面的东西[".$tmp_line."]"); - if (strlen($tmp_line) > $line_width[$current_line]) { // 地方不够,另起一行 - $line_data[$current_line] = str_replace('| ', '', $line_data[$current_line]); - ++$current_line; - goto start; - } // 地方够,直接写到后面并另起一行 - $line_data[$current_line] .= $k . ': '; - if ($colorful) { - $line_data[$current_line] .= TermColor::color8(32); - } - $line_data[$current_line] .= $v; - if ($colorful) { - $line_data[$current_line] .= TermColor::RESET; - } - ++$current_line; - } - } else { // 不够宽,直接写单行 - self::writeNoDouble($k, $v, $line_data, $line_width, $current_line, $colorful, $max_border); - } - } - foreach ($line_data as $v) { - echo $v . PHP_EOL; - } - echo str_pad('', $max_border, '=') . PHP_EOL; - } - - public function getTtyWidth(): int - { - $size = exec('stty size'); - if (empty($size)) { - return 65; - } - return (int) explode(' ', trim($size))[1]; - } - public static function loadFrameworkState() { if (!file_exists(DataProvider::getDataFolder() . '.state.json')) { @@ -609,6 +428,24 @@ class Framework return file_put_contents(DataProvider::getDataFolder() . '.state.json', json_encode($data, 64 | 128 | 256)); } + public function mapOutput(array &$out) + { + $translate = [ + 'working_dir' => '工作目录', + 'listen' => '监听地址', + 'worker' => '工作进程数', + 'environment' => '环境类型', + 'log_level' => '日志级别', + 'version' => '框架版本', + 'master_pid' => '主进程PID', + ]; + foreach ($out as $k => $v) { + if (isset($translate[$k])) { + $this->arrayChangeKey($out, $k, $translate[$k]); + } + } + } + private static function printMotd($tty_width) { if (file_exists(DataProvider::getSourceRootDir() . '/config/motd.txt')) { @@ -776,68 +613,14 @@ class Framework } } } - $global_hook = ZMConfig::get('global', 'runtime')['swoole_coroutine_hook_flags'] ?? (SWOOLE_HOOK_ALL & (~SWOOLE_HOOK_CURL)); - if ($coroutine_mode) { - Runtime::enableCoroutine(true, $global_hook); - } else { - Runtime::enableCoroutine(false, SWOOLE_HOOK_ALL); - } + return $coroutine_mode; } - private static function writeNoDouble($k, $v, &$line_data, &$line_width, &$current_line, $colorful, $max_border) + private function arrayChangeKey(array &$arr, $old_key, $new_key) { - $tmp_line = $k . ': ' . $v; - // Console::info("写入[".$tmp_line."]"); - if (strlen($tmp_line) > $line_width[$current_line]) { // 输出的内容太多了,以至于一行都放不下一个,要折行 - $title_strlen = strlen($k . ': '); - $content_len = $line_width[$current_line] - $title_strlen; - - $line_data[$current_line] = ' ' . $k . ': '; - if ($colorful) { - $line_data[$current_line] .= TermColor::color8(32); - } - $line_data[$current_line] .= substr($v, 0, $content_len); - if ($colorful) { - $line_data[$current_line] .= TermColor::RESET; - } - $rest = substr($v, $content_len); - ++$current_line; // 带标题的第一行满了,折到第二行 - do { - if ($colorful) { - $line_data[$current_line] = TermColor::color8(32); - } - $line_data[$current_line] .= ' ' . substr($rest, 0, $max_border - 2); - if ($colorful) { - $line_data[$current_line] .= TermColor::RESET; - } - $rest = substr($rest, $max_border - 2); - ++$current_line; - } while ($rest > $max_border - 2); // 循环,直到放完 - } else { // 不需要折行 - // Console::info("不需要折行"); - $line_data[$current_line] = ' ' . $k . ': '; - if ($colorful) { - $line_data[$current_line] .= TermColor::color8(32); - } - $line_data[$current_line] .= $v; - if ($colorful) { - $line_data[$current_line] .= TermColor::RESET; - } - - if ($max_border >= 57) { - if (strlen($tmp_line) >= intval(($max_border - 2) / 2)) { // 不需要折行,直接输出一个转下一行 - // Console::info("不需要折行,直接输出一个转下一行"); - ++$current_line; - } else { // 输出很小,写到前面并分片 - // Console::info("输出很小,写到前面并分片"); - $space = intval($max_border / 2) - 2 - strlen($tmp_line); - $line_data[$current_line] .= str_pad('', $space); - $line_data[$current_line] .= '| '; // 添加分片 - $line_width[$current_line] -= (strlen($tmp_line) + 3 + $space); - } - } else { - ++$current_line; - } - } + $keys = array_keys($arr); + $values = array_values($arr); + $keys[array_search($old_key, $keys)] = $new_key; + $arr = array_combine($keys, $values); } }