GOMS WebSocket 集成设计(STOMP over SockJS)
GOMS WebSocket 集成设计(STOMP over SockJS)
笔记归档:基于 Goms-Cloud-springboot2(RuoYi-Cloud 3.6.8 / Spring Boot 2.7 / Vue2)落地实践。
对齐旧项目 LegacyAppChatMessage字段,增补大类category,并理顺点对点路由与鉴权边界。
源码备份见同目录webSocket_demo/。
近期收敛:常量/构建合并为单一ChatMessages;SockJS 断开噪音由SockJsExceptionResolver收口(不动公共GlobalExceptionHandler);监测浮层「在线 N(M)」+ 指定用户el-autocomplete(可手输 userId,下拉 z-index 高于浮层)。
2026-08-07:支持sys_config.websocket_url独立 WS 通道(nginx/ws/);已配时优先原生websocket,未配仍走 API 网关 xhr/xdr 回退。
2026-08-10:补齐 主平台二级目录(/iws/yj_goms/)场景:主平台反代必须透传Upgrade,否则只能 xhr 长轮询;正式已验证配置见 §3.3。
同日(工作区增强,周五发包未含):WebSocketService增加 https 协议纠正、websocket_native_enabled、以及WS_NATIVE_FAILED_KEY本会话跳过原生 WS(见 §3.4)。
1. 目标与边界
| 目标 | 说明 |
|---|---|
| 浏览器实时收消息 | 通知弹窗、应急多人同步、建议刷新页面等 |
| 业务写操作仍走 REST | 前端不通过 WebSocket 改业务状态;改完由服务端推事件 |
| 网关/反代友好 | 默认可走 SockJS HTTP 回退;正式/本地可配独立 websocket_url(nginx /ws/ + Upgrade)走原生 websocket |
| 二级目录挂载 | 微应用挂在主平台 /iws/{app}/ 下时,WS 与静态页共用入口反代,每一层都要透传 Upgrade(见 §3.3) |
| 可观测 | 监测浮层 + 控制台 __gomsWs + 诊断 REST |
非目标(当前):跨 JVM 广播(内嵌 SimpleBroker 不跨实例;集群需外置 broker)。
2. 总体架构
2.1 分层职责
| 层 | 职责 |
|---|---|
主平台 nginx /yj_goms/ | 二级目录入口:静态 + WS Upgrade 透传到 GOMS 机(§3.3,最易漏) |
GOMS nginx /ws/ | 独立 WS 入口;须支持 Upgrade,反代到 goms-system /socket/endpointWs |
| 网关 | 回退路径 /goms-system/socket/** 白名单(握手);剥离外部伪造的 from-source;/webSocket/** 不白名单 |
| goms-system | STOMP Broker、推送 Service、诊断 REST、模板推送 |
| goms-module / 其它服务 | 业务落库后 Feign 调 RemoteWebSocketService |
| ruoyi-ui | Layout 建连;Navbar 订系统频道;业务页订事件或自订 topic |
3. 协议与端点
| 项 | 值 |
|---|---|
| 推荐(独立 WS) | sys_config.websocket_url,如 http://127.0.0.1:19095/ws(nginx /ws/ → goms-system:18093/socket/endpointWs) |
| 回退(API 网关) | {origin}{VUE_APP_BASE_API}/goms-system/socket/endpointWs |
| 服务内路径 | /socket/endpointWs(网关 StripPrefix / nginx 反代后) |
| 传输 | 已配 websocket_url:默认优先原生 websocket,失败再 xhr/xdr;未配:仅 xhr-streaming / xdr-streaming / xhr-polling / xdr-polling;可用 websocket_native_enabled / 本会话跳过强制仅 xhr(§3.4) |
| STOMP | CONNECT / SUBSCRIBE / MESSAGE |
前端解析见 WebSocketService.resolveEndpointUrl() / resolveSockJsTransports();诊断快照含 endpointSource(sys_config.websocket_url | VUE_APP_BASE_API)、nativeTransportEnabled / nativeTransportSkipped。
3.1 为何握手不能带 Authorization
浏览器 SockJS 的 /info、xhr 请求由 sockjs-client 发起,无法像 axios 一样自定义 Authorization。
因此:
- 回退走网关时必须白名单
/socket/**(否则握手 401) - 独立
/ws/路径不经 iws-gateway REST,仍靠 nginx/IWS 暴露;鉴权不变 - 身份在 STOMP CONNECT 帧校验:前端已传
Authorization: Bearer …,服务端InboundChannelInterceptor用TokenService解析,Principal 只认 Token 内 userId
3.2 独立 websocket_url 与 nginx(本地示例)
| 项 | 说明 |
|---|---|
| SQL | ruoyi-modules/goms-system/sql/websocket_sys_config.sql 写入 websocket_url(默认 http://127.0.0.1:19095/ws) |
| nginx | 监听 19095,location /ws/ → http://127.0.0.1:18093/socket/endpointWs/,并开启 Upgrade / Connection |
| 正式环境 | 按 IWS 平台 WS 路径填写,如 https://host/iws/yj_goms/ws(须 https,见 §3.3) |
| 改后 | 「参数设置」刷新缓存;前端重新登录或拉配置后再建连 |
未配 websocket_url(空)时行为与旧版一致:经 API 网关、仅 HTTP 回退,避免现场无 Upgrade 时优先原生 ws 拖慢建连。
3.3 二级目录 / 主平台挂载场景(踩坑重点 · 卡了很久)
场景:GOMS 作为微应用挂在主平台
/iws/yj_goms/下(VUE_APP_SUB_PATH=/iws/yj_goms/),页面与 WS 同域同入口,中间至少两层 nginx。
对照集成文档:主平台H5方式集成.md§六;对照旧项目 LegacyApp:/iws/BizApp/ws/.../websocket。
3.3.1 为什么 Network「很脏」:不是接口变慢
未打通原生 WS 时,SockJS 只能走 xhr-polling,URL 形如:
.../goms-system/socket/endpointWs/{server}/{session}/xhr
| 现象 | 真相 |
|---|---|
| 每条约 25s 结束一次 | SockJS 默认心跳 / 长轮询超时,协议设计如此 |
| 请求很频繁 | 每个开了 WS 的登录用户都在轮转;人多时网关日志会「刷屏」 |
F12 没有 websocket 类型 | 原生升级失败或被禁用,降级到 HTTP 回退 |
LegacyApp 线上「干净」是因为走了专用通道:
ws://.../iws/BizApp/ws/{session}/websocket ← 一条原生 WS
GOMS 若把 SockJS 绑在 iws-gateway/yj_goms_api(REST 网关) 上,网关通常不支持 Upgrade,只能 xhr —— 这是架构差异,不是业务接口性能问题。
3.3.2 正确链路(两层 nginx,缺一不可)
[浏览器]
wss://主平台/iws/yj_goms/ws/{session}/websocket
│ sys_config.websocket_url = https://主平台/iws/yj_goms/ws
▼
[主平台 nginx] location /yj_goms/ ← ★ 必须透传 Upgrade / Connection
│ proxy_pass → GOMS 机 nginx(端口以现场为准)
│ 剥掉 /yj_goms 前缀后,上游收到 /ws/...
▼
[GOMS 机 nginx] location /ws/ ← 反代到业务进程
│ proxy_pass → 127.0.0.1:18093/socket/endpointWs/
▼
[goms-system]
STOMP CONNECT + Authorization → Principal
| 层级 | location | 作用 | 漏配后果 |
|---|---|---|---|
| 主平台 | /yj_goms/ | 静态页 + WS Upgrade 透传到 GOMS 机 | Upgrade 头变 null → 后端 invalid Upgrade header: null → 只能 xhr |
| GOMS 机 | /ws/(写在 location / 之前) | 直连 goms-system:18093,不经 ruoyi-gateway / iws-gateway | /ws 被当静态 SPA 吃掉,或 Upgrade 再次丢失 |
REST 业务仍走 iws-gateway/yj_goms_api(token / 加解密 / 防篡改不变)。
WebSocket 从不经过 axios 入参加密与防篡改;握手可无 Bearer,身份只在 STOMP CONNECT。
3.3.3 正式环境已验证:主平台加这几行就够
现场卡点往往在 主平台 /yj_goms/ 只做了普通 HTTP 反代,没有 Upgrade。补上即可(OPTIONS 等原有块可保留):
location /yj_goms/ {
# 原 OPTIONS + 静态/页面反代可保留
proxy_pass http://{GOMS机IP}:{端口}/; # 正式端口以现场为准,如 18081 / 19095
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 36000s;
}
GOMS 机(本机 nginx 端口以现场为准,常见 19095 / 18081)侧:
location /ws/ {
proxy_pass http://127.0.0.1:18093/socket/endpointWs/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
proxy_read_timeout 36000s;
}
3.3.4 websocket_url 怎么填(二级目录)
| 环境 | sys_config.websocket_url | 说明 |
|---|---|---|
| 本地 | http://127.0.0.1:19095/ws | 直连本机 nginx /ws/ |
| 正式(主平台挂载) | https://{主平台域名}/iws/yj_goms/ws | 与页面同域;必须 https,页面 https 时 SockJS 才走 wss:// |
易错:
| 错误写法 | 问题 |
|---|---|
http://.../iws/yj_goms/ws(正式) | HTTPS 页混合内容 / 无法升 wss |
.../iws/iws-gateway/yj_goms_api/goms-system/socket/endpointWs | 又绕回 REST 网关,原生 WS 仍不可用 |
只配 GOMS 机 /ws/,主平台不透传 Upgrade | 请求能到 18093,但 Upgrade 已丢 → invalid Upgrade header: null |
只改 nginx、不改 websocket_url | 前端仍走 VUE_APP_BASE_API 回退路径 |
改后:「参数设置」刷新缓存 → 重新登录或重连;控制台 __gomsWs.reconnect()。
3.3.5 与 LegacyApp 对照
| 项 | LegacyApp | GOMS(二级目录正确配法) |
|---|---|---|
| 浏览器入口 | /iws/BizApp/ws/.../websocket | /iws/yj_goms/ws/.../websocket |
| 主平台/边缘 | 专用 /ws/ + Upgrade | /yj_goms/ 整段透传 Upgrade(WS 与静态同入口) |
| 应用机 | location /ws/ → busModule/endpointWs | location /ws/ → socket/endpointWs |
| 前端 | websocket_url + transports 含 websocket | 同:sys_config.websocket_url;已配则优先原生 |
| Network | 一条 WS | 一条 WS(未配齐时满屏 /xhr) |
3.3.6 验证清单
| 检查 | 期望 |
|---|---|
| F12 Network | 出现 websocket 类型,或 URL 以 /websocket 结尾 |
__gomsWs.status().transport | "websocket" |
__gomsWs.status().endpointSource | "sys_config.websocket_url" |
__gomsWs.status().nativeTransportSkipped | Upgrade 通时应为 false;未通且已降级可为 true |
| 后端日志 | WebSocket CONNECT, userId=(不是 invalid Upgrade header: null) |
临时兜底(主平台短期改不了):见 §3.4(websocket_native_enabled=false / 清空 websocket_url / 本会话自动跳过)—— 能用但 Network 仍会 25s /xhr。
3.4 原生 WS 失败兜底(WS_NATIVE_FAILED_KEY 等)
背景:已配
websocket_url且主平台 Upgrade 未透传时,SockJS 仍会先试原生websocket→ 控制台刷ws:///wss://失败 → 再降级 xhr。
周五发包(上午发布)尚未包含本节前端逻辑;属工作区后续增强,源码备份已同步webSocket_demo/js/WebSocketService.js。
| 机制 | 说明 |
|---|---|
_normalizeEndpointProtocol | 页面为 https: 时,把配置里的 http:// 端点升为 https://,避免混合内容与错误的 ws:// |
websocket_native_enabled | sys_config 开关,默认 true(未配键等同 true);设 false 时即使有 websocket_url 也只用 xhr/xdr |
WS_NATIVE_FAILED_KEY(sessionStorage.goms_ws_native_failed) | 本会话标记:已确认原生不可用 |
_markNativeWebsocketFailed | 连上后若期望原生却实际 transport !== 'websocket',写入上述 key,并记诊断事件 native_ws_skip |
_shouldSkipNativeWebsocket | native_enabled=false 或 session 已标记 → resolveSockJsTransports 返回仅 xhr/xdr |
已配 websocket_url
├─ websocket_native_enabled=false → 全程 xhrOnly
├─ sessionStorage goms_ws_native_failed=1 → 本会话 xhrOnly(刷新标签页前)
└─ 否则 transports = [websocket, xhr-streaming, …]
└─ onConnect 发现 transport≠websocket → 标记 failed,后续重连不再试原生
运维建议
| 场景 | 做法 |
|---|---|
| IWS 短期改不了 Upgrade | 参数设置 websocket_native_enabled=false(或清空 websocket_url) |
| IWS 已修好 | 保持 true;清 session / 新开标签即可再试原生 |
正式 websocket_url | 仍须 https://…/iws/yj_goms/ws;协议纠正只是防配错 http |
诊断:__gomsWs.status() 看 nativeTransportEnabled、nativeTransportSkipped、transport。
SQL 键可与现有三项一并维护(前端未配键时默认启用原生);示例:
INSERT INTO sys_config (config_name, config_key, config_value, config_type, create_by, create_time, remark)
VALUES ('WebSocket原生传输', 'websocket_native_enabled', 'true', 'Y', 'admin', NOW(),
'已配 websocket_url 时是否尝试原生 websocket;IWS 未透传 Upgrade 时可设 false,仅 xhr');
4. Broker 与点对点路由(踩坑重点)
4.1 正确配置
registry.enableSimpleBroker("/topic", "/queue", "/mass");
registry.setUserDestinationPrefix("/user");
| 前缀 | 角色 |
|---|---|
/topic、/mass | 广播 / 群发 |
/queue | 点对点真实投递前缀 |
/user | 仅 UserDestination 逻辑前缀,禁止再写进 enableSimpleBroker |
4.2 双投 vs 丢消息
| 错误配置 | 现象 |
|---|---|
SimpleBroker 含 /user(LegacyApp) | convertAndSendToUser 双投(同一连接收两条) |
SimpleBroker 去掉 /user 且没有 /queue | 解析成 /pushTask-user{sessionId},无匹配前缀 → 静默丢消息 |
| 正确 | SimpleBroker 含 /queue;服务端发 /queue/pushTask;客户端订 /user/queue/pushTask |
4.3 投递路径
服务端: convertAndSendToUser(userId, "/queue/pushTask", ChatMessage)
↓ UserDestinationResolver
实际: /queue/pushTask-user{sessionId} ← 命中 SimpleBroker /queue
客户端: subscribe("/user/queue/pushTask") ← 按 CONNECT Principal 映射,勿拼 userId
5. 消息信封(ChatMessage)
对齐 LegacyApp,GOMS 增补 大类 category。
| 字段 | 类型 | 说明 |
|---|---|---|
id | String | 消息 id,服务端可生成 |
keyId | String | 业务实例隔离(如 jeId:123),多场同屏靠它过滤 |
category | String | 大类:NOTIFY | BIZ |
messageType | String | 小类:动作码(SYSTEM、EMERGENCY_PAUSE、WS_DIAG…) |
mess | String | 通知纯文本;BIZ 可选摘要 |
data | Object | BIZ 的 JSON 载荷;NOTIFY 可空 |
senderId / receiverId | String | 发送人 / 接收人(与 Principal.name 一致) |
sedTime | String | 发送时间,服务端填充 |
5.1 两大类行为
| 大类 | 载荷重心 | 前端默认 |
|---|---|---|
NOTIFY | mess 纯文本 | Element Notification(受 websocket_notify_enabled;WS_DIAG 始终弹) |
BIZ | data JSON | 不弹窗;$root 抛 goms-websocket-biz,业务页同步/刷新 |
缺省 category:有非空 mess 或 WS_DIAG → NOTIFY,否则 → BIZ(兼容旧推送)。
5.2 常量与构建(单一类,勿再拆)
- Java:
com.ruoyi.system.api.domain.ChatMessages(与ChatMessage同包)- 大类:
CATEGORY_NOTIFY/CATEGORY_BIZ - 小类:
TYPE_SYSTEM/TYPE_WS_DIAG/TYPE_EMERGENCY_*/TYPE_PAGE_REFRESH… - 工厂:
notifyOf/bizOf;发送前normalize
- 大类:
- JS:
utils/goms/wsMessage.js(镜像常量与解析)
ChatMessages.notifyOf(receiverId, ChatMessages.TYPE_SYSTEM, "纯文本");
ChatMessages.bizOf(receiverId, ChatMessages.TYPE_EMERGENCY_PAUSE, "jeId:123", dataMap);
6. 鉴权与接口矩阵
| 表面 | 鉴权 | 说明 |
|---|---|---|
SockJS /socket/** | 网关白名单 | 仅握手 |
| STOMP CONNECT | Token(CONNECT 头) | 拒无效 Token;不以客户端自报 userId 为准 |
GET /webSocket/status | 登录 | brokerUserCount(在线人数);onlineUsers / canDiagSend 仅诊断权限;另有 selfOnline/selfSessionCount(面板可不展示) |
POST /webSocket/ping、diagSend | goms:webSocket:diagSend | 浏览器自测 / 指定接收人 |
POST /webSocket/sendMsg、sendToUser | @InnerAuth | 仅 Feign;浏览器伪造 from-source 会被网关剥离 |
原则:浏览器测推送用 diagSend;业务推送用服务端 Feign,不要让普通登录 Token 直调任意广播入口。
7. 前端设计
| 模块 | 路径 | 职责 |
|---|---|---|
| 单例连接 | utils/WebSocketService.js | connect/disconnect/subscribe;websocket_url / 原生传输开关 / 本会话跳过;诊断事件环;__gomsWs |
| 消息约定 | utils/goms/wsMessage.js | category 解析、订阅常量、事件名 |
| Layout | layout/index.vue | 按 websocket_enabled 启停连接 |
| Navbar | layout/components/Navbar.vue | 订 /user/queue/pushTask,分流 Notification / 事件 |
| 监测浮层 | components/goms/WebSocketMonitor | 状态、在线人数、自测发送、回执 |
| 诊断 API | api/system/websocket.js | status / ping / diagSend |
7.1 监测浮层文案与指定用户(易混点)
| 展示 | 含义 | 说明 |
|---|---|---|
顶栏绿点 + WS 已连接/未连接 | 本浏览器 STOMP 连接态 | 本人是否连上,看这里即可 |
在线 N(M) | brokerUserCount + selfSessionCount | N=本机在线人数;M=本账号会话数(多标签 >1);标签用「在线」避免与固定宽 label 换行 |
selfOnline | status 仍返回 | 面板主文案不写「我在线」(与顶栏重复) |
指定用户推送:
- 控件:
el-autocomplete(可直接输入任意userId,也可点选onlineUsers) - 名单:
GET /webSocket/status的onlineUsers(须goms:webSocket:diagSend);约 8s 轮询,切到「指定用户」/输入框聚焦时立刻刷新 - 下拉挂 body:
popper-class的z-index须 > 浮层 4000(否则选项被挡住,看起来像「没数据」)
业务页推荐:
- 系统频道(已由 Navbar 订):听
$root的goms-websocket-biz/goms-websocket-notify - 独立 topic:
websocketService.subscribe('/topic/xxx', …)(少用,注意权限与在线范围)
8. 后端设计
| 模块 | 路径 | 职责 |
|---|---|---|
| Config | websocket/config/WebSocketConfig | 端点、Broker、线程池 |
| 拦截器 | websocket/interceptor/InboundChannelInterceptor | CONNECT 验 Token |
| 推送 | websocket/service/WebSocketPushService | isEnabled + send / sendToUser(normalize) |
| 诊断 | websocket/controller/WebSocketDiagController | status / ping / diagSend |
| 内部推送 | websocket/controller/WebSocketSendController | @InnerAuth send* |
| Feign | RemoteWebSocketService | 其它服务调用入口 |
| 模板推送 | SysPushModulesServiceImpl#pushWebSocket | 工作流等模板 → NOTIFY |
| SockJS 异常 | websocket/handler/SockJsExceptionResolver | 仅 /socket/**:warn + 空响应;不改公共 GlobalExceptionHandler |
sys_config:
websocket_enabled:总开关websocket_notify_enabled:是否弹 Notificationwebsocket_url:SockJS 独立入口(不经 iws-gateway REST);空则回退VUE_APP_BASE_API+ xhr/xdr;二级目录正式填https://{host}/iws/yj_goms/ws(§3.3)websocket_native_enabled:已配websocket_url时是否尝试原生websocket(默认 true);IWS 未透传 Upgrade 时可false(§3.4)
公共模块边界:SockJS 客户端断开写帧失败属常态噪音;收口放在 goms-system 本地 HandlerExceptionResolver(非 socket 返回 null 继续走若依全局处理),避免动 ruoyi-common-security。
9. 业务协同推荐模式(应急等)
用户 A 点「暂停」→ REST(带 Authorization)→ Service 落库
↓
ChatMessages.bizOf(..., EMERGENCY_PAUSE, keyId, data)
↓
sendToUser("/queue/pushTask", msg) 或 Feign
↓
用户 B/C 浏览器 ← BIZ 事件 ← 按 keyId + messageType 更新本地 UI
禁止:浏览器直调 sendToUser 伪装推送给他人。
10. 源码备份清单
目录:notes/java/webSocket/webSocket_demo/
webSocket_demo/
├── java/
│ ├── ChatMessage.java # 信封实体
│ ├── ChatMessages.java # 大类/小类常量 + notifyOf/bizOf/normalize
│ ├── RemoteWebSocketService.java
│ ├── config/ # WebSocketConfig / UserPrincipal
│ ├── interceptor/ # InboundChannelInterceptor
│ ├── service/ # WebSocketPushService
│ ├── controller/ # Diag / Send(InnerAuth)
│ └── handler/ # SockJsExceptionResolver(仅 goms-system)
├── js/
│ ├── WebSocketService.js
│ ├── wsMessage.js
│ └── websocket.js # api/system/websocket.js
└── vue/
└── WebSocketMonitor.vue
仓库内权威路径仍以 Goms-Cloud-springboot2 为准;本目录供笔记对照与离线查阅。
11. 运维与排障摘要
| 现象 | 排查 |
|---|---|
| 能连不能收 | SimpleBroker 是否含 /queue;发送是否 /queue/pushTask;订阅是否 /user/queue/pushTask |
| CONNECT 失败 | Token 是否过期;日志 WebSocket CONNECT 拒绝 |
| 收两条 | 是否又把 /user 写进 enableSimpleBroker |
| 自测无权限 | 角色是否勾选 goms:webSocket:diagSend |
| 指定用户「没名单」 | ① 有无 diag 权限(onlineUsers 才下发)② 联想下拉 z-index 是否被浮层挡住 ③ 是否已轮询/聚焦刷新 status |
| 自测无回执 | 先看顶栏是否 WS 已连接;diagSend 对离线目标会直接返回「未投递」 |
| xhr 写帧异常 | 客户端已离开时的常态噪音;看是否已由 SockJsExceptionResolver 降为 warn,勿当业务故障 |
满屏 /xhr 每 ~25s | 二级目录:主平台 /yj_goms/ 未透传 Upgrade,或未配 websocket_url;属 SockJS 降级,不是接口变慢(§3.3) |
invalid Upgrade header: null | 请求已到 goms-system,但中间反代剥掉了 Upgrade;优先查主平台 /yj_goms/,其次 GOMS /ws/ |
配了 websocket_url 仍连不上 | 正式是否用 https;GOMS /ws/ → :18093/socket/endpointWs/;endpointSource 是否为 sys_config.websocket_url |
控制台反复 ws:///wss:// 失败 | Upgrade 未通;临时:websocket_native_enabled=false,或等本会话 native_ws_skip 后只走 xhr(§3.4) |
nativeTransportSkipped=true 仍 xhr | 预期:本会话已判定原生不可用,或开关为 false;IWS 修好后清 session / 新标签再试 |
| 未配独立地址建连慢/失败 | 属预期:仅 xhr/xdr;勿强开原生 ws;或补齐两层 nginx + websocket_url |
项目侧使用说明见 GOMS 仓库:ai-doc/Wiki/WebSocket使用指南.md;运维/安全见 ai-doc/Wiki/WebSocket推送.md(含 websocket_url)。
主平台二级目录落地步骤见:主平台H5方式集成.md §六。
12. 参考对照
| 项目 | 说明 |
|---|---|
| LegacyApp | ChatMessage 字段、keyId 多场隔离、应急 messageType 分流;Broker 含 /user(双投);线上 /iws/BizApp/ws/ + 原生 websocket |
| GOMS | 去 /user 进 Broker、补 /queue;CONNECT 验 Token;category 大类(常量集中在 ChatMessages);InnerAuth 保护 send*;SockJS 异常本模块收口;可选 websocket_url 独立 WS 通道;原生失败本会话跳过(§3.4) |
| GOMS + 主平台二级目录 | 页面挂 /iws/yj_goms/;WS 同入口 .../iws/yj_goms/ws;主平台 /yj_goms/ 必须 Upgrade 透传 + GOMS /ws/ → 18093;详见 §3.3 |