1. 程式人生 > 其它 >淺析如何使用SimpMessagingTemplate在應用的任意地方將websocket訊息傳送給客戶端及遇到的迴圈依賴問題解決

淺析如何使用SimpMessagingTemplate在應用的任意地方將websocket訊息傳送給客戶端及遇到的迴圈依賴問題解決

  最近在做一個web terminal的需求,自己也寫了 demo ,使用 websocket + stomp 進行前後端通訊,其中遇到一個問題,就是我的前後端連線正常及 ssh 連線也正常了,但是我需要把 ssh 連線返回的資訊,再返回給客戶端。瞭解到使用 SimpMessagingTemplate ,但是在使用過程中卻遇到一個迴圈依賴的問題,所以這裡記錄一下。

一、SimpMessagingTemplate的作用

1、SimpMessagingTemplate可以在應用的任意地方傳送訊息。

  Spring的SimpMessagingTemplate能夠在應用的任何地方傳送訊息,甚至不必以首先接收一條訊息作為前提。使用SimpMessagingTemplate的最簡單方式是將它(或者其介面SimpMessageSendingOperations)自動裝配到所需的物件中。

2、SimpMessagingTemplate可以為指定的使用者傳送訊息。

  SimpMessagingTemplate還提供了convertAndSendToUser()方法。convertAndSendToUser()方法能夠讓我們給特定使用者傳送訊息。

simpMessageSendingOperations.convertAndSendToUser("1", "/message", "測試convertAndSendToUser");

stomp.subscribe('/users/1/message', function(message){ });

  客戶端接收一對一訊息的主題是"/users/"+usersId+"/message",這裡的使用者Id可以是一個普通字串,只要每個客戶端都使用自己的Id並且伺服器端知道每個使用者的Id就行了。

二、如何使用SimpMessagingTemplate將websocket訊息傳送給客戶端

1、引入 websocket 依賴

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

2、配置registry

@Configuration
@Slf4j
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { @Override public void registerStompEndpoints(StompEndpointRegistry registry ) { //路徑"/web-terminal"被註冊為STOMP端點,對外暴露,客戶端通過該路徑接入WebSocket服務 registry.addEndpoint("web-terminal").setAllowedOrigins("*"); } @Override public void configureMessageBroker(MessageBrokerRegistry config) { // 使用者可以訂閱來自以"/topic"為字首的訊息,客戶端只可以訂閱這個字首的主題 config.enableSimpleBroker("/topic/1024"); // 客戶端傳送過來的訊息,需要以"/xterm"為字首,再經過Broker轉發給響應的Controller config.setApplicationDestinationPrefixes("/xterm"); } }

3、使用SimpMessagingTemplate將websocket訊息傳送給客戶端

// 注入SimpMessagingTemplate 呼叫convertAndSend來推送訊息
// 1、注入
@Autowired
private SimpMessagingTemplate simpMessagingTemplate;

// 2、使用
//如果沒有資料來,執行緒會一直阻塞在這個地方等待資料。
while ((i = inputStream.read(buffer)) != -1) {
    System.out.println(new java.lang.String(Arrays.copyOfRange(buffer, 0, i)));
    template.convertAndSend("/topic/1024", new String(Arrays.copyOfRange(buffer, 0, i)));
}

三、解決SimpMessagingTemplate迴圈依賴問題

1、問題背景

  在這個類上注入 SocketService,SocketService 裡有我們的業務處理,在 SocketService 裡我們需要將訊息傳送給客戶端,所以需要注入 SimpMessagingTemplate, 但是一執行就會報錯 迴圈依賴 的問題。

  猜測可能是 WebSocketConfig 實現的 WebSocketMessageBrokerConfigurer 裡有實現 SimpMessagingTemplate,那麼 WebSocketConfig 依賴 SocketService,反過來 SocketService 又依賴 SimpMessagingTemplate,所以導致了迴圈依賴問題。

2、解決方案

  根據 spring 的描述,基於構造器的迴圈依賴是沒法解決的,官方文件都攤牌了,你想讓構造器注入支援迴圈依賴,是不存在的,不如把程式碼改了。所以解決方法就是可以改造一下程式碼。

  在 Controller 層去注入 SimpMessagingTemplate,然後將其作為引數傳給 Service層的 SocketService 裡的方法去呼叫。

// Controller 層注入及傳參
@Controller
@AllArgsConstructor
public class SocketController {
    private SocketService socketService;
    private SimpMessagingTemplate template;

    @MessageMapping("/msg")
    public void send(WebSSHData webSSHData) {
        System.out.println(webSSHData.getOperate());
        socketService.handlerMsg(webSSHData, template);
    }
}

// Service 層拿到引數去處理
public void handlerMsg(WebSSHData webSSHData, SimpMessagingTemplate template) {
    ......
    connectToSSH(sshConnectInfo, finalWebSSHData, template);
}

// 在 connectToSSH 裡使用
template.convertAndSend("/topic/1024", new String(Arrays.copyOfRange(buffer, 0, i)));

  這樣確實就解決了迴圈依賴的問題,訊息也順利的傳送給了客戶端。