使用spring boot +WebSocket實現(後台主動)訊息推送

來源:互聯網
上載者:User

前言:使用此webscoket務必確保生產環境能相容/支援。使用此webscoket務必確保生產環境能相容/支援。使用此webscoket務必確保生產環境能相容/支援。主要是tomcat的相容與支援。

有個需求:APP使用者產生某個操作,需要讓後台管理系統部分人員感知(表現為一個頁面訊息)。

最早版本是後台管理系統輪訓,每隔一段時間輪訓一次,由於訊息重要,每隔幾秒就查一次。這樣做明顯很不雅。會消耗大量資源,並且大部分請求是沒有用的(查不到資料進來),很藍瘦。

後來,想著用訊息推送的方式來處理這個邏輯。使用者在app產生了目標操作,即產生一個訊息,推送給後台管理系統的對應使用者。

然後我就找各種資料,一開始同事推薦dwz,後來發現不太適用於目前的項目(也許能實現只是我不知道如何?)。

後來瞭解到WebSocket,網上看了很多文檔都是類似聊天室的情境,有些不同。在此,我主要側重介紹下 伺服器主動推送,由服務端來觸發。

WebSocket 主要能實現的情境:

1、網頁聊天室

2、伺服器訊息即時通知

WebSocket 使用方法應該有很多,在次介紹下使用  tomcat8+h5 環境下的實現。

ps:我自己的測試環境是tomcat7這樣寫是不行的。wang115032337《https://blog.csdn.net/wang115032337》這位朋友在他的環境下,tomcat7/8都可以用本文章的寫法,只不過需要去除WebSocketConfig類(有文章表示tomcat7和8對websocket的支援是不同的,本人未深入瞭解)

話不多說,直接上代碼,想深入瞭解WebSocket 的請查閱相關介紹。

1.pom

<dependency>            <groupId>org.springframework.boot</groupId>            <artifactId>spring-boot-starter-websocket</artifactId>        </dependency>
2. 使用@ServerEndpoint創立websocket endpoint(  wang115032337這位朋友在他的環境下加入@ServerEndpoint類會報錯,直接刪除了仍可用

@Configurationpublic class WebSocketConfig {    @Bean    public ServerEndpointExporter serverEndpointExporter() {        return new ServerEndpointExporter();    }}
3.具體實作類別 可自己選擇url要不要帶參數
package com.star.manager.service;import java.io.IOException;import java.util.concurrent.CopyOnWriteArraySet;import javax.websocket.OnClose;import javax.websocket.OnError;import javax.websocket.OnMessage;import javax.websocket.OnOpen;import javax.websocket.Session;import javax.websocket.server.ServerEndpoint;import lombok.extern.slf4j.Slf4j;import org.springframework.stereotype.Component;@Slf4j//@ServerEndpoint("/websocket/{user}")@ServerEndpoint(value = "/websocket")@Componentpublic class WebSocketServer {    //靜態變數,用來記錄當前線上串連數。應該把它設計成安全執行緒的。    private static int onlineCount = 0;    //concurrent包的安全執行緒Set,用來存放每個用戶端對應的MyWebSocket對象。    private static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<WebSocketServer>();    //與某個用戶端的串連會話,需要通過它來給用戶端發送資料    private Session session;    /**     * 串連建立成功調用的方法*/    @OnOpen    public void onOpen(Session session) {        this.session = session;        webSocketSet.add(this);     //加入set中        addOnlineCount();           //線上數加1        log.info("有新串連加入。當前線上人數為" + getOnlineCount());        try {         sendMessage("串連成功");        } catch (IOException e) {            log.error("websocket IO異常");        }    }////串連開啟時執行//@OnOpen//public void onOpen(@PathParam("user") String user, Session session) {//currentUser = user;//System.out.println("Connected ... " + session.getId());//}    /**     * 串連關閉調用的方法     */    @OnClose    public void onClose() {        webSocketSet.remove(this);  //從set中刪除        subOnlineCount();           //線上數減1        log.info("有一串連關閉。當前線上人數為" + getOnlineCount());    }    /**     * 收到用戶端訊息後調用的方法     *     * @param message 用戶端發送過來的訊息*/    @OnMessage    public void onMessage(String message, Session session) {    log.info("來自用戶端的訊息:" + message);        //群發訊息        for (WebSocketServer item : webSocketSet) {            try {                item.sendMessage(message);            } catch (IOException e) {                e.printStackTrace();            }        }    }/** *  * @param session * @param error */    @OnError    public void onError(Session session, Throwable error) {        log.error("發生錯誤");        error.printStackTrace();    }    public void sendMessage(String message) throws IOException {        this.session.getBasicRemote().sendText(message);    }    /**     * 群發自訂訊息     * */    public static void sendInfo(String message) throws IOException {    log.info(message);        for (WebSocketServer item : webSocketSet) {            try {                item.sendMessage(message);            } catch (IOException e) {                continue;            }        }    }    public static synchronized int getOnlineCount() {        return onlineCount;    }    public static synchronized void addOnlineCount() {        WebSocketServer.onlineCount++;    }    public static synchronized void subOnlineCount() {        WebSocketServer.onlineCount--;    }}

產生一個訊息:產生訊息情境有多種,http(s),定時任務,mq等,這貼一個httpq請求的controller代碼

 @RequestMapping(value="/pushVideoListToWeb",method=RequestMethod.POST,consumes = "application/json") public @ResponseBody Map<String,Object> pushVideoListToWeb(@RequestBody Map<String,Object> param) { Map<String,Object> result =new HashMap<String,Object>();  try { WebSocketServer.sendInfo("有新客戶呼入,sltAccountId:"+CommonUtils.getValue(param, "sltAccountId")); result.put("operationResult", true); }catch (IOException e) { result.put("operationResult", true); } return result; }


重要的地方我都加粗了,主要是這段,使用這個方法,可以實現伺服器主動推送。

    public void sendMessage(String message) throws IOException {        this.session.getBasicRemote().sendText(message);    }

4.js(html就不寫了,隨便找個能觸發這個js的就可以)

//socket = new WebSocket("ws://localhost:9094/starManager/websocket/張三");var socket;if(typeof(WebSocket) == "undefined") {console.log("您的瀏覽器不支援WebSocket");}else{console.log("您的瀏覽器支援WebSocket");//實現化WebSocket對象,指定要已連線的服務器地址與連接埠  建立串連//socket = new WebSocket("ws://localhost:9094/starManager/websocket/張三")
socket = new WebSocket("ws://localhost:9094/starManager/websocket");//開啟事件socket.onopen = function() {console.log("Socket 已開啟");//socket.send("這是來自用戶端的訊息" + location.href + new Date());};//獲得訊息事件socket.onmessage = function(msg) {console.log(msg.data);//發現訊息進入    調後台擷取getCallingList();};//關閉事件socket.onclose = function() {console.log("Socket已關閉");};//發生了錯誤事件socket.onerror = function() {alert("Socket發生了錯誤");} $(window).unload(function(){  socket.close();});//                            $("#btnSend").click(function() {//                            socket.send("這是來自用戶端的訊息" + location.href + new Date());//                            });////                            $("#btnClose").click(function() {//                            socket.close();//                            });}                                

簡單說說:

通過前端代碼 

socket = new WebSocket("ws://localhost:9094/starManager/websocket");

其中,starManager是工程名,/webscoket是訪問路徑名

建立串連,前端調用scoket.open() 會使後台在靜態成員變數webSocketSet裡面增加一個元素,相當於一個緩衝。後台服務調用sendMessage

(指定某個使用者,定向)或sendInfo(遍曆webSocketSet逐個發送,類似群發)方法,即可向已登入的用戶端推送訊息。

代碼就這麼多。我的用這些代碼就跑的起來。做的時候出現過頁面報404等錯誤,如果也是spring boot+h5,仔細核對下和我代碼有無區別,加配置 路徑是有ok,問題應該不大。


如果你恰好也有可以用WebSocket實現的類似情境,希望對你有協助。如有寫的不對或不夠好的地方,歡迎指正。









聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.