From d734ec45d6dbfa7b1735c727e165bb7481de925b Mon Sep 17 00:00:00 2001 From: ZF sun <34314687@qq.com> Date: Fri, 23 Jan 2026 15:54:08 +0800 Subject: [PATCH] =?UTF-8?q?feat(ws=5Fserver):=20=E5=A2=9E=E5=BC=BA?= =?UTF-8?q?=E6=95=B0=E6=8D=AE=E5=BA=93=E8=BF=9E=E6=8E=A5=E7=A8=B3=E5=AE=9A?= =?UTF-8?q?=E6=80=A7=E5=92=8C=E7=BB=B4=E6=8A=A4=E6=9C=BA=E5=88=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 添加数据库连接异常处理和自动重连功能,当数据库连接失败时尝试重新初始化 引入子进程定期检查数据库连接状态,确保连接持续可用 添加信号处理机制,确保进程能优雅退出 --- src/ws_server.php | 134 +++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 126 insertions(+), 8 deletions(-) diff --git a/src/ws_server.php b/src/ws_server.php index f3df9c742..086d07fc8 100644 --- a/src/ws_server.php +++ b/src/ws_server.php @@ -101,13 +101,59 @@ try { $current_addons = $cachedAddons; } else { echo "[WebSocket服务器] 从数据库获取插件列表\n"; - $addon_model = new Addon(); - $addon_data = $addon_model->getAddonList([], 'name,status'); - $current_addons = $addon_data['data']; - // 将结果存入缓存 - $cache->set($cacheKey, $current_addons, $cacheExpire); - echo "[WebSocket服务器] 插件列表已缓存(有效期: {$cacheExpire}秒)\n"; + // 尝试获取数据库连接并确保连接有效 + try { + // 尝试初始化数据库连接 + $addon_model = new Addon(); + $addon_data = $addon_model->getAddonList([], 'name,status'); + $current_addons = $addon_data['data']; + + // 将结果存入缓存 + $cache->set($cacheKey, $current_addons, $cacheExpire); + echo "[WebSocket服务器] 插件列表已缓存(有效期: {$cacheExpire}秒)\n"; + } catch (\Exception $dbEx) { + echo "[WebSocket服务器] 数据库操作失败: {$dbEx->getMessage()}\n"; + echo "[WebSocket服务器] 尝试重新初始化数据库连接...\n"; + + // 尝试重新初始化应用和数据库连接 + try { + // 重新初始化应用 + $app->initialize(); + $cache = $app->cache; + + // 再次尝试获取插件列表 + $addon_model = new Addon(); + $addon_data = $addon_model->getAddonList([], 'name,status'); + $current_addons = $addon_data['data']; + + // 将结果存入缓存 + $cache->set($cacheKey, $current_addons, $cacheExpire); + echo "[WebSocket服务器] 重新连接数据库成功,插件列表已缓存\n"; + } catch (\Exception $retryEx) { + echo "[WebSocket服务器] 重新连接数据库失败: {$retryEx->getMessage()}\n"; + echo "[WebSocket服务器] 回退到直接扫描目录获取插件列表\n"; + + // 回退到直接扫描目录 + $addonNames = []; + if (is_dir($addonDir)) { + $handle = opendir($addonDir); + while (($file = readdir($handle)) !== false) { + if ($file != '.' && $file != '..' && is_dir($addonDir . '/' . $file)) { + $addonNames[] = $file; + } + } + closedir($handle); + sort($addonNames); + } + + $current_addon_names = $addonNames; + $enabled_addons = $addonNames; // 回退模式下默认所有插件都启用 + + // 跳过后续的数据库操作 + throw new \Exception('数据库连接失败,已回退到目录扫描模式'); + } + } } $db_addon_names = array_column($current_addons, 'name'); @@ -326,9 +372,81 @@ if (!empty($missingDirAddons)) { echo " - 所有已启用的addon目录都存在\n"; } +// 添加定期检查数据库连接的机制 +// 创建一个单独的进程来定期检查和维护数据库连接 +// 添加信号处理,确保当父进程停止时,子进程也会被终止 +if (extension_loaded('pcntl')) { + // 记录子进程PID + $dbMaintenancePid = null; + + // 创建数据库连接维护子进程 + $pid = pcntl_fork(); + if ($pid == -1) { + echo "[WebSocket服务器] 无法创建子进程来维护数据库连接\n"; + } elseif ($pid == 0) { + // 子进程:定期检查数据库连接 + echo "[WebSocket服务器] 启动数据库连接维护进程\n"; + + // 每30秒检查一次数据库连接 + $checkInterval = 30; // 秒 + + // 设置子进程的信号处理 + pcntl_signal(SIGTERM, function() { + echo "[数据库维护子进程] 收到终止信号,正在退出...\n"; + exit(0); + }); + + while (true) { + // 检查是否有信号需要处理 + pcntl_signal_dispatch(); + + try { + // 尝试执行一个简单的数据库查询来测试连接 + $addon_model = new Addon(); + $addon_model->getAddonList([], 'name', 1, 1); // 只查询一条记录 + echo "[数据库维护子进程] 数据库连接正常\n"; + } catch (\Exception $e) { + echo "[数据库维护子进程] 数据库连接异常: {$e->getMessage()}\n"; + echo "[数据库维护子进程] 尝试重新初始化数据库连接...\n"; + + try { + // 重新初始化应用和数据库连接 + $app->initialize(); + $cache = $app->cache; + echo "[数据库维护子进程] 重新初始化应用成功\n"; + } catch (\Exception $retryEx) { + echo "[数据库维护子进程] 重新初始化应用失败: {$retryEx->getMessage()}\n"; + } + } + + // 等待指定的时间间隔 + sleep($checkInterval); + } + } else { + // 父进程:记录子进程PID并设置信号处理 + $dbMaintenancePid = $pid; + + // 设置父进程的信号处理 + pcntl_signal(SIGINT, function() use ($dbMaintenancePid) { + echo "[WebSocket服务器] 收到终止信号,正在停止...\n"; + + // 如果子进程存在,发送终止信号 + if ($dbMaintenancePid) { + echo "[WebSocket服务器] 停止数据库连接维护进程\n"; + posix_kill($dbMaintenancePid, SIGTERM); + // 等待子进程退出 + pcntl_wait($status); + } + + echo "[WebSocket服务器] 已停止\n"; + exit(0); + }); + } +} + +// 运行服务器 +echo "[WebSocket服务器] 启动主服务器进程\n"; echo "\n默认测试路径:\n"; echo " - ws://{$httpHost}:{$port}/ws (默认路径,用于连接测试)\n"; echo "按 Ctrl+C 停止服务器\n"; - -// 运行服务器 $ratchetApp->run();