feat(socket): 添加 Socket 运行时调试日志及轮询 400 错误调试文档
- 更新 web/index.html 中 index.js 的资源版本为 v6 以清除旧缓存 - 在 web/index.js 中新增 Socket 运行时日志上报逻辑,捕获并上报连接、断开、错误及重连事件 - 新增调试文档记录生产环境 Socket.IO polling 400 错误的问题、现象与排查计划
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
# [OPEN] debug-socket-polling-400
|
||||
|
||||
## 问题描述
|
||||
- 生产环境后台页持续出现 Socket.IO polling 400。
|
||||
- 浏览器控制台可见大量:
|
||||
- `GET /socket.io/?EIO=4&transport=polling&sid=... 400`
|
||||
- `POST /socket.io/?EIO=4&transport=polling&sid=... 400`
|
||||
- 同时存在无关但可观察现象:
|
||||
- `vue.global.js` 开发版提示
|
||||
- `/favicon.ico` 404
|
||||
|
||||
## 已知现象
|
||||
- 首次 `polling` 握手能拿到 `sid`,后续带 `sid` 的轮询请求失败。
|
||||
- 失败后客户端持续重连,表现为“连接一下又断开”。
|
||||
|
||||
## 初始假设
|
||||
- 假设 1:反向代理或负载均衡未做会话保持,导致同一个 `sid` 的后续请求落到不同后端实例。
|
||||
- 假设 2:CDN/边缘代理对 `/socket.io/` 的长轮询处理不稳定,导致 `sid` 生命周期被中断。
|
||||
- 假设 3:客户端当前传输策略触发了不兼容的重连路径,反复创建新 `sid` 又迅速失效。
|
||||
- 假设 4:服务端 Engine.IO 对同一会话的请求顺序或超时异常敏感,导致后续 `GET/POST` 被判无效。
|
||||
- 假设 5:`vue.global.js` 开发版提示和 `favicon.ico` 404 只是噪音,不是 Socket.IO 400 的主因。
|
||||
|
||||
## 当前计划
|
||||
- 先继续收集线上现成证据,不改业务逻辑。
|
||||
- 对比首次握手与后续 `sid` 请求是否稳定落到同一服务链路。
|
||||
- 若证据仍指向服务端/代理层,再决定是否做最小客户端降级或服务端兼容修复。
|
||||
+2
-2
@@ -1,4 +1,4 @@
|
||||
<!DOCTYPE html>
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<!-- 充满未知和不稳定的票务系统! -->
|
||||
|
||||
@@ -933,7 +933,7 @@
|
||||
</div>
|
||||
<script src="/custom-dialog.js?v=12"></script>
|
||||
<script src="/public-status.js?v=13"></script>
|
||||
<script src="index.js?v=5"></script>
|
||||
<script src="index.js?v=6"></script>
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
const isDomain = location.hostname.includes('fse-media.group');
|
||||
|
||||
+63
-2
@@ -38,6 +38,38 @@ createApp({
|
||||
// Prefer polling first so admin remains connected even when the proxy
|
||||
// does not support WebSocket upgrades reliably.
|
||||
const socket = io({ transports: ['polling', 'websocket'] });
|
||||
// #region debug-point socket-runtime-admin
|
||||
const reportSocketRuntime = (type, detail = {}) => {
|
||||
try {
|
||||
const payload = JSON.stringify({
|
||||
category: 'admin',
|
||||
source: 'socket-runtime',
|
||||
level: type.includes('error') ? 'error' : 'info',
|
||||
type,
|
||||
detail: {
|
||||
page: location.pathname,
|
||||
href: location.href,
|
||||
online: navigator.onLine,
|
||||
socket_id: socket.id || '',
|
||||
connected: !!socket.connected,
|
||||
transport: socket.io?.engine?.transport?.name || '',
|
||||
...detail
|
||||
}
|
||||
});
|
||||
if (navigator.sendBeacon) {
|
||||
const blob = new Blob([payload], { type: 'application/json' });
|
||||
navigator.sendBeacon('/api/log', blob);
|
||||
return;
|
||||
}
|
||||
fetch('/api/log', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: payload,
|
||||
keepalive: true
|
||||
}).catch(() => {});
|
||||
} catch (_) {}
|
||||
};
|
||||
// #endregion
|
||||
|
||||
// Data State
|
||||
const stations = ref([]);
|
||||
@@ -1354,8 +1386,37 @@ createApp({
|
||||
};
|
||||
|
||||
// Socket Listeners
|
||||
socket.on('connect', () => { connected.value = true; });
|
||||
socket.on('disconnect', () => { connected.value = false; });
|
||||
// #region debug-point socket-runtime-admin
|
||||
socket.on('connect', () => {
|
||||
connected.value = true;
|
||||
reportSocketRuntime('socket_connect');
|
||||
});
|
||||
socket.on('disconnect', (reason) => {
|
||||
connected.value = false;
|
||||
reportSocketRuntime('socket_disconnect', { reason: reason || '' });
|
||||
});
|
||||
socket.on('connect_error', (error) => {
|
||||
reportSocketRuntime('socket_connect_error', {
|
||||
message: error?.message || '',
|
||||
description: error?.description || '',
|
||||
context: error?.context || null
|
||||
});
|
||||
});
|
||||
if (socket.io) {
|
||||
socket.io.on('reconnect_attempt', (attempt) => {
|
||||
reportSocketRuntime('socket_reconnect_attempt', { attempt: Number(attempt) || 0 });
|
||||
});
|
||||
socket.io.on('reconnect_error', (error) => {
|
||||
reportSocketRuntime('socket_reconnect_error', {
|
||||
message: error?.message || '',
|
||||
description: error?.description || ''
|
||||
});
|
||||
});
|
||||
socket.io.on('reconnect_failed', () => {
|
||||
reportSocketRuntime('socket_reconnect_failed');
|
||||
});
|
||||
}
|
||||
// #endregion
|
||||
|
||||
socket.on('stations:updated', (data) => {
|
||||
stations.value = data;
|
||||
|
||||
Reference in New Issue
Block a user