SpringBoot集成WebSocket長(zhǎng)連接實(shí)際應(yīng)用詳解
前言:
一、WebSocket之初出茅驢
官方定義:WebSocket是一種在單個(gè)TCP連接上進(jìn)行全雙工通信的協(xié)議。WebSocket使得客戶端和服務(wù)器之間的數(shù)據(jù)交換變得更加簡(jiǎn)單,允許服務(wù)端主動(dòng)向客戶端推送數(shù)據(jù)。在WebSocket API中,瀏覽器和服務(wù)器只需要完成一次握手,兩者之間就直接可以創(chuàng)建持久性的連接,并進(jìn)行雙向數(shù)據(jù)傳輸。是真正的雙向平等對(duì)話,屬于服務(wù)器推送技術(shù)的一種。
太官方啦,還是博主過(guò)來(lái)翻譯一下吧 :WebSocket技術(shù)只需要service和client建立一次連接,就能實(shí)現(xiàn)服務(wù)器和客戶端雙方相互頻繁的發(fā)送請(qǐng)求和通信!(簡(jiǎn)單加粗暴的翻譯有木有,哈哈?。?/p>
WebSocket經(jīng)典的使用場(chǎng)景:網(wǎng)站在線聊天系統(tǒng)、彈幕系統(tǒng)…
臣附議:webSocket技術(shù)無(wú)法做到向下兼容,不兼容低版本的IE,因此依賴于瀏覽器版本,這也正是webSocket非常顯著的缺陷。
二、WebSocket的出現(xiàn)到底為我們解決了什么實(shí)際問(wèn)題?
在傳統(tǒng)的b/s架構(gòu)中,要實(shí)現(xiàn)服務(wù)器向client進(jìn)行實(shí)時(shí)消息推送功能,市場(chǎng)上常用的解決方案大致分為三類:
| 定時(shí)輪詢 | 客戶端以一定的時(shí)間間隔向服務(wù)端發(fā)出請(qǐng)求 |
|---|---|
| 長(zhǎng)輪詢 | 如果服務(wù)器沒(méi)有可以立即返回給客戶端的數(shù)據(jù),則不會(huì)立刻返回一個(gè)空結(jié)果 |
| 流技術(shù) | 客戶端隱藏的窗口向服務(wù)端發(fā)出一個(gè)長(zhǎng)輪詢的請(qǐng)求 |
長(zhǎng)輪詢機(jī)制:

綜合這幾種方案,您會(huì)發(fā)現(xiàn)這些目前我們所使用的所謂的實(shí)時(shí)技術(shù)并不是真正的實(shí)時(shí)技術(shù),它們只是在用 Ajax 方式來(lái)模擬實(shí)時(shí)的效果,定時(shí)輪詢需要實(shí)時(shí)獲取取服務(wù)端信息的應(yīng)用時(shí), client需要頻繁輪詢server,為了拿到最新信息, client一直這樣循環(huán)下去server如果一直沒(méi)有新的消息, client的大多請(qǐng)求都是屬于無(wú)效請(qǐng)求, 導(dǎo)致會(huì)帶來(lái)很多無(wú)謂的網(wǎng)絡(luò)傳輸,所以這是一種非常低效的實(shí)時(shí)方案。長(zhǎng)輪詢對(duì)服務(wù)器造成的壓力非常大,并且如果服務(wù)端的數(shù)據(jù)變更非常頻繁的話,這種方式無(wú)異于定時(shí)輪詢。所以為了解決傳統(tǒng)http請(qǐng)求的實(shí)際問(wèn)題,WebSocket技術(shù)應(yīng)運(yùn)而生!下面博主給張圖讓大家生動(dòng)的理解傳統(tǒng)HTTP和WebSocket的差異化:

三、博主使用WebSocket的場(chǎng)景
博主最進(jìn)在公司調(diào)用第三方影像采集系統(tǒng),由于影像狀態(tài)是異步返回給業(yè)務(wù)系統(tǒng)的,導(dǎo)致當(dāng)業(yè)務(wù)系統(tǒng)收到第三方回調(diào)后,對(duì)于前臺(tái)用戶體驗(yàn)來(lái)說(shuō)是無(wú)感知的,因此前臺(tái)必須刷新頁(yè)面才能獲取到影像最新?tīng)顟B(tài)。這時(shí)候由service主動(dòng)向client實(shí)時(shí)發(fā)送影像采集狀態(tài)的通知是最好不過(guò)的方案!在上述提到的常用解決方案,像輪詢這種比較low的實(shí)現(xiàn),博主作為技術(shù)宅,肯定是不會(huì)作為技術(shù)選型的,哈哈…
四、不多bb,上代碼!
本項(xiàng)目是基于SpringBoot環(huán)境開(kāi)發(fā)
1、導(dǎo)入websocket坐標(biāo)
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-websocket</artifactId> </dependency>
2、封裝WebSocketUtil工具類,用于提供對(duì)session鏈接、斷開(kāi)連接、推送消息的簡(jiǎn)單控制。
public class WebsocketUtil {
/**
* 記錄當(dāng)前在線的Session
*/
private static final Map<String, Session> ONLINE_SESSION = new ConcurrentHashMap<> ();
/**
* 添加session
* @param userId
* @param session
*/
public static void addSession(String userId, Session session){
// 此處只允許一個(gè)用戶的session鏈接。一個(gè)用戶的多個(gè)連接,我們視為無(wú)效。
ONLINE_SESSION.putIfAbsent ( userId, session );
}
/**
* 關(guān)閉session
* @param userId
*/
public static void removeSession(String userId){
ONLINE_SESSION.remove ( userId );
}
/**
* 給單個(gè)用戶推送消息
* @param session
* @param message
*/
public static void sendMessage(Session session, String message){
if(session == null){
return;
}
// 同步
RemoteEndpoint.Async async = session.getAsyncRemote ();
async.sendText ( message );
}
/**
* 向所有在線人發(fā)送消息
* @param message
*/
public static void sendMessageForAll(String message) {
//jdk8 新方法
ONLINE_SESSION.forEach((sessionId, session) -> sendMessage(session, message));
}
}
3、 WebSocketController
如上,已經(jīng)創(chuàng)建好了簡(jiǎn)單的session管理和消息管理,接下來(lái)要使用注解的方式,使用SpringBoot的websocket包提供的方法,實(shí)現(xiàn)OnOpen、OnClose、OnMessage三個(gè)方法,再實(shí)現(xiàn)一個(gè)OnError方法來(lái)應(yīng)對(duì)異常。代碼段如下:
/**
* websocket接口處理類
*/
@Component
@ServerEndpoint ( value = "/chat/{userid}" )
public class WebsocketController {
/**
* 連接事件,加入注解
* @param userId
* @param session
*/
@OnOpen
public void onOpen( @PathParam ( value = "userid" ) String userId, Session session ) {
String message ="[" + userId + "]加入聊天室??!";
// 添加到session的映射關(guān)系中
WebsocketUtil.addSession ( userId, session );
// 廣播通知,某用戶上線了
WebsocketUtil.sendMessageForAll ( message );
}
/**
* 連接事件,加入注解
* 用戶斷開(kāi)鏈接
* @param userId
* @param session
*/
@OnClose
public void onClose(@PathParam ( value = "userId" ) String userId, Session session ) {
String message ="[" + userId + "]退出了聊天室...";
// 刪除映射關(guān)系
WebsocketUtil.removeSession ( userId );
// 廣播通知,用戶下線了
WebsocketUtil.sendMessageForAll ( message );
}
/**
* 當(dāng)接收到用戶上傳的消息
* @param userId
* @param session
*/
@OnMessage
public void onMessage(@PathParam ( value = "userId" ) String userId, Session session ,String message) {
String msg ="[" + userId + "]:"+message;
// 直接廣播
WebsocketUtil.sendMessageForAll ( msg );
}
/**
* 處理用戶活連接異常
* @param session
* @param throwable
*/
@OnError
public void onError(Session session, Throwable throwable) {
try {
session.close();
} catch (IOException e) {
e.printStackTrace();
}
throwable.printStackTrace();
}
}
4、添加 ServerEndpointExporter 啟動(dòng)Bean
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
/**
* 會(huì)自動(dòng)注冊(cè)使用了@ServerEndpoint注解聲明的Websocket endpoint
* 要注意,如果使用獨(dú)立的servlet容器,
* 而不是直接使用springboot的內(nèi)置容器,
* 就不要注入ServerEndpointExporter,因?yàn)樗鼘⒂扇萜髯约禾峁┖凸芾怼?
*/
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}
那些年踩過(guò)的坑:
注意:在websocketEndpoint中,使用@Autowired一些列注解注入Bean時(shí)候,一直無(wú)法注入,報(bào)空指針。原因在于spring管理的都是單例(singleton),和 websocket (多對(duì)象)相沖突。
解決辦法:通過(guò)上下文獲取bean實(shí)例:從Spring上下文獲取bean實(shí)例的方法
到此這篇關(guān)于SpringBoot集成WebSocket長(zhǎng)連接實(shí)際應(yīng)用詳解的文章就介紹到這了,更多相關(guān)SpringBoot WebSocket長(zhǎng)連接內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java報(bào)錯(cuò):Error:java:?程序包org.springframework.boot不存在解決辦法
建完springboot項(xiàng)目時(shí),點(diǎn)擊啟動(dòng),有可能會(huì)報(bào)錯(cuò),下面這篇文章主要給大家介紹了關(guān)于Java報(bào)錯(cuò):Error:java:?程序包org.springframework.boot不存在的解決辦法,需要的朋友可以參考下2024-02-02
Java中的Unsafe在安全領(lǐng)域的使用總結(jié)和復(fù)現(xiàn)(實(shí)例詳解)
unsafe里面有很多好用的方法,比如allocateInstance可以直接創(chuàng)建實(shí)例對(duì)象,defineAnonymousClass可以創(chuàng)建一個(gè)VM匿名類(VM?Anonymous?Class),以及直接從內(nèi)存級(jí)別修改對(duì)象的值。這篇文章主要介紹了Java中的Unsafe在安全領(lǐng)域的一些應(yīng)用總結(jié)和復(fù)現(xiàn),需要的朋友可以參考下2022-03-03
MybatisPlus特殊查詢的實(shí)現(xiàn)介紹
這篇文章主要介紹了MybatisPlus查詢投影、聚合查詢、分組查詢、等值查詢、范圍查詢、模糊查詢、排序查詢,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧2022-10-10
Spring中bean的生命周期之getSingleton方法
今天給大家?guī)?lái)的是關(guān)于Spring的相關(guān)知識(shí),文章圍繞著Spring中bean的生命周期之getSingleton方法展開(kāi),文中有非常詳細(xì)的介紹及代碼示例,需要的朋友可以參考下2021-06-06
Java使用synchronized實(shí)現(xiàn)互斥鎖功能示例
這篇文章主要介紹了Java使用synchronized實(shí)現(xiàn)互斥鎖功能,結(jié)合實(shí)例形式分析了Java使用synchronized互斥鎖功能簡(jiǎn)單實(shí)現(xiàn)方法與操作技巧,需要的朋友可以參考下2020-05-05
java開(kāi)源區(qū)塊鏈jdchain入門(mén)
這篇文章主要介紹了java開(kāi)源區(qū)塊鏈jdchain入門(mén),文中為大家講解了關(guān)于部署及組件遇到的一些問(wèn)題,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2022-02-02
springboot restTemplate連接池整合方式
這篇文章主要介紹了springboot restTemplate連接池整合方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-10-10

