小編在做微服務項目上遇到一個問題,正如標題描述一樣,但是百度好久未果(太多文章都是互相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 的端口即可
親測可用
有問題的同學可以留言討論