武培軒html
推薦搜索git
Spring BootJavaMySQLRedisGit數據結構github
假設有這樣一個場景:服務端的資源常常在更新,客戶端須要儘可能及時地瞭解到這些更新發生後展現給用戶,若是是 HTTP 1.1,一般會開啓 ajax 請求詢問服務端是否有更新,經過定時器反覆輪詢服務端響應的資源是否有更新。web
ajax 輪詢
在長時間不更新的狀況下,反覆地去詢問會對服務器形成很大的壓力,對網絡也有很大的消耗,若是定時的時間比較大,服務端有更新的話,客戶端可能須要等待定時器達到之後才能獲知,這個信息也不能很及時地獲取到。ajax
而有了 WebSocket 協議,就能很好地解決這些問題,WebSocket 能夠反向通知的,一般向服務端訂閱一類消息,服務端發現這類消息有更新就會不停地通知客戶端。spring
WebSocket數據庫
WebSocket 協議是基於 TCP 的一種新的網絡協議,它實現了瀏覽器與服務器全雙工(full-duplex)通訊—容許服務器主動發送信息給客戶端,這樣就能夠實現從客戶端發送消息到服務器,而服務器又能夠轉發消息到客戶端,這樣就可以實現客戶端之間的交互。對於 WebSocket 的開發,Spring 也提供了良好的支持,目前不少瀏覽器已經實現了 WebSocket 協議,可是依舊存在着不少瀏覽器沒有實現該協議,爲了兼容那些沒有實現該協議的瀏覽器,每每還須要經過 STOMP 協議來完成這些兼容。json
下面咱們在 Spring Boot 中集成 WebSocket 來實現服務端推送消息到客戶端。瀏覽器
首先建立一個 Spring Boot 項目,而後在 pom.xml 加入以下依賴集成 WebSocket:服務器
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency>
接下來在 config 包下建立一個 WebSocket 配置類 WebSocketConfiguration,在配置類上加入註解 @EnableWebSocket,代表開啓 WebSocket,內部實例化 ServerEndpointExporter 的 Bean,該 Bean 會自動註冊 @ServerEndpoint 註解聲明的端點,代碼以下:
@Configuration @EnableWebSocket public class WebSocketConfiguration { @Bean public ServerEndpointExporter serverEndpointExporter() { return new ServerEndpointExporter(); } }
接下來使用 @ServerEndpoint 定義一個端點服務類,在端點服務類中,能夠定義 WebSocket 的打開、關閉、錯誤和發送消息的方法,具體代碼以下所示:
@ServerEndpoint("/websocket/{userId}") @Component public class WebSocketServer { private static final Logger log = LoggerFactory.getLogger(WebSocketServer.class); /** * 當前在線鏈接數 */ private static AtomicInteger onlineCount = new AtomicInteger(0); /** * 用來存放每一個客戶端對應的 WebSocketServer 對象 */ private static ConcurrentHashMap<String, WebSocketServer> webSocketMap = new ConcurrentHashMap<>(); /** * 與某個客戶端的鏈接會話,須要經過它來給客戶端發送數據 */ private Session session; /** * 接收 userId */ private String userId = ""; /** * 鏈接創建成功調用的方法 */ @OnOpen public void onOpen(Session session, @PathParam("userId") String userId) { this.session = session; this.userId = userId; if (webSocketMap.containsKey(userId)) { webSocketMap.remove(userId); webSocketMap.put(userId, this); } else { webSocketMap.put(userId, this); addOnlineCount(); } log.info("用戶鏈接:" + userId + ",當前在線人數爲:" + getOnlineCount()); try { sendMessage("鏈接成功!"); } catch (IOException e) { log.error("用戶:" + userId + ",網絡異常!!!!!!"); } } /** * 鏈接關閉調用的方法 */ @OnClose public void onClose() { if (webSocketMap.containsKey(userId)) { webSocketMap.remove(userId); subOnlineCount(); } log.info("用戶退出:" + userId + ",當前在線人數爲:" + getOnlineCount()); } /** * 收到客戶端消息後調用的方法 * * @param message 客戶端發送過來的消息 */ @OnMessage public void onMessage(String message, Session session) { log.info("用戶消息:" + userId + ",報文:" + message); if (!StringUtils.isEmpty(message)) { try { JSONObject jsonObject = JSON.parseObject(message); jsonObject.put("fromUserId", this.userId); String toUserId = jsonObject.getString("toUserId"); if (!StringUtils.isEmpty(toUserId) && webSocketMap.containsKey(toUserId)) { webSocketMap.get(toUserId).sendMessage(jsonObject.toJSONString()); } else { log.error("請求的 userId:" + toUserId + "不在該服務器上"); } } catch (Exception e) { e.printStackTrace(); } } } /** * 發生錯誤時調用 * * @param session * @param error */ @OnError public void onError(Session session, Throwable error) { log.error("用戶錯誤:" + this.userId + ",緣由:" + error.getMessage()); error.printStackTrace(); } /** * 實現服務器主動推送 */ public void sendMessage(String message) throws IOException { this.session.getBasicRemote().sendText(message); } public static synchronized AtomicInteger getOnlineCount() { return onlineCount; } public static synchronized void addOnlineCount() { WebSocketServer.onlineCount.getAndIncrement(); } public static synchronized void subOnlineCount() { WebSocketServer.onlineCount.getAndDecrement(); } }
其中,@ServerEndpoint("/websocket/{userId}")表示讓 Spring 建立 WebSocket 的服務端點,其中請求地址是 /websocket/{userId}。
另外 WebSocket 一共有四個事件,分別對應 JSR-356 定義的 @OnOpen、@OnMessage、@OnClose、@OnError 註解。
接下來啓動項目,使用 WebSocket 在線測試工具(http://www.easyswoole.com/wstool.html)進行測試,有能力的也能夠本身寫個 html 測試。
打開網頁後,在服務地址中輸入ws://127.0.0.1:8080/websocket/wupx,點擊開啓鏈接按鈕,消息記錄中會多一條由服務器端發送的鏈接成功!記錄。
接下來再打開一個網頁,服務地址中輸入ws://127.0.0.1:8080/websocket/huxy,點擊開啓鏈接按鈕,而後回到第一次打開的網頁在消息框中輸入{"toUserId":"huxy","message":"i love you"},點擊發送到服務端,第二個網頁中會收到服務端推送的消息{"fromUserId":"wupx","message":"i love you","toUserId":"huxy"}。
一樣,項目的日誌中也會有相應的日誌:
2020-06-30 12:40:48.894 INFO 78908 --- [nio-8080-exec-1] com.wupx.server.WebSocketServer : 用戶鏈接:wupx,當前在線人數爲:1 2020-06-30 12:40:58.073 INFO 78908 --- [nio-8080-exec-2] com.wupx.server.WebSocketServer : 用戶鏈接:huxy,當前在線人數爲:2 2020-06-30 12:41:05.870 INFO 78908 --- [nio-8080-exec-3] com.wupx.server.WebSocketServer : 用戶消息:wupx,報文:{"toUserId":"huxy","message":"i love you"}
總結
本文簡單地介紹了 Spring Boot 集成 WebSocket 實現服務端主動推送消息到客戶端,是否是十分簡單呢?你們能夠本身也寫個 demo 試試!
本文的完整代碼在 https://github.com/wupeixuan/SpringBoot-Learn 的 websocket 目錄下。
留言討論
最好的關係就是互相成就,你們的點贊、在看、轉發、留言就是我創做的最大動力。
參考
https://github.com/wupeixuan/SpringBoot-Learn
《深刻淺出Spring Boot 2.x》
完
●如何優雅地中止 Spring Boot 應用?
●Spring Boot 集成 Swagger 構建接口文檔
●Spring Boot 集成 Flyway 實現數據庫版本控制
武培軒有幫助?在看,轉發走一波喜歡做者