报告中心
appLogin 调用 zjark-platform 403 问题分析报告
WebSocket 网关 1005 问题分析报告
本文档使用 MrDoc 发布
-
+
首页
WebSocket 网关 1005 问题分析报告
**日期**:2026-08-05 **模块**:opmcs-map / opmcs-datacs **端点**: - `/ws/map/baseMap/getTrucksInPositionList`(设备位置推送) - `/ws/mine/getTruckList`(运单列表推送) - `/ws/ruiming/gps`(锐明推送接入) - `/ws/app/gps`(App GPS 接入) --- ## 问题现象 | 访问方式 | 结果 | |---------|------| | 直连 `ws://service:port/path?token=xxx` | ✅ 正常 | | 网关 `ws://gateway:9000/path?token=xxx` | ❌ 连接建立后立即 1005 断开 | --- ## 根因定位(共两处) ### 根因一:HttpSessionHandshakeInterceptor 崩溃 `WebSocketAuthHandshakeInterceptor extends HttpSessionHandshakeInterceptor`,`super.beforeHandshake()` 内部调用 `getServletRequest().getSession()`。网关→后端是 Netty 连接,无 Servlet HttpSession → 异常 → 握手中断。 **修复**:双路径兼容——网关路径读 `User-Code` header 直接返回(不调 super),直连路径 try-catch 兜底。 ### 根因二:全量 JSON 帧超过网关 max-frame-payload-length Spring Cloud Gateway `max-frame-payload-length` 默认 **64KB**。1445 台设备全量推送帧大小 ~1MB,远超限制。网关收到超大帧 → TCP RST → 1005。 **验证过程**: | 尝试 | 结果 | 推论 | |------|------|------| | 加 300ms 延迟 | 仍 RST | 非 relay 就绪时机问题 | | 加 heartbeat 保活 | 客户端收到心跳,数据帧仍 RST | 非 idle timeout | | 调大网关 max-frame-payload-length | ✅ 正常 | **确认根因** | **修复**: - 网关 Nacos 配置:`max-frame-payload-length: 5242880`(5MB) - 后端加 >500KB warn 日志预警 --- ## 帧大小评估 DeviceViewVO 25 个字段,实际填充约 19 个,单设备 JSON ~740 字节。 | 设备数 | 帧大小 | 是否超 64KB | |--------|--------|------------| | 100 台 | ~74 KB | ✅ 刚好超 | | 500 台 | ~370 KB | ✅ | | 1000 台 | ~740 KB | ✅ | | 1445 台(当前) | ~1.07 MB | ✅ | | 5000 台 | ~3.7 MB | ✅ | ### max-frame-payload-length 选值约束 | 上限 | 评价 | |------|------| | 64KB | 默认值,基本啥都发不了 | | 1MB | 够 1000 台,零余量 | | **5MB** | **推荐**,当前 5x 余量,100 并发最坏 500MB 可控 | | 10MB | 够 5000+ 台 | | >10MB | 不建议,应拆分帧或分页 | 推荐配置: ```yaml spring.cloud.gateway.httpclient.websocket.max-frame-payload-length: 5242880 # 5MB ``` 需**重启网关**生效(httpclient 配置不支持热更新)。 --- ## 完整修改清单 ### opmcs-map | 文件 | 改动 | |------|------| | `WebSocketAuthHandshakeInterceptor` | 双路径兼容:网关读 header 不调 super,直连 try-catch | | `TruckPositionWebSocketHandler` | resolveUserCode 拆分(网关/直连双路径);延迟退避推送(300+600ms);>500KB warn | | `TruckTripWebSocketHandler` | 同上(延迟退避推送 + 帧大小预警) | | `WebSocketConfig` | 端点加 `/ws` 前缀 | ### opmcs-datacs | 文件 | 改动 | |------|------| | `DatacsWebSocketHandshakeInterceptor` | **新增**,网关兼容(锐明为外部系统不做认证) | | `WebSocketConfig` | 注册拦截器到 `/ws/ruiming/gps` 和 `/ws/app/gps` | ### zjark-gateway(Nacos 配置) | 配置项 | 值 | |--------|-----| | `spring.cloud.gateway.httpclient.websocket.max-frame-payload-length` | `5242880` | --- ## 认证链路 ### 网关路径 ``` 浏览器 → ws://gateway:9000/ws/map/...?Authorization=Bearer xxx │ ├─ AuthFilter:JWT 校验 → 注入 header: User-Code, User-Id ├─ WebsocketRoutingFilter:HTTP Upgrade → relay(帧上限 5MB) │ └─ 后端: ├─ HandshakeInterceptor:检测到 header User-Code → 不调 super,存 attributes ├─ afterConnectionEstablished:300ms 延迟 → sendMessage ✅ └─ 数据隔离:resolveUserCode 直接读 attributes ``` ### 直连路径 ``` 浏览器 → ws://service:port/ws/map/...?token=xxx │ └─ 后端: ├─ HandshakeInterceptor:提取 token → 存 attributes → super 正常 ├─ afterConnectionEstablished:延迟推送(直连不需要但无害) └─ 数据隔离:resolveUserCode 从 ATTR_TOKEN 解析 JWT ``` --- ## 安全模型 | 场景 | 认证 | 连接 | 数据范围 | |------|------|------|---------| | 网关 + token | AuthFilter | ✅ | 按工程公司过滤 | | 网关 + 无 token | AuthFilter | ❌ 401 | — | | 直连 + token | 后端解析 JWT | ✅ | 按工程公司过滤 | | 直连 + 无 token | — | ✅ | 全量(软隔离) | --- ## 相关文件 | 文件 | 模块 | 说明 | |------|------|------| | `WebSocketAuthHandshakeInterceptor.java` | opmcs-map | 握手拦截器(双路径 + 数据隔离常量) | | `TruckPositionWebSocketHandler.java` | opmcs-map | 设备位置推送(延迟退避 + 帧大小预警) | | `TruckTripWebSocketHandler.java` | opmcs-map | 运单推送(延迟退避 + 帧大小预警) | | `WebSocketConfig.java` | opmcs-map | Handler 注册 + `/ws` 前缀 | | `DatacsWebSocketHandshakeInterceptor.java` | opmcs-datacs | **新增**,网关兼容(无认证) | | `WebSocketConfig.java` | opmcs-datacs | 注册拦截器 | | `RuimingGpsWebSocketHandler.java` | opmcs-datacs | 锐明推送接入 | | `AuthFilter.java` | zjark-gateway | 网关鉴权(注入 User-Code header) |
七宇轩远
2026年08月05日
转发文档
收藏文档
上一篇
下一篇
手机扫码
复制链接
手机扫一扫转发分享
复制链接
Markdown文件
分享
链接
类型
密码
更新密码