SpringBoot+WebSocket實現(xiàn)消息推送功能
背景
項目中經(jīng)常會用到消息推送功能,關(guān)于推送技術(shù)的實現(xiàn),我們通常會聯(lián)想到輪詢、comet長連接技術(shù),雖然這些技術(shù)能夠?qū)崿F(xiàn),但是需要反復(fù)連接,對于服務(wù)資源消耗過大,隨著技術(shù)的發(fā)展,HtML5定義了WebSocket協(xié)議,能更好的節(jié)省服務(wù)器資源和帶寬,并且能夠更實時地進行通訊。本文將介紹如何采用websocket實現(xiàn)消息推送。
WebSocket簡介
WebSocket協(xié)議是基于TCP的一種新的網(wǎng)絡(luò)協(xié)議。它實現(xiàn)了瀏覽器與服務(wù)器全雙工(full-duplex)通信——允許服務(wù)器主動發(fā)送信息給客戶端。瀏覽器和服務(wù)器僅需一次握手,兩者之間就直接可以創(chuàng)建持久性的連接,并進行雙向數(shù)據(jù)傳輸。
協(xié)議原理
Websocket協(xié)議基于Http協(xié)議,針對Http協(xié)議進行了相關(guān)的改善,且Websocket協(xié)議也需要建立TCP連接來實現(xiàn)數(shù)據(jù)傳輸,具體實現(xiàn)如下圖:
說明:
- 客戶端發(fā)起http請求,經(jīng)過3次握手后,建立起TCP連接;http請求里存放WebSocket支持的版本號等信息,如:Upgrade、Connection、WebSocket-Version等。
- 服務(wù)器收到客戶端的握手請求后,同樣采用HTTP協(xié)議回饋數(shù)據(jù)
- 客戶端收到連接成功的消息后,開始借助于TCP傳輸信道進行全雙工通信.
WebSocket與HTTP協(xié)議的區(qū)別
相同點:都是一樣基于TCP的,都是可靠性傳輸協(xié)議。都是應(yīng)用層協(xié)議。
不同點:
- WebSocket是雙向通信協(xié)議,可以雙向發(fā)送或接受信息,而HTTP是單向協(xié)議
- WebSocket需要瀏覽器和服務(wù)器握手進行建立連接的,而http是瀏覽器發(fā)起向服務(wù)器的連接。
WebSocket特點
- 建立在TCP協(xié)議之上,服務(wù)器端的實現(xiàn)比較容易。
- 數(shù)據(jù)格式比較輕量,性能開銷小,通信高效。
- 支持多種數(shù)據(jù)格式,可以發(fā)送文本、二進制數(shù)據(jù)。
- 客戶端可以與任意服務(wù)器通信,無同源限制。
應(yīng)用場景
- 即時聊天通信
- 在線協(xié)同編輯/編輯
- 實時數(shù)據(jù)流的拉取與推送
- 實時地圖位置
系統(tǒng)集成Websocket
jar包引入
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.4.RELEASE</version> <relativePath/> </parent> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency>
Websocket配置
@Configuration public class WebSocketConfig { @Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); } }
具體實現(xiàn)
@ServerEndpoint(value="/websocket/{uid}") @Component public class WebSocketServer { private Logger logger = LoggerFactory.getLogger(WebSocketServer.class); private static final AtomicInteger onlineCount = new AtomicInteger(0); private static CopyOnWriteArraySet<Session> sessionSet = new CopyOnWriteArraySet<Session>(); @OnOpen public void onOpen(Session session,@PathParam("uid") String uid) { logger.info("open message uid:{}",uid); sessionSet.add(session); onlineCount.incrementAndGet(); logger.info("窗口開始監(jiān)聽uid:{},當(dāng)前在線人數(shù):{}",uid,onlineCount); } @OnClose public void onClose(Session session) { String sessionId=session.getId(); logger.info("sessionid:{} close",sessionId); sessionSet.remove(this); int count=onlineCount.decrementAndGet(); logger.info("有一連接關(guān)閉!當(dāng)前在線人數(shù)為:{}",count); } @OnError public void onError(Session session, Throwable error) { logger.error("消息發(fā)生錯誤:{},Session ID: {}",error.getMessage(),session.getId()); } public void batchSendMesasge(String uid,String message) throws IOException { logger.info("推送消息到窗口:{},推送內(nèi)容:{}",uid,message); for(Session session:sessionSet){ sendMessage(session, message); } } public void sendMessage(Session session, String message) throws IOException { if(session!=null) { synchronized (session) { session.getBasicRemote().sendText(message); } } } }
說明: @OnOpen :當(dāng)有新的WebSocket連接進入時調(diào)用 @OnClose:當(dāng)有WebSocket連接關(guān)閉時調(diào)用 @OnError :當(dāng)有WebSocket拋出異常時調(diào)用 @OnMessage:當(dāng)接收到字符串消息時,對該方法進行回調(diào)
測試示例
@Controller public class WebScoketController { @Autowired private WebSocketServer webSocketServer; @ResponseBody @RequestMapping("/sendMessage") public String batchMessage(String uid,String message) { Map<String, String> map =new HashMap<String, String>(); try { map.put("code", "200"); webSocketServer.batchSendMesasge(uid,message); } catch (Exception e) { map.put("code", "-1"); map.put("message", e.getMessage()); } return JSON.toJSONString(map); } @GetMapping("/enter") public String enter() { return "webscoketTest.html"; } }
頁面請求websocket
<!DOCTYPE HTML> <html> <head> <meta charset="utf-8"> <title>websocket test</title> <script type="text/javascript"> if ("WebSocket" in window) { console.log("您的瀏覽器支持 WebSocket!"); var ws = new WebSocket("ws://127.0.0.1:9092/websocket/1234"); console.log('ws連接狀態(tài):' + ws.readyState); //打開 ws.onopen = function() { ws.send("message test"); console.log("mesage sending"); }; //發(fā)送消息 ws.onmessage = function (evt) { var received_msg = evt.data; alert(received_msg); }; //關(guān)閉 ws.onclose = function() { // 關(guān)閉 websocket console.log("socket is close"); }; } else { console.log("您的瀏覽器不支持 WebSocket!"); } </script> </head> </html>
測試效果
啟動程序:運行http://localhost:9092/enter 進入頁面開啟websocket。
用戶發(fā)送消息:http://localhost:9092/sendMessage?uid=1235&message=this%20is%20message1
執(zhí)行的結(jié)果如下:
open message uid:1234
[nio-9092-exec-2] c.s.f.w.controller.WebSocketServer: 窗口開始監(jiān)聽uid:1234,當(dāng)前在線人數(shù):1
[nio-9092-exec-5] c.s.f.w.controller.WebSocketServer: 推送消息到窗口:1234,推送內(nèi)容:this is message
以上就是SpringBoot+WebSocket實現(xiàn)消息推送功能的詳細內(nèi)容,更多關(guān)于SpringBoot WebSocket消息推送的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java中volatile關(guān)鍵字實現(xiàn)原理
本文詳細解讀一下volatile關(guān)鍵字如何保證變量在多線程之間的可見性,對Java中volatile關(guān)鍵字實現(xiàn)原理感興趣的朋友一起通過本文學(xué)習(xí)吧2017-06-06PowerJob分布式任務(wù)調(diào)度源碼流程解讀
這篇文章主要為大家介紹了PowerJob分布式任務(wù)調(diào)度源碼流程解讀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2024-02-02Java使用for循環(huán)解決經(jīng)典的雞兔同籠問題示例
這篇文章主要介紹了Java使用for循環(huán)解決經(jīng)典的雞兔同籠問題,結(jié)合實例形式分析了Java巧妙使用流程控制語句for循環(huán)解決雞兔同籠問題相關(guān)操作技巧,需要的朋友可以參考下2018-05-05springboot 實現(xiàn)mqtt物聯(lián)網(wǎng)的示例代碼
這篇文章主要介紹了springboot 實現(xiàn)mqtt物聯(lián)網(wǎng),本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-03-03Java數(shù)據(jù)庫連接_jdbc-odbc橋連接方式(詳解)
下面小編就為大家?guī)硪黄狫ava數(shù)據(jù)庫連接_jdbc-odbc橋連接方式(詳解)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-08-08