feat(ws_server): 增强数据库连接稳定性和维护机制
添加数据库连接异常处理和自动重连功能,当数据库连接失败时尝试重新初始化 引入子进程定期检查数据库连接状态,确保连接持续可用 添加信号处理机制,确保进程能优雅退出
This commit is contained in:
@@ -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();
|
||||||
|
|||||||
Reference in New Issue
Block a user