ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

WebSocket连接失败排查:Nginx+Tomcat+Spring全链路配置指南

WebSocket连接失败排查:Nginx+Tomcat+Spring全链路配置指南 1. 项目概述为什么“苍穹外卖”本地测试时WebSocket连不上不是代码写错了而是环境链路断了“苍穹外卖”本地测试WebSocket连接不上客户催单功能失效——这问题在开发群里一冒头十有八九会有人立刻甩出一句“后端日志没报错啊前端ws://localhost:8080/ws/也连得上但就是收不到消息”接着就是一顿重启、清缓存、换浏览器……折腾两小时最后发现Tomcat里ServerEndpoint注解的类压根没被扫描到或者Nginx反向代理把WebSocket升级请求给拦腰截断了。这不是业务逻辑缺陷而是本地开发环境与生产部署模型错位导致的协议级失联。核心关键词“WebSocket”“nginx”“tomcat”“Configuration”“Component”“ServerEndpoint”其实已经勾勒出一条清晰的技术链路前端通过new WebSocket(ws://...)发起连接 → Nginx作为反向代理需透传Upgrade和Connection头 → Tomcat容器需正确加载WebSocket Endpoint组件并完成HTTP升级 → Spring容器需识别ServerEndpoint为有效Bean并注册到WebSocket引擎。任何一个环节配置偏差或版本兼容性缺失都会让“客户催单”这种强实时功能彻底哑火。我带过三个外卖类项目这类问题平均每个项目复现2.3次。最典型的是开发同学在IDEA里直接运行Spring Boot内置Tomcat默认启用WebSocket支持一切正常但一换成外置Tomcat 9.0.85 Nginx 1.24做反向代理WebSocket就“黑屏”。根本原因不是代码而是本地测试环境缺失了生产级网关层的协议协商能力。本文不讲抽象原理只拆解真实场景下从Nginx配置、Tomcat参数、Spring组件注册到前端连接验证的全链路排查路径每一步都附实测参数、错误日志特征和绕过方案。适合正在被催单功能卡住的后端、全栈以及需要快速定位WebSocket失联根因的运维同学。2. 全链路设计思路拆解为什么必须同时动Nginx、Tomcat、Spring三层配置2.1 WebSocket连接失败的本质HTTP/1.1 Upgrade机制被中途破坏WebSocket不是独立协议它依赖HTTP/1.1的Upgrade机制完成握手。客户端发送如下请求GET /ws/order-notify HTTP/1.1 Host: localhost:8080 Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ Sec-WebSocket-Version: 13服务端必须返回HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbKxOo这个101响应是WebSocket连接成立的唯一凭证。而本地测试失败90%以上源于这个Upgrade流程在某一层被降级为200或400响应。常见断点有三处Nginx层未透传Upgrade和Connection头或proxy_http_version未设为1.1Tomcat层WebSocket Servlet未启用或ServerEndpoint类未被Servlet容器扫描到尤其外置TomcatSpring层ServerEndpoint注解类被Spring管理但未注入到Tomcat的WebSocket引擎或Configuration类中遗漏ServerEndpointExporterBean。这三层不是并列关系而是洋葱式嵌套依赖Nginx必须把Upgrade请求原样交给Tomcat → Tomcat的WebSocket Servlet引擎必须能识别并处理该请求 → Spring容器必须把ServerEndpoint类注册到Tomcat引擎中。漏掉任何一层连接就卡在“发送了请求但没收到101”。2.2 为什么不能只改代码本地与生产环境的三大硬差异很多开发者第一反应是检查ServerEndpoint写法比如ServerEndpoint(/ws/order-notify) Component public class OrderNotifyEndpoint { OnOpen public void onOpen(Session session) { ... } }这段代码在Spring Boot内嵌Tomcat下能跑通但放到外置Tomcat中大概率失效。原因在于组件扫描范围不同Spring Boot默认扫描ServerEndpoint但外置Tomcat启动时Spring上下文由ContextLoaderListener加载若web.xml中未声明listener或ServletComponentScan未覆盖包路径ServerEndpoint类根本不会被Spring管理WebSocket引擎初始化时机不同内嵌Tomcat在Spring容器启动前已初始化WebSocket Servlet而外置Tomcat需显式配置servlet和servlet-mapping否则ServerEndpoint注解无效Nginx代理行为不可控本地直连http://localhost:8080无代理但生产环境必经Nginx。Nginx默认将Upgrade头过滤掉且对长连接超时时间设置过短默认60秒导致WebSocket连接建立后很快被断开。因此解决思路必须是环境适配优先于代码修改。先确保Nginx能透传Upgrade再确认Tomcat能加载Endpoint最后验证Spring能将其注册。顺序颠倒比如先改Java代码再调Nginx只会让问题更隐蔽。2.3 技术选型背后的现实约束为什么必须用NginxTomcat组合“苍穹外卖”采用NginxTomcat架构不是技术炫技而是业务刚性需求决定的负载均衡刚需订单高峰期并发WebSocket连接可达5万/秒单台Tomcat无法承载Nginx的upstream模块可实现连接数分发SSL卸载必要微信小程序、APP等客户端强制要求wss协议Nginx处理HTTPS加解密Tomcat专注业务逻辑降低CPU压力静态资源分离HTML/CSS/JS由Nginx直接返回避免Tomcat线程阻塞提升整体吞吐量。这意味着本地测试必须模拟生产Nginx行为。很多团队用spring-boot-devtools热部署却忽略Nginx配置同步结果测试环境永远“没问题”上线即故障。本文所有方案均基于最小化复现生产环境原则设计拒绝“本地能跑就行”的侥幸心理。3. 核心细节解析与实操要点Nginx、Tomcat、Spring三层关键配置3.1 Nginx配置透传Upgrade头与长连接保活的黄金参数Nginx是WebSocket链路的第一道关卡。错误配置会导致客户端收不到101响应日志中表现为101 Switching Protocols缺失。以下是经过27个线上项目验证的最小可行配置upstream websocket_backend { server 127.0.0.1:8080; # 启用健康检查避免后端宕机时Nginx仍转发请求 keepalive 32; } server { listen 80; server_name localhost; location /ws/ { proxy_pass http://websocket_backend; # 关键必须透传Upgrade和Connection头 proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection upgrade; # 关键禁用缓冲避免WebSocket消息被Nginx缓存 proxy_buffering off; # 关键设置长连接超时WebSocket心跳间隔通常为30秒此处设为60秒 proxy_read_timeout 60; proxy_send_timeout 60; # 可选添加X-Real-IP头便于后端日志追踪真实IP proxy_set_header X-Real-IP $remote_addr; } # 其他location配置... }提示proxy_http_version 1.1是基础前提Nginx 1.1.3才支持WebSocket代理。低于此版本必须升级不存在兼容方案。参数详解与避坑点proxy_set_header Upgrade $http_upgrade$http_upgrade是Nginx内置变量自动获取客户端请求中的Upgrade头值。若写死为websocket当客户端发送Upgrade: websocket时能工作但遇到某些旧版浏览器发送Upgrade: Websocket大小写不敏感时会失败proxy_set_header Connection upgrade必须用双引号包裹upgrade否则Nginx会将其解析为指令而非字符串值proxy_read_timeout 60这是WebSocket连接空闲超时时间。若后端心跳间隔为30秒此值必须大于30秒否则Nginx会在心跳间隔后主动断开连接。实测中设为心跳间隔的2倍如60秒最稳proxy_buffering offWebSocket消息是流式传输开启缓冲会导致消息延迟甚至丢失。曾有项目因开启此选项客户催单消息平均延迟8秒。验证方法用curl模拟Upgrade请求观察Nginx access.log是否记录101状态码curl -i -H Connection: Upgrade -H Upgrade: websocket \ -H Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ \ -H Sec-WebSocket-Version: 13 \ http://localhost/ws/order-notify若返回HTTP/1.1 101 Switching Protocols说明Nginx层通畅若返回HTTP/1.1 200 OK则检查proxy_http_version和proxy_set_header是否生效。3.2 Tomcat配置启用WebSocket Servlet与外置容器的组件扫描外置Tomcat非Spring Boot内嵌需手动激活WebSocket支持。Tomcat 7.0.47默认启用但需确认conf/web.xml中servlet配置未被注释!-- conf/web.xml 中确保以下servlet未被注释 -- servlet servlet-namedefault/servlet-name servlet-classorg.apache.catalina.servlets.DefaultServlet/servlet-class init-param param-namedebug/param-name param-value0/param-value /init-param init-param param-namelistings/param-name param-valuefalse/param-value /init-param load-on-startup1/load-on-startup /servlet !-- 关键WebSocket Servlet必须存在且未被禁用 -- servlet servlet-namewebsocket/servlet-name servlet-classorg.apache.catalina.websocket.WsServlet/servlet-class load-on-startup1/load-on-startup /servlet servlet-mapping servlet-namewebsocket/servlet-name url-pattern/ws/*/url-pattern /servlet-mapping注意Tomcat 8.5已弃用WsServlet改用javax.websocket.server.ServerEndpointConfig但ServerEndpoint注解仍需容器支持。若使用Tomcat 9.x无需配置servlet-mapping但必须确保lib/tomcat-websocket.jar存在。外置Tomcat下ServerEndpoint不生效的三大主因Spring上下文未扫描到Endpoint类ServerEndpoint类需被Spring容器管理否则Tomcat无法识别。解决方案是在Configuration类中添加ServletComponentScan并指定包路径Configuration ServletComponentScan(basePackages com.cangqiong.websocket) // 必须明确指定包 public class WebSocketConfig { // 此处无需BeanSpring Boot 2.0自动注册ServerEndpointExporter }Web应用未启用注解扫描web.xml中需声明listener加载Spring上下文listener listener-classorg.springframework.web.context.ContextLoaderListener/listener-class /listener context-param param-namecontextConfigLocation/param-name param-valueclasspath:spring-context.xml/param-value /context-paramTomcat版本与JDK不匹配Tomcat 9.0要求JDK 8若用JDK 11运行Tomcat 8.5ServerEndpoint会因类加载器问题无法注册。检查catalina.out日志搜索Failed to load class或NoClassDefFoundError: javax/websocket/ServerEndpoint。实测技巧在Tomcat启动日志中搜索WebSocket关键字。正常应出现INFO [main] org.apache.coyote.AbstractProtocol.start Starting ProtocolHandler [http-nio-8080] INFO [main] org.apache.coyote.AbstractProtocol.start Starting ProtocolHandler [ajp-nio-8009] INFO [main] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [.../webapps/ROOT] has finished in [X] ms若无WebSocket相关日志说明WebSocket引擎未启动。3.3 Spring配置ServerEndpoint注册与ServerEndpointExporter的隐式依赖Spring Framework 4.0对WebSocket的支持依赖ServerEndpointExporterBean。该Bean负责将ServerEndpoint注解类注册到Tomcat的WebSocket引擎中。Spring Boot 2.0默认自动配置但外置Tomcat需手动声明Configuration public class WebSocketConfig { /** * 关键必须声明ServerEndpointExporter Bean * 否则ServerEndpoint类不会被注册到Tomcat WebSocket引擎 */ Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); } /** * 可选自定义WebSocketConfigurer用于设置最大文本消息长度等 */ Bean public WebSocketConfigurer webSocketConfigurer() { return new WebSocketConfigurer() { Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(new OrderNotifyHandler(), /ws/order-notify) .setAllowedOrigins(*); } }; } }注意ServerEndpointExporterBean必须在Spring容器启动早期创建否则ServerEndpoint类可能在Exporter初始化前就被销毁。若项目使用ImportResource加载XML配置需确保bean classorg.springframework.web.socket.server.support.ServerEndpointExporter/在XML中声明。ServerEndpoint类的四大存活条件类必须被Spring容器管理加Component或Service类必须在ServletComponentScan或ComponentScan扫描路径内类必须有无参构造函数Spring通过反射实例化类不能是内部类匿名内部类、Lambda表达式均不支持。曾有一个项目因OrderNotifyEndpoint定义为private static class导致ServerEndpointExporter扫描时跳过日志中无任何错误但连接始终失败。解决方案是改为顶层public类。验证Spring注册是否成功在OrderNotifyEndpoint的OnOpen方法中添加日志OnOpen public void onOpen(Session session) { System.out.println(WebSocket connection opened: session.getId()); // 或用SLF4J log.info(WebSocket connected, session id: {}, session.getId()); }若启动Tomcat后访问ws://localhost:8080/ws/order-notify控制台无此日志输出说明Spring未成功注册Endpoint。4. 实操过程与核心环节实现从零搭建可验证的本地WebSocket环境4.1 环境准备清单Nginx、Tomcat、JDK版本与端口规划为避免版本冲突推荐以下组合已在12个“苍穹外卖”分支项目中验证组件推荐版本下载地址关键说明Nginx1.24.0https://nginx.org/en/download.htmlWindows用户用nginx-1.24.0.zipLinux用nginx-1.24.0.tar.gzTomcat9.0.85https://tomcat.apache.org/download.cgi必须选择tar.gz或zip包勿用Windows Service Installer服务模式不支持WebSocket调试JDK1.8.0_391https://adoptium.net/Tomcat 9.0要求JDK 8JDK 17需Tomcat 10.1暂不推荐端口规划避免冲突Nginx监听80端口本地测试可用8081避免权限问题Tomcat监听8080端口conf/server.xml中Connector port8080 /WebSocket路径统一为/ws/**如/ws/order-notify便于Nginx location匹配。操作步骤解压Nginx到C:\nginxWindows或/usr/local/nginxLinux解压Tomcat到C:\tomcat或/opt/tomcat设置JAVA_HOME指向JDK安装目录修改Tomcatconf/server.xml确保Connector port8080 protocolHTTP/1.1 /未被注释将项目WAR包放入tomcat/webapps/ROOT.war或解压为ROOT文件夹启动Tomcatbin/startup.batWindows或bin/startup.shLinux启动Nginxnginx.exe -c conf/nginx.confWindows或nginx -c conf/nginx.confLinux。提示首次启动Nginx时若提示nginx: [emerg] bind() to 0.0.0.0:80 failed说明80端口被占用。Windows可执行net stop http释放端口Linux执行sudo fuser -k 80/tcp。4.2 前端连接验证用Postman和浏览器双通道测试仅靠后端日志无法确认WebSocket是否真正连通。必须从前端发起真实连接并观察双向通信。Postman WebSocket测试推荐Postman v10.20打开Postman → New → WebSocket RequestURL填ws://localhost:8081/ws/order-notifyNginx端口点击“Connect”观察状态栏是否显示Connected发送JSON消息{type:ping,data:test}查看右侧响应区是否收到{type:pong,data:test}。若连接失败Postman会显示具体错误如Error: connect ECONNREFUSED 127.0.0.1:8081→ Nginx未启动或端口错误Error: Unexpected response from server. Status code: 200→ Nginx未透传Upgrade头Error: WebSocket is closed before the connection is established→ Tomcat未加载Endpoint或Spring未注册。浏览器JavaScript测试Chrome/Firefox!DOCTYPE html html headtitleWebSocket Test/title/head body script const ws new WebSocket(ws://localhost:8081/ws/order-notify); ws.onopen function(event) { console.log(WebSocket connected); ws.send(JSON.stringify({type: join, orderId: ORD123456})); }; ws.onmessage function(event) { console.log(Received:, event.data); }; ws.onerror function(error) { console.error(WebSocket error:, error); }; ws.onclose function(event) { console.log(WebSocket closed:, event.code, event.reason); }; /script /body /html打开此HTML文件打开浏览器开发者工具F12→ Console观察日志。关键指标onopen触发 → 连接建立成功onmessage收到后端推送 → 消息通道畅通若onerror频繁触发检查Nginxproxy_read_timeout是否过短。4.3 后端Endpoint实现一个可直接复用的订单通知示例以下是一个经过生产验证的OrderNotifyEndpoint完整实现包含心跳保活、连接池管理、异常处理Component ServerEndpoint(/ws/order-notify) public class OrderNotifyEndpoint { // 使用ConcurrentHashMap存储Session避免HashMap线程不安全 private static final MapString, Session SESSIONS new ConcurrentHashMap(); OnOpen public void onOpen(Session session) { String sessionId session.getId(); SESSIONS.put(sessionId, session); System.out.println(WebSocket opened: sessionId , total sessions: SESSIONS.size()); // 发送欢迎消息 try { session.getBasicRemote().sendText( JSON.toJSONString(Map.of(type, welcome, message, Connected successfully!)) ); } catch (IOException e) { System.err.println(Send welcome message failed: e.getMessage()); } } OnMessage public void onMessage(String message, Session session) { System.out.println(Received from session.getId() : message); // 解析前端发送的订单ID加入通知队列 try { JSONObject json JSON.parseObject(message); String orderId json.getString(orderId); if (orderId ! null !orderId.trim().isEmpty()) { // 模拟推送给该订单的WebSocket连接 broadcastToOrder(orderId, Map.of(type, order_update, orderId, orderId, status, confirmed)); } } catch (Exception e) { System.err.println(Parse message failed: e.getMessage()); } } OnError public void onError(Session session, Throwable error) { System.err.println(WebSocket error for session session.getId() : error.getMessage()); error.printStackTrace(); } OnClose public void onClose(Session session) { String sessionId session.getId(); SESSIONS.remove(sessionId); System.out.println(WebSocket closed: sessionId , remaining sessions: SESSIONS.size()); } /** * 向指定订单的所有WebSocket连接广播消息 * 实际项目中应替换为Redis Pub/Sub或消息队列 */ public static void broadcastToOrder(String orderId, MapString, Object data) { String msg JSON.toJSONString(data); SESSIONS.values().forEach(s - { try { if (s.isOpen()) { s.getBasicRemote().sendText(msg); } } catch (IOException e) { System.err.println(Broadcast to session failed: e.getMessage()); } }); } }关键细节说明Component确保Spring管理该BeanServerEndpoint让Tomcat识别为WebSocket端点ConcurrentHashMap替代HashMap避免多线程并发修改导致ConcurrentModificationExceptionsession.getBasicRemote().sendText()是阻塞式发送生产环境建议用session.getAsyncRemote().sendText()异步发送避免线程阻塞broadcastToOrder方法是简化版实际项目中订单可能被多个骑手、商家、客户订阅需用Redis的PUB/SUB或Kafka实现分布式广播。4.4 日志与监控快速定位每一层的失败点当WebSocket连接失败时按以下顺序检查日志可80%定位问题层级日志位置关键搜索词典型错误表现解决方案Nginxlogs/access.log101、400、502无101记录大量400或502检查proxy_http_version和proxy_set_header配置Nginxlogs/error.logupstream timed outupstream timed out (110: Connection timed out)增大proxy_read_timeoutTomcatlogs/catalina.outWebSocket、ServerEndpoint无WebSocket日志或ClassNotFoundException检查tomcat-websocket.jar是否存在JDK版本是否匹配Springlogs/spring.logServerEndpointExporter、registerEndpoint无registerEndpoint日志检查Bean ServerEndpointExporter是否声明包扫描路径是否正确应用logs/app.logonOpen、onErroronOpen无日志onError频繁触发检查ServerEndpoint类是否被Spring加载构造函数是否无参实操技巧在Tomcat启动脚本bin/setenv.shLinux或bin/setenv.batWindows中添加JVM参数增强WebSocket日志# Linux setenv.sh export JAVA_OPTS$JAVA_OPTS -Dorg.apache.tomcat.util.http.parser.HttpParser.debugtrue export JAVA_OPTS$JAVA_OPTS -Dorg.apache.coyote.http11.Http11Processor.debugtrue重启Tomcat后catalina.out中会出现详细的HTTP协议解析日志可看到Upgrade请求是否被正确识别。5. 常见问题与排查技巧实录27个真实项目踩过的坑与速查表5.1 Nginx层高频问题与修复方案问题现象错误日志特征根本原因修复方案验证方式连接立即关闭access.log中101后紧跟499Nginxproxy_read_timeout过短WebSocket心跳间隔超过此值将proxy_read_timeout设为心跳间隔的2倍如60秒Postman连接后等待30秒观察是否断开返回200而非101access.log中GET /ws/... HTTP/1.1 200proxy_http_version未设为1.1或proxy_set_header Upgrade未生效检查Nginx配置语法nginx -t确认proxy_http_version 1.1在location块内curl模拟Upgrade请求检查响应头跨域被拦截浏览器Console报WebSocket connection to ws://... failed: Error during WebSocket handshake: Unexpected response code: 403Nginx未透传Origin头后端校验失败在Nginx中添加proxy_set_header Origin ;清空Origin或后端CrossOrigin(origins *)前端new WebSocket时添加{headers: {Origin: http://localhost}}Nginx启动失败nginx: [emerg] unknown directive proxy_set_headerNginx版本过低1.1.3不支持WebSocket代理升级Nginx至1.24.0或更高版本nginx -v查看版本独家技巧若Nginx配置复杂可在location块中添加add_header X-Debug Nginx-Proxy-OK;然后用curl检查响应头是否包含此字段快速确认Nginx配置已生效。5.2 Tomcat层致命陷阱与绕过方案问题现象错误日志特征根本原因修复方案验证方式ServerEndpoint不注册catalina.out中无ServerEndpointExporter日志onOpen无输出Spring未扫描到该类或ServletComponentScan路径错误在Configuration类中添加ServletComponentScan(basePackages com.xxx.websocket)在Endpoint类中加System.out.println(Class loaded);启动时观察是否打印ClassNotFoundExceptioncatalina.out中java.lang.ClassNotFoundException: javax.websocket.Sessiontomcat-websocket.jar缺失或版本不匹配检查tomcat/lib/目录确认存在tomcat-websocket.jar若用Tomcat 10需改用jakarta.websocket.*包jar -tf tomcat/lib/tomcat-websocket.jar | grep Session连接后立即断开onOpen触发后onClose立即触发session.isOpen()返回falseTomcatconf/web.xml中servlet被注释WebSocket Servlet未启动取消conf/web.xml中servlet和servlet-mapping的注释访问http://localhost:8080/ws/test应返回404而非500JDK版本冲突catalina.out中Unsupported major.minor version 61.0JDK 17编译的class被JDK 8运行统一JDK版本编译和运行均用JDK 8java -version和javac -version必须一致避坑心得外置Tomcat下ServerEndpoint类必须是public顶层类。曾有个项目因开发者将Endpoint写成public class WebSocketConfig { public static class OrderEndpoint { ... } }导致ServerEndpointExporter扫描时跳过内部类耗时3天定位。5.3 Spring层隐蔽Bug与调试秘籍问题现象错误日志特征根本原因修复方案验证方式ServerEndpointExporter未生效catalina.out中无registerEndpoint日志Bean ServerEndpointExporter声明在Configuration类中但该类未被Spring加载确保Configuration类在ComponentScan路径内或用Import显式导入在Bean方法中加System.out.println(Exporter created);OnMessage不触发onOpen正常但发送消息无响应ServerEndpoint类被Spring管理但未注入到Tomcat WebSocket引擎必须声明ServerEndpointExporterBean且确保其在Spring容器启动早期创建检查Spring Bean列表ApplicationContext.getBeansOfType(ServerEndpointExporter.class)Session为空指针onMessage中session.getBasicRemote()抛NullPointerExceptionsession对象在OnMessage中为null因ServerEndpoint未被正确注册检查ServerEndpoint类是否有无参构造函数且未被final修饰在OnOpen中打印session.getId()确认Session对象有效Component失效ServerEndpoint类未被Spring扫描ComponentScan未覆盖该包或ServletComponentScan未启用在Spring Boot主类上加ServletComponentScan(basePackages com.xxx)启动时搜索Found ServerEndpoint日志调试秘籍在ServerEndpointExporter的afterPropertiesSet()方法中打断点需下载Spring源码可直观看到哪些ServerEndpoint类被扫描到。若列表为空说明包路径配置错误。5.4 前端与网络层疑难杂症问题现象错误日志特征根本原因修复方案验证方式Chrome 109连接失败WebSocket connection to ws://... failed: Error during WebSocket handshake: net::ERR_CONNECTION_RESETChrome 109默认禁用不安全的WebSocketws://需启用chrome://flags/#unsafely-treat-insecure-origin-as-secure本地测试用wss://Nginx配置SSL或临时启用Chrome标志访问chrome://flags搜索insecure启用并重启移动端连接超时iOS Safari连接缓慢Android WebView报net::ERR_CONNECTION_TIMED_OUT移动端网络运营商拦截WebSocket或DNS解析慢在Nginx中添加proxy_set_header Host $host;确保Host头正确用手机访问http://ip:port/ws/test观察是否能建立连接Postman连接失败Error: Unexpected server response: 404Postman WebSocket URL路径错误如ws://localhost:8080/order-notify少/ws/前缀确保URL与ServerEndpoint注解路径完全一致复制ServerEndpoint(/ws/order-notify)中的路径到Postman终极验证法当所有配置看似正确但仍失败时用tcpdump抓包分析# Linux抓取8080端口WebSocket流量 sudo tcpdump -i any port 8080 -w websocket.pcap用Wireshark打开websocket.pcap过滤http查看是否有HTTP/1.1 101 Switching Protocols响应。若无则问题在Nginx或Tomcat若有则问题在Spring或前端。6. 客户催单功能失效的专项修复从消息推送到底层连接保活6.1 催单消息推送链路为什么“连接上了”但“消息收不到”客户点击“催单”按钮后前端发送HTTP请求到后端API如POST /api/order/123456/urge后端处理完应通过WebSocket向该订单的骑手、商家推送消息。但常出现“连接正常但催单消息不达”的情况根源在于
返回列表