feat(ws_server): 增强数据库连接稳定性和维护机制

添加数据库连接异常处理和自动重连功能,当数据库连接失败时尝试重新初始化
引入子进程定期检查数据库连接状态,确保连接持续可用
添加信号处理机制,确保进程能优雅退出
This commit is contained in:
2026-01-23 15:54:08 +08:00
parent 6bedc732d1
commit d734ec45d6

View File

@@ -101,13 +101,59 @@ try {
$current_addons = $cachedAddons; $current_addons = $cachedAddons;
} else { } else {
echo "[WebSocket服务器] 从数据库获取插件列表\n"; 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); try {
echo "[WebSocket服务器] 插件列表已缓存(有效期: {$cacheExpire}秒)\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";
} 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'); $db_addon_names = array_column($current_addons, 'name');
@@ -326,9 +372,81 @@ if (!empty($missingDirAddons)) {
echo " - 所有已启用的addon目录都存在\n"; 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 "\n默认测试路径:\n";
echo " - ws://{$httpHost}:{$port}/ws (默认路径,用于连接测试)\n"; echo " - ws://{$httpHost}:{$port}/ws (默认路径,用于连接测试)\n";
echo "按 Ctrl+C 停止服务器\n"; echo "按 Ctrl+C 停止服务器\n";
// 运行服务器
$ratchetApp->run(); $ratchetApp->run();