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

1.问题描述

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

2.框架技术

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

3.代码实现

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

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.websocket.*;
import javax.websocket.server.PathParam;
import javax.websocket.server.ServerEndpoint;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;

@Slf4j
@Component
@ServerEndpoint("/webSocket/record/{userId}")
public class CheckRecordWebSocket {
    private String userId;

    private static CopyOnWriteArrayList<CheckRecordWebSocket> webSockets = new CopyOnWriteArrayList<>();
    private static ConcurrentHashMap<String, Session> sessionPool = new ConcurrentHashMap<>();

    @OnOpen
    public void onOpen(Session session, @PathParam("userId") String userId) {
        this.userId = userId;
        Map<String, String> paramMap = session.getPathParameters();

        String token = paramMap.get("authorization");
        log.info("token:{}", token);
        webSockets.add(this);
        sessionPool.put(userId, session);
        log.info("用户{}建立长连接.", userId);
    }

    @OnError
    public void onError(Session session, Throwable error) {

    }

    @OnClose
    public void OnClose(){
        sessionPool.remove(userId);
        webSockets.remove(this);
        log.info("用户{}断开长连接.", userId);
    }

    @OnMessage
    public void OnMessage(String message) {
        log.info("用户{}发来消息:{}", userId, message);
    }

    public void sendMessage(String userId, String message) {
        Session s = sessionPool.get(userId);
        if (s != null) {
            log.info("给用户{}发消息:{}", userId, message);
            s.getAsyncRemote().sendText(message);
        }
    }
}

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

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

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

spring:
  cloud:
    gateway:
      discovery:
        locator:
          # 启用自动根据服务ID生成路由
          enabled: true
          # 设置路由的路径为小写的服务ID
          lower-case-service-id: true
      # 配置路由规则
      routes:
        - id: spd-pmc-ws
          uri: lb:ws://spd-pmc
          predicates:
            - Path=/spd-pmc/websocket/**
          filters:
            - StripPrefix=2
        - id: spd-pmc
          uri: lb://spd-pmc
          predicates:
            - Path=/spd-pmc/**
          filters:
            - StripPrefix=1
            - 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文件中添加以下两段代码:

map $http_upgrade $connection_upgrade {
        default upgrade;
        ''      close;
    }

...

proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection $connection_upgrade;

6.总结

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

7.补充

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

👍如果对你有帮助,请给我一个免费的点赞以示鼓励
欢迎各位🔎点赞👍评论收藏⭐️

  • 6
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
当使用Spring Cloud Gateway转发WebSocket请求时,需要进行一些特殊配置。下面是一个简单的教程,演示了如何配置Spring Cloud Gateway转发WebSocket请求。 1. 首先,确保你已经有一个基本的Spring Cloud Gateway项目,并且已经添加了必要的依赖。 2. 在你的Gateway配置类中,添加一个`@Bean`方法来创建一个`WebSocketHandlerMapping`的实例。这个实例将用于将WebSocket请求转发到相应的目标服务。 ```java @Configuration public class GatewayConfig { @Bean public WebSocketHandlerMapping webSocketHandlerMapping() { Map<String, WebSocketHandler> handlerMap = new HashMap<>(); // 添加需要转发WebSocket处理器 handlerMap.put("/ws-endpoint", new MyWebSocketHandler()); // 创建WebSocketHandlerMapping实例,并设置handlerMap SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping(); mapping.setOrder(Ordered.HIGHEST_PRECEDENCE); mapping.setUrlMap(handlerMap); return mapping; } } ``` 请替换`MyWebSocketHandler`为你自己实现的WebSocket处理器。 3. 在Gateway配置文件中,添加以下配置来启用WebSocket支持和设置路由规则。 ```yaml spring: cloud: gateway: routes: - id: websocket_route uri: lb://websocket-service predicates: - Path=/ws-endpoint/** filters: - WebSocket=ws-endpoint ``` 这里的`websocket_route`是路由的ID,`lb://websocket-service`是目标WebSocket服务的地址,`/ws-endpoint/**`是需要转发WebSocket请求路径。请根据你的实际情况进行修改。 4. 在你的WebSocket服务项目中,实现一个WebSocket处理器作为目标处理器。例如: ```java @Component public class MyWebSocketHandler extends TextWebSocketHandler { @Override protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception { // 处理WebSocket消息 session.sendMessage(new TextMessage("Hello, WebSocket!")); } } ``` 这里的`handleTextMessage`方法用于处理收到的WebSocket消息。 5. 最后,启动你的Gateway服务和WebSocket服务,并尝试发送一个WebSocket消息到`/ws-endpoint`路径。如果一切配置正确,Gateway应该会将该消息转发WebSocket服务,并返回处理结果。 希望这个简单的教程能帮助到你实现Spring Cloud GatewayWebSocket转发功能。如有任何疑问,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值