增加websocket,数据实时显示

This commit is contained in:
Alex Yang
2025-11-26 01:11:37 +08:00
parent 63154085f7
commit 54dbb024e1
5 changed files with 447 additions and 3 deletions

View File

@@ -5,6 +5,8 @@ let ratioChart = null;
let dnsRequestsChart = null;
let queryTypeChart = null; // 解析类型统计饼图
let intervalId = null;
let wsConnection = null;
let wsReconnectTimer = null;
// 存储统计卡片图表实例
let statCardCharts = {};
// 存储统计卡片历史数据
@@ -30,14 +32,260 @@ async function initDashboard() {
// 初始化时间范围切换
initTimeRangeToggle();
// 设置定时更新
intervalId = setInterval(loadDashboardData, 5000); // 每5秒更新一次
// 建立WebSocket连接
connectWebSocket();
// 在页面卸载时清理资源
window.addEventListener('beforeunload', cleanupResources);
} catch (error) {
console.error('初始化仪表盘失败:', error);
showNotification('初始化失败: ' + error.message, 'error');
}
}
// 建立WebSocket连接
function connectWebSocket() {
try {
// 构建WebSocket URL
const wsProtocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
const wsUrl = `${wsProtocol}//${window.location.host}/ws/stats`;
console.log('正在连接WebSocket:', wsUrl);
// 创建WebSocket连接
wsConnection = new WebSocket(wsUrl);
// 连接打开事件
wsConnection.onopen = function() {
console.log('WebSocket连接已建立');
showNotification('实时数据更新已连接', 'success');
// 清除重连计时器
if (wsReconnectTimer) {
clearTimeout(wsReconnectTimer);
wsReconnectTimer = null;
}
};
// 接收消息事件
wsConnection.onmessage = function(event) {
try {
const data = JSON.parse(event.data);
if (data.type === 'initial_data' || data.type === 'stats_update') {
console.log('收到实时数据更新');
processRealTimeData(data.data);
}
} catch (error) {
console.error('处理WebSocket消息失败:', error);
}
};
// 连接关闭事件
wsConnection.onclose = function(event) {
console.warn('WebSocket连接已关闭代码:', event.code);
wsConnection = null;
// 设置重连
setupReconnect();
};
// 连接错误事件
wsConnection.onerror = function(error) {
console.error('WebSocket连接错误:', error);
};
} catch (error) {
console.error('创建WebSocket连接失败:', error);
// 如果WebSocket连接失败回退到定时刷新
fallbackToIntervalRefresh();
}
}
// 设置重连逻辑
function setupReconnect() {
if (wsReconnectTimer) {
return; // 已经有重连计时器在运行
}
const reconnectDelay = 5000; // 5秒后重连
console.log(`将在${reconnectDelay}ms后尝试重新连接WebSocket`);
wsReconnectTimer = setTimeout(() => {
connectWebSocket();
}, reconnectDelay);
}
// 处理实时数据更新
function processRealTimeData(stats) {
try {
// 更新统计卡片
updateStatsCards(stats);
// 获取查询类型统计数据
let queryTypeStats = null;
if (stats.dns && stats.dns.QueryTypes) {
queryTypeStats = Object.entries(stats.dns.QueryTypes).map(([type, count]) => ({
type,
count
}));
}
// 更新图表数据
updateCharts(stats, queryTypeStats);
// 更新卡片图表
updateStatCardCharts(stats);
// 尝试从stats中获取总查询数等信息
if (stats.dns) {
totalQueries = stats.dns.Allowed + stats.dns.Blocked + (stats.dns.Errors || 0);
blockedQueries = stats.dns.Blocked;
errorQueries = stats.dns.Errors || 0;
allowedQueries = stats.dns.Allowed;
} else {
totalQueries = stats.totalQueries || 0;
blockedQueries = stats.blockedQueries || 0;
errorQueries = stats.errorQueries || 0;
allowedQueries = stats.allowedQueries || 0;
}
// 更新新卡片数据
if (document.getElementById('avg-response-time')) {
const responseTime = stats.avgResponseTime ? stats.avgResponseTime.toFixed(2) + 'ms' : '---';
// 计算响应时间趋势
let responsePercent = '---';
let trendClass = 'text-gray-400';
let trendIcon = '---';
if (stats.avgResponseTime !== undefined && stats.avgResponseTime !== null) {
// 存储当前值用于下次计算趋势
const prevResponseTime = window.dashboardHistoryData.prevResponseTime || stats.avgResponseTime;
window.dashboardHistoryData.prevResponseTime = stats.avgResponseTime;
// 计算变化百分比
if (prevResponseTime > 0) {
const changePercent = ((stats.avgResponseTime - prevResponseTime) / prevResponseTime) * 100;
responsePercent = Math.abs(changePercent).toFixed(1) + '%';
// 设置趋势图标和颜色
if (changePercent > 0) {
trendIcon = '↓';
trendClass = 'text-danger';
} else if (changePercent < 0) {
trendIcon = '↑';
trendClass = 'text-success';
} else {
trendIcon = '•';
trendClass = 'text-gray-500';
}
}
}
document.getElementById('avg-response-time').textContent = responseTime;
const responseTimePercentElem = document.getElementById('response-time-percent');
if (responseTimePercentElem) {
responseTimePercentElem.textContent = trendIcon + ' ' + responsePercent;
responseTimePercentElem.className = `text-sm flex items-center ${trendClass}`;
}
}
if (document.getElementById('top-query-type')) {
const queryType = stats.topQueryType || '---';
const queryPercentElem = document.getElementById('query-type-percentage');
if (queryPercentElem) {
queryPercentElem.textContent = '• ---';
queryPercentElem.className = 'text-sm flex items-center text-gray-500';
}
document.getElementById('top-query-type').textContent = queryType;
}
if (document.getElementById('active-ips')) {
const activeIPs = stats.activeIPs !== undefined ? formatNumber(stats.activeIPs) : '---';
// 计算活跃IP趋势
let ipsPercent = '---';
let trendClass = 'text-gray-400';
let trendIcon = '---';
if (stats.activeIPs !== undefined) {
const prevActiveIPs = window.dashboardHistoryData.prevActiveIPs || stats.activeIPs;
window.dashboardHistoryData.prevActiveIPs = stats.activeIPs;
if (prevActiveIPs > 0) {
const changePercent = ((stats.activeIPs - prevActiveIPs) / prevActiveIPs) * 100;
ipsPercent = Math.abs(changePercent).toFixed(1) + '%';
if (changePercent > 0) {
trendIcon = '↑';
trendClass = 'text-primary';
} else if (changePercent < 0) {
trendIcon = '↓';
trendClass = 'text-secondary';
} else {
trendIcon = '•';
trendClass = 'text-gray-500';
}
}
}
document.getElementById('active-ips').textContent = activeIPs;
const activeIpsPercentElem = document.getElementById('active-ips-percentage');
if (activeIpsPercentElem) {
activeIpsPercentElem.textContent = trendIcon + ' ' + ipsPercent;
activeIpsPercentElem.className = `text-sm flex items-center ${trendClass}`;
}
}
} catch (error) {
console.error('处理实时数据失败:', error);
}
}
// 回退到定时刷新
function fallbackToIntervalRefresh() {
console.warn('回退到定时刷新模式');
showNotification('实时更新连接失败,已切换到定时刷新模式', 'warning');
// 如果已经有定时器,先清除
if (intervalId) {
clearInterval(intervalId);
}
// 设置新的定时器
intervalId = setInterval(async () => {
try {
await loadDashboardData();
} catch (error) {
console.error('定时刷新失败:', error);
}
}, 5000); // 每5秒更新一次
}
// 清理资源
function cleanupResources() {
// 清除WebSocket连接
if (wsConnection) {
wsConnection.close();
wsConnection = null;
}
// 清除重连计时器
if (wsReconnectTimer) {
clearTimeout(wsReconnectTimer);
wsReconnectTimer = null;
}
// 清除定时刷新
if (intervalId) {
clearInterval(intervalId);
intervalId = null;
}
}
// 加载仪表盘数据
async function loadDashboardData() {
console.log('开始加载仪表盘数据');