1. 程式人生 > 實用技巧 >springboot整合websocket實現訊息推送

springboot整合websocket實現訊息推送

​最近想起之前專案裡面的一個實現,是關於訂閱推送的,當粉絲訂閱了大V或者說作者釋出的內容被評論和點贊之後,對應的使用者會受到通知,當然,本身系統使用者並不多,所以直接採用的是輪訓的方式,由前端這邊定時向後端發起介面請求,獲取訊息推送,無疑呢,此種方式也可以解決問題,但是大部分請求基本無用,白白浪費頻寬和網路資源。

今天難得媳婦兒帶孩子回孃家了,下班到家也無事,便想著整理下前後端通過websocket實現訊息推送的方式。當然,前端這塊,主要採用原始的js通過websocket的方式獲取訊息,在微信公眾號和支付寶小程式開發中都有相應的onWebSocekt方式,有興趣的同學可以自行學習。

廢話不多說,開始啃程式碼。

1、pom.xml

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


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

2、application.yml

server:
  port: 8080


spring:
  thymeleaf:
    cache: false # 開發時關閉快取,不然沒法看到實時頁面
    mode: HTML # 用非嚴格的 HTML
    encoding: UTF-8
    servlet:
      content-type: text/html

3、WebSocketServer,實現前後端的長連線

package com.cookie.server;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; import javax.websocket.*; import javax.websocket.server.ServerEndpoint; import java.io.IOException; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.atomic.AtomicInteger; /** * @Author : cxq * @Date : 2020/8/31 15:50 */ // 前端通過該連線與後端保持互動 @ServerEndpoint(value = "/server") @Component public class WebSocketServer { @PostConstruct public void init() { System.out.println("websocket 載入"); } private static Logger log = LoggerFactory.getLogger(WebSocketServer.class); private static final AtomicInteger OnlineCount = new AtomicInteger(0); // concurrent包的執行緒安全Set,用來存放每個客戶端對應的Session物件。 private static CopyOnWriteArraySet<Session> SessionSet = new CopyOnWriteArraySet<Session>(); /** * 連線建立成功呼叫的方法 */ @OnOpen public void onOpen(Session session) { SessionSet.add(session); int cnt = OnlineCount.incrementAndGet(); // 線上數加1 log.info("有連線加入,當前連線數為:{}", cnt); SendMessage(session, "連線成功"); } /** * 連線關閉呼叫的方法 */ @OnClose public void onClose(Session session) { SessionSet.remove(session); int cnt = OnlineCount.decrementAndGet(); log.info("有連線關閉,當前連線數為:{}", cnt); } /** * 收到客戶端訊息後呼叫的方法 * * @param message * 客戶端傳送過來的訊息 */ @OnMessage public void onMessage(String message, Session session) { log.info("來自客戶端的訊息:{}",message); SendMessage(session, "收到訊息,訊息內容:"+message); } /** * 出現錯誤 * @param session * @param error */ @OnError public void onError(Session session, Throwable error) { log.error("發生錯誤:{},Session ID: {}",error.getMessage(),session.getId()); error.printStackTrace(); } /** * 傳送訊息,實踐表明,每次瀏覽器重新整理,session會發生變化。 * @param session * @param message */ public static void SendMessage(Session session, String message) { try { // session.getBasicRemote().sendText(String.format("%s (From Server,Session ID=%s)",message,session.getId())); session.getBasicRemote().sendText(message); } catch (IOException e) { log.error("傳送訊息出錯:{}", e.getMessage()); e.printStackTrace(); } } /** * 群發訊息 * @param message * @throws IOException */ public static void BroadCastInfo(String message) throws IOException { for (Session session : SessionSet) { if(session.isOpen()){ SendMessage(session, message); } } } /** * 指定Session傳送訊息 * @param sessionId * @param message * @throws IOException */ public static void SendMessage(String message,String sessionId) throws IOException { Session session = null; for (Session s : SessionSet) { if(s.getId().equals(sessionId)){ session = s; break; } } if(session!=null){ SendMessage(session, message); } else{ log.warn("沒有找到你指定ID的會話:{}",sessionId); } } }

4、WebSocketController,主要實現訊息群發和一對一發送

package com.cookie.controller;

import com.cookie.server.WebSocketServer;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.io.IOException;

/**
 * @Author : cxq
 * @Date : 2020/8/31 16:19
 */

@RestController
@RequestMapping("/webSocket")
public class WebSocketController {


    /**
     * 群發訊息內容
     *
     * @param message
     * @return
     */
    @RequestMapping(value = "/sendAll", method = RequestMethod.GET)
    public String sendAllMessage(@RequestParam(required = true) String message) {
        try {
            WebSocketServer.BroadCastInfo(message);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "ok";
    }

    /**
     * 指定會話ID發訊息
     *
     * @param message 訊息內容
     * @param id      連線會話ID
     * @return
     */
    @RequestMapping(value = "/sendOne", method = RequestMethod.GET)
    public String sendOneMessage(@RequestParam(required = true) String message,
                                 @RequestParam(required = true) String id) {
        try {
            WebSocketServer.SendMessage(message, id);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "ok";
    }
}

5、index.html接收後端傳送的訊息及展示

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>websocket測試</title>
    <script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
    <style type="text/css">
        h3,h4{
            text-align:center;
        }
    </style>
</head>
<body>

<h3>WebSocket測試,客戶端接收到的訊息如下:</h3>

<textarea id = "messageId" readonly="readonly" cols="150" rows="30" >

</textarea>


<script type="text/javascript">
    var socket;
    if (typeof (WebSocket) == "undefined") {
        console.log("遺憾:您的瀏覽器不支援WebSocket");
    } else {
        console.log("恭喜:您的瀏覽器支援WebSocket");
        //實現化WebSocket物件
        //指定要連線的伺服器地址與埠建立連線
        //注意ws、wss使用不同的埠。我使用自簽名的證書測試,
        //無法使用wss,瀏覽器開啟WebSocket時報錯
        //ws對應http、wss對應https。
        socket = new WebSocket("ws://localhost:8080/server");
        //連線開啟事件
        socket.onopen = function() {
            console.log("Socket 已開啟");
            socket.send("訊息傳送測試(From Client)");
        };
        //收到訊息事件
        socket.onmessage = function(msg) {
            $("#messageId").append(msg.data+ "\n");
            console.log(msg.data  );
        };
        //連線關閉事件
        socket.onclose = function() {
            console.log("Socket已關閉");
        };
        //發生了錯誤事件
        socket.onerror = function() {
            alert("Socket發生了錯誤");
        }
        //視窗關閉時,關閉連線
        window.unload=function() {
            socket.close();
        };
    }
</script>

</body>
</html>

6、啟動專案,訪問頁面看效果

訪問 localhost:8080,網頁展示如下

多看幾個頁面,頁面展示內容都一樣,同時後端控制檯列印訊息如下

接下來,使用postman,依次呼叫一對一訊息推送和群發訊息

一對一:http://localhost:8080/webSocket/sendOne?message="這是一條單個訊息"&id=1

頁面展示如下:

群發訊息:http://localhost:8080/webSocket/sendAll?message="這是一條群發訊息"

頁面展示如下: