springcloud网关转发websocket


小编在做微服务项目上遇到一个问题,正如标题描述一样,但是百度好久未果(太多文章都是互相copy的)

小编决定自己写一下解决方案。。。

下面只贴部分代码,如需完整demo请上github获取

  1. 首先搭建 websocket 的服务端

 

1.1. 在 websocket 服务端的 pom 配置文件中需要有 websocket 的依赖(由于在pom中配置了统一版本管理,这里就不需要写版本了)

1 <dependency>
2     <groupId>org.springframework.boot</groupId>
3     <artifactId>spring-boot-starter-websocket</artifactId>
4 </dependency>

 

1.2. 略过启动类,配置 websocket 工具类;配置的说明,在代码中都有体现

 1 package com.beyond.config;
 2 
 3 
 4 import org.springframework.context.annotation.Configuration;
 5 import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
 6 import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
 7 import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
 8 
 9 @Configuration
10 // 此注解表示使用STOMP协议来传输基于消息代理的消息,此时可以在@Controller类中使用@MessageMapping
11 @EnableWebSocketMessageBroker
12 public class WebSocketAutoConfig implements WebSocketMessageBrokerConfigurer {
13     @Override
14     public void registerStompEndpoints(StompEndpointRegistry registry) {
15         //本实例前端 sockjs 请求路径为 http://localhost:8888/client/websocket/web/socket
16         registry.addEndpoint("/websocket/web/socket")         //websocket监听地址,sockjs连接地址(如果有配置项目路径【server.servlet.context-path】,需要加上)
17                 .setAllowedOrigins("*")         //允许跨域访问
18                 .withSockJS();                  //使用sockJS
19     }
20 }

 

1.3. 使用 websocket 【使用方式,只写了 请求回复定时推送 两种】

 

1.3.1. 请求回复模式【编写方式和普通的 http 的请求回复很像】

 1 package com.beyond.controller;
 2 
 3 import com.alibaba.fastjson.JSONObject;
 4 import lombok.extern.slf4j.Slf4j;
 5 import org.springframework.messaging.handler.annotation.MessageMapping;
 6 import org.springframework.messaging.handler.annotation.SendTo;
 7 import org.springframework.web.bind.annotation.RestController;
 8 
 9 @RestController
10 @Slf4j
11 public class FirstController {
12 
13     /**
14      * 此方法用于点对点,一发一答
15      *
16      * @param message
17      * @return
18      */
19     @SendTo("/websocket/read/response/message")//前端订阅消息的地址如果是这个,就会收到该方法的返回结果
20     //MessageMapping 注解可写在类上,和 RequestMapping 类似
21     @MessageMapping("/websocket1")//前端往这个地址推送数据,类似于 http 的方法请求
22     public String readMessage(String message) {
23         //todo something
24         log.info("message {}", message);
25         return JSONObject.parseObject(message).get("message") + " say: hello word.";
26     }
27 }

 

1.3.2. 定时推送模式【这里需要注入 SimpMessagingTemplate 对象进行操作】;这里引入一点题外知识【注入对象的方式】

 1 package com.beyond.task;
 2 
 3 import lombok.extern.slf4j.Slf4j;
 4 import org.springframework.messaging.simp.SimpMessagingTemplate;
 5 import org.springframework.scheduling.annotation.Async;
 6 import org.springframework.scheduling.annotation.Scheduled;
 7 import org.springframework.stereotype.Component;
 8 
 9 @Component
10 @Async
11 @Slf4j
12 public class WebsocketTask {
13 
14 
15 /*
16  *
17  * 方式一 Autowired 属性注入 (不推荐使用)
18 //    @Autowired
19 //    private SimpMessagingTemplate simpMessagingTemplate;
20  */
21 
22 /*
23  *
24  * 方式二 set注入,在属性多的情况下推荐使用
25 //    private SimpMessagingTemplate simpMessagingTemplate;
26 
27 //    @Autowired
28 //    public void setSimpMessagingTemplate(SimpMessagingTemplate simpMessagingTemplate) {
29 //        this.simpMessagingTemplate = simpMessagingTemplate;
30 //    }
31  */
32 
33     /**
34      * 方式三 构造函数注入,在属性少的情况下推荐使用
35      */
36     private final SimpMessagingTemplate simpMessagingTemplate;
37 
38     public WebsocketTask(SimpMessagingTemplate simpMessagingTemplate) {
39         this.simpMessagingTemplate = simpMessagingTemplate;
40     }
41 
42 
43     /**
44      * 此定时任务用于往 /websocket/send/response/message 订阅地址定时发送消息;发送消息需要依赖 SimpMessagingTemplate 消息模板
45      */
46     @Scheduled(cron = "*/5 * * * * ?")
47     public void roundSendMessage() {
48         //todo something
49         //向 /websocket/send/response/message 订阅地址发送消息 [hello word!!]
50         simpMessagingTemplate.convertAndSend("/websocket/send/response/message", "hello word!!");
51     }
52 }

 

  2. 现在就可以用前端代码测试了,这个代码是从百度上 copy 后改写的

 1 <!DOCTYPE html>
 2 <html>
 3 <head>
 4 <meta charset="UTF-8" />
 5 <title>Spring cloud gateway WebSocket</title>
 6 </head>
 7 <body>
 8     <div>
 9         <div>
10             <button id="connect" onclick="connect()">连接</button>
11             <button id="disconnect"  onclick="disconnect();">断开连接</button>
12         </div>
13         <div id="conversationDiv">
14             <label>输入消息</label> <input type="text" id="messgae" />
15             <button id="send" onclick="send();">发送</button>
16             <p id="response"></p>
17         </div>
18     </div>
19     <script src="sockjs.min.js"></script>
20     <script src="stomp.min.js"></script>
21     <script src="jquery.min.js"></script>
22     <script type="text/javascript">
23         var connectioned = false;
24 
25          function connect() {
26                 // websocket的连接地址,此值等于WebSocketMessageBrokerConfigurer中registry.addEndpoint("/websocket/web/socket").withSockJS()配置的地址;如果有项目前缀,需要加上
27                 //var socket = new SockJS('http://192.168.1.95:19999/server/websocket/web/socket');//网关websocket代理地址
28                 var socket = new SockJS('http://192.168.1.95:8888/server/websocket/web/socket');//子项目websocket地址
29                 stompClient = Stomp.over(socket);
30                 stompClient.connect({}, function(frame) {
31                     connectioned = true;
32                     console.log('Connected: ' + frame);
33                     // 客户端订阅消息的目的地址:此值等于BroadcastCtl中@SendToUser("/topic/getResponse")注解的里配置的值。这是请求的地址必须使用/user前缀
34                     stompClient.subscribe('/websocket/read/response/message', function(respnose){
35                         console.log("read -------------------");
36                         console.log(respnose.body);
37                     });
38                     
39                      stompClient.subscribe('/websocket/send/response/message', function(respnose){
40                         console.log("------------------- send");
41                         console.log(respnose.body);
42                     });
43                 });
44             }
45 
46 
47             function disconnect() {
48                 if (stompClient != null) {
49                     stompClient.disconnect();
50                 }
51                 connectioned = false;
52                 console.log("Disconnected");
53             }
54 
55             function send() {
56                 if(connectioned){
57                     var name = $('#messgae').val();
58                     console.log("message --- " +name);
59                     //客户端消息发送的目的:服务端使用BroadcastCtl中@MessageMapping("/websocket1")注解的方法来处理发送过来的消息;如果类上有配置 @MessageMapping 注解,请求路径需要拼接 [类上/方法上]
60                     stompClient.send("/websocket1", {}, JSON.stringify({ 'message': name }));
61                 }
62             }
63     </script>
64 </body>
65 </html>

 

测试结果就不上图了,亲测可用

 

  3. 网关配置

  先说明下,这里小编踩过坑,在这里要提醒各位同学,在配置 spring cloud gateway 的时候,必须要用 yml 格式的配置文件,否则不生效。但是不知道为什么,有知道的大神可以告诉我!

 

3.1. 网关的pom配置文件中需要引入 springcloud 的 gateway

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

 

3.2. 只需要配置路由转发规则即可,其他帖子上说要配置其他的,全部都是瞎扯

要使用 yml 格式的配置文件

properties 和 yml 配置文件可以共存【properties 优先级比 yml 高】

#必须使用 yml 格式配置文件,转发 socket 才会生效
spring:
  cloud:
    gateway:
      #路由转发
      routes:
        #转发web请求
        - id: customize-web
          #lb 代表从注册中心获取 serverId 来做负载请求
          uri: lb://server
          predicates:
            - Path=/server/websocket/web/socket/info/**
           #如果需要加访问前缀,转发是需要去掉;前缀加一层,StripPrefix 值+1
           #filters:
           # - StripPrefix=1
        #转发websocket
        - id: customize-websocket
          uri: lb:ws://server
          predicates:
            - Path=/server/websocket/web/socket/**

 

到这里就已经配置完成了,在刚刚那个前端实例里面,端口改成 gateway 的端口即可

亲测可用

有问题的同学可以留言讨论


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM