节点监控离线延迟去抖

This commit is contained in:
root
2026-02-01 14:41:23 +00:00
parent 936158dd32
commit 38b70821c5
+67 -17
View File
@@ -88,6 +88,8 @@ export default function NodePage() {
const [nodeList, setNodeList] = useState<Node[]>([]); const [nodeList, setNodeList] = useState<Node[]>([]);
const [nodeOrder, setNodeOrder] = useState<number[]>([]); const [nodeOrder, setNodeOrder] = useState<number[]>([]);
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [wsConnected, setWsConnected] = useState(false);
const [wsConnecting, setWsConnecting] = useState(false);
const [dialogVisible, setDialogVisible] = useState(false); const [dialogVisible, setDialogVisible] = useState(false);
const [dialogTitle, setDialogTitle] = useState(''); const [dialogTitle, setDialogTitle] = useState('');
const [isEdit, setIsEdit] = useState(false); const [isEdit, setIsEdit] = useState(false);
@@ -122,6 +124,31 @@ export default function NodePage() {
const reconnectTimerRef = useRef<NodeJS.Timeout | null>(null); const reconnectTimerRef = useRef<NodeJS.Timeout | null>(null);
const reconnectAttemptsRef = useRef(0); const reconnectAttemptsRef = useRef(0);
const maxReconnectAttempts = 5; const maxReconnectAttempts = 5;
const offlineTimersRef = useRef<Map<number, ReturnType<typeof setTimeout>>>(new Map());
const offlineDelayMs = 3000;
const clearOfflineTimer = (nodeId: number) => {
const timer = offlineTimersRef.current.get(nodeId);
if (timer) {
clearTimeout(timer);
offlineTimersRef.current.delete(nodeId);
}
};
const scheduleNodeOffline = (nodeId: number) => {
if (offlineTimersRef.current.has(nodeId)) return;
const timer = setTimeout(() => {
offlineTimersRef.current.delete(nodeId);
setNodeList((prev) =>
prev.map((node) => {
if (node.id !== nodeId) return node;
if (node.connectionStatus === 'offline' && node.systemInfo === null) return node;
return { ...node, connectionStatus: 'offline', systemInfo: null };
})
);
}, offlineDelayMs);
offlineTimersRef.current.set(nodeId, timer);
};
useEffect(() => { useEffect(() => {
loadNodes(); loadNodes();
@@ -208,10 +235,13 @@ export default function NodePage() {
const wsUrl = baseUrl.replace(/^http/, 'ws').replace(/\/api\/v1\/$/, '') + `/system-info?type=0&secret=${localStorage.getItem('token')}`; const wsUrl = baseUrl.replace(/^http/, 'ws').replace(/\/api\/v1\/$/, '') + `/system-info?type=0&secret=${localStorage.getItem('token')}`;
try { try {
setWsConnecting(true);
websocketRef.current = new WebSocket(wsUrl); websocketRef.current = new WebSocket(wsUrl);
websocketRef.current.onopen = () => { websocketRef.current.onopen = () => {
reconnectAttemptsRef.current = 0; reconnectAttemptsRef.current = 0;
setWsConnected(true);
setWsConnecting(false);
}; };
websocketRef.current.onmessage = (event) => { websocketRef.current.onmessage = (event) => {
@@ -229,9 +259,13 @@ export default function NodePage() {
websocketRef.current.onclose = () => { websocketRef.current.onclose = () => {
websocketRef.current = null; websocketRef.current = null;
setWsConnected(false);
setWsConnecting(false);
attemptReconnect(); attemptReconnect();
}; };
} catch (error) { } catch (error) {
setWsConnected(false);
setWsConnecting(false);
attemptReconnect(); attemptReconnect();
} }
}; };
@@ -239,21 +273,27 @@ export default function NodePage() {
// 处理WebSocket消息 // 处理WebSocket消息
const handleWebSocketMessage = (data: any) => { const handleWebSocketMessage = (data: any) => {
const { id, type, data: messageData } = data; const { id, type, data: messageData } = data;
const nodeId = Number(id);
if (Number.isNaN(nodeId)) return;
if (type === 'status') { if (type === 'status') {
setNodeList(prev => prev.map(node => { if (messageData === 1) {
if (node.id == id) { clearOfflineTimer(nodeId);
return { setNodeList((prev) =>
...node, prev.map((node) => {
connectionStatus: messageData === 1 ? 'online' : 'offline', if (node.id !== nodeId) return node;
systemInfo: messageData === 0 ? null : node.systemInfo if (node.connectionStatus === 'online') return node;
}; return { ...node, connectionStatus: 'online' };
} })
return node; );
})); } else {
// 离线事件做延迟处理,避免短抖动导致频繁闪烁
scheduleNodeOffline(nodeId);
}
} else if (type === 'info') { } else if (type === 'info') {
clearOfflineTimer(nodeId);
setNodeList(prev => prev.map(node => { setNodeList(prev => prev.map(node => {
if (node.id == id) { if (node.id === nodeId) {
try { try {
let systemInfo; let systemInfo;
if (typeof messageData === 'string') { if (typeof messageData === 'string') {
@@ -316,10 +356,12 @@ export default function NodePage() {
// 尝试重新连接 // 尝试重新连接
const attemptReconnect = () => { const attemptReconnect = () => {
if (reconnectTimerRef.current) return;
if (reconnectAttemptsRef.current < maxReconnectAttempts) { if (reconnectAttemptsRef.current < maxReconnectAttempts) {
reconnectAttemptsRef.current++; reconnectAttemptsRef.current++;
reconnectTimerRef.current = setTimeout(() => { reconnectTimerRef.current = setTimeout(() => {
reconnectTimerRef.current = null;
initWebSocket(); initWebSocket();
}, 3000 * reconnectAttemptsRef.current); }, 3000 * reconnectAttemptsRef.current);
} }
@@ -332,7 +374,12 @@ export default function NodePage() {
reconnectTimerRef.current = null; reconnectTimerRef.current = null;
} }
offlineTimersRef.current.forEach((timer) => clearTimeout(timer));
offlineTimersRef.current.clear();
reconnectAttemptsRef.current = 0; reconnectAttemptsRef.current = 0;
setWsConnected(false);
setWsConnecting(false);
if (websocketRef.current) { if (websocketRef.current) {
websocketRef.current.onopen = null; websocketRef.current.onopen = null;
@@ -347,12 +394,6 @@ export default function NodePage() {
websocketRef.current = null; websocketRef.current = null;
} }
setNodeList(prev => prev.map(node => ({
...node,
connectionStatus: 'offline',
systemInfo: null
})));
}; };
@@ -823,6 +864,15 @@ export default function NodePage() {
</div> </div>
{!wsConnected && (
<Alert
color="warning"
variant="flat"
description={wsConnecting ? '监控连接中...' : '监控连接已断开,正在重连...'}
className="mb-4"
/>
)}
{/* 节点列表 */} {/* 节点列表 */}
{loading ? ( {loading ? (
<div className="flex items-center justify-center h-64"> <div className="flex items-center justify-center h-64">