当前位置:   article > 正文

基于Spring cloud 的gateway转发websocket提示404的问题_spring gateway 转发websocket报错404

spring gateway 转发websocket报错404

1.问题描述

最近工作中有场景需要长连接,代码开发完成,自测也没问题,但是上了微服务,通过网关后,就提示404找不到。

2.框架技术

项目用的是spring cloud 框架,eureka注册,gateway路由, websocket的长连接。

3.代码实现

websocket实现是javax.websocket的@ServerEndpoint 注解实现

  1. import lombok.extern.slf4j.Slf4j;
  2. import org.springframework.stereotype.Component;
  3. import javax.websocket.*;
  4. import javax.websocket.server.PathParam;
  5. import javax.websocket.server.ServerEndpoint;
  6. import java.util.Map;
  7. import java.util.concurrent.ConcurrentHashMap;
  8. import java.util.concurrent.CopyOnWriteArrayList;
  9. @Slf4j
  10. @Component
  11. @ServerEndpoint("/webSocket/record/{userId}")
  12. public class CheckRecordWebSocket {
  13. private String userId;
  14. private static CopyOnWriteArrayList<CheckRecordWebSocket> webSockets = new CopyOnWriteArrayList<>();
  15. private static ConcurrentHashMap<String, Session> sessionPool = new ConcurrentHashMap<>();
  16. @OnOpen
  17. public void onOpen(Session session, @PathParam("userId") String userId) {
  18. this.userId = userId;
  19. Map<String, String> paramMap = session.getPathParameters();
  20. String token = paramMap.get("authorization");
  21. log.info("token:{}", token);
  22. webSockets.add(this);
  23. sessionPool.put(userId, session);
  24. log.info("用户{}建立长连接.", userId);
  25. }
  26. @OnError
  27. public void onError(Session session, Throwable error) {
  28. }
  29. @OnClose
  30. public void OnClose(){
  31. sessionPool.remove(userId);
  32. webSockets.remove(this);
  33. log.info("用户{}断开长连接.", userId);
  34. }
  35. @OnMessage
  36. public void OnMessage(String message) {
  37. log.info("用户{}发来消息:{}", userId, message);
  38. }
  39. public void sendMessage(String userId, String message) {
  40. Session s = sessionPool.get(userId);
  41. if (s != null) {
  42. log.info("给用户{}发消息:{}", userId, message);
  43. s.getAsyncRemote().sendText(message);
  44. }
  45. }
  46. }

 直接通过应用的serverName:端口连接,没有问题

上环境后,通过网关调用就报错404找不到路径

一开始以为是网关路由配置问题,以下是路由配置代码:

  1. spring:
  2. cloud:
  3. gateway:
  4. discovery:
  5. locator:
  6. # 启用自动根据服务ID生成路由
  7. enabled: true
  8. # 设置路由的路径为小写的服务ID
  9. lower-case-service-id: true
  10. # 配置路由规则
  11. routes:
  12. - id: spd-pmc-ws
  13. uri: lb:ws://spd-pmc
  14. predicates:
  15. - Path=/spd-pmc/websocket/**
  16. filters:
  17. - StripPrefix=2
  18. - id: spd-pmc
  19. uri: lb://spd-pmc
  20. predicates:
  21. - Path=/spd-pmc/**
  22. filters:
  23. - StripPrefix=1
  24. - SwaggerHeaderFilter

直接调用的url是: ws://10.56.58.180:9306/webSocket/record/user1,应用的serviceName是spd-pmc。通过网关调用url: ws://10.56.58.180:9500/dmc/spd-pmc/websocket/webSocket/record/user1,其中10.56.58.180:9500/dmc是网关ip和路由,spc-pmc/websocket是配置的路由规则,经过路由id: spd-pmc-ws 的规则截取后,最终将调到 host:port/webSocketrecord/user1,理论上是一样的,但是报了404.

4.原因分析

后来分析url,我的应用端口是9306,网关端口是9301,但是url中是9500,说明请求到网关时应该还经过了至少一次转发,最终发现公司有用到nginx反向代理,1.3.13版本后的nginx转发支持websocket但是需要配置协议升级,经过查询,最终定位原因确实如此。nginx配置修改重启后,问题解决了。

5.解决办法

根据官方文档解释:从版本 1.3.13 开始, nginx 实现特殊操作模式 这允许在客户端和代理之间设置隧道 服务器(如果代理服务器返回了包含代码的响应) 101(交换协议), 客户端通过“升级”要求协议切换 标头。

公司使用的nginx是1.23版本。因此只需要修改我dmc请求的配置即可。在.conf文件中添加以下两段代码:

  1. map $http_upgrade $connection_upgrade {
  2. default upgrade;
  3. '' close;
  4. }
  5. ...
  6. proxy_http_version 1.1;
  7. proxy_set_header Upgrade $http_upgrade;
  8. proxy_set_header Connection $connection_upgrade;

6.总结

其实一开始定位问题方向就错了,gateway网关没有问题,经测试即使不单独配置lb:ws:spd-pmc的路由规则,也能通过原有的lb:spd-pmc规则进行路由。只是没有想到公司在前面还用到了nginx进行负载转发。因此漏掉了这里的排查。

7.补充

后来帮同事远程配置nginx支持websocket,完全按照步骤配置后,仍然不生效,后来检查发现前面有多层nginx转发, 因此,对于多层转发的情况,需要每层nginx都按照上述配置.

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/木道寻08/article/detail/858220
推荐阅读
相关标签