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ù)器資源和帶寬,并且能夠更實時地進(jìn)行通訊。本文將介紹如何采用websocket實現(xiàn)消息推送。
WebSocket簡介
WebSocket協(xié)議是基于TCP的一種新的網(wǎng)絡(luò)協(xié)議。它實現(xiàn)了瀏覽器與服務(wù)器全雙工(full-duplex)通信——允許服務(wù)器主動發(fā)送信息給客戶端。瀏覽器和服務(wù)器僅需一次握手,兩者之間就直接可以創(chuàng)建持久性的連接,并進(jìn)行雙向數(shù)據(jù)傳輸。
協(xié)議原理
Websocket協(xié)議基于Http協(xié)議,針對Http協(xié)議進(jìn)行了相關(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傳輸信道進(jìn)行全雙工通信.
WebSocket與HTTP協(xié)議的區(qū)別
相同點:都是一樣基于TCP的,都是可靠性傳輸協(xié)議。都是應(yīng)用層協(xié)議。
不同點:
- WebSocket是雙向通信協(xié)議,可以雙向發(fā)送或接受信息,而HTTP是單向協(xié)議
- WebSocket需要瀏覽器和服務(wù)器握手進(jìn)行建立連接的,而http是瀏覽器發(fā)起向服務(wù)器的連接。
WebSocket特點
- 建立在TCP協(xié)議之上,服務(wù)器端的實現(xiàn)比較容易。
- 數(shù)據(jù)格式比較輕量,性能開銷小,通信高效。
- 支持多種數(shù)據(jù)格式,可以發(fā)送文本、二進(jìn)制數(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連接進(jìn)入時調(diào)用 @OnClose:當(dāng)有WebSocket連接關(guān)閉時調(diào)用 @OnError :當(dāng)有WebSocket拋出異常時調(diào)用 @OnMessage:當(dāng)接收到字符串消息時,對該方法進(jìn)行回調(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 進(jìn)入頁面開啟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)消息推送功能的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot WebSocket消息推送的資料請關(guān)注腳本之家其它相關(guān)文章!
- Spring?Boot+Vue實現(xiàn)Socket通知推送的完整步驟
- spring中websocket定時任務(wù)實現(xiàn)實時推送
- Springboot集成SSE實現(xiàn)單工通信消息推送流程詳解
- SpringBoot整合WebSocket實現(xiàn)后端向前端主動推送消息方式
- Spring?Boot?使用?SSE?方式向前端推送數(shù)據(jù)詳解
- Springboot整合企業(yè)微信機(jī)器人助手推送消息的實現(xiàn)
- SpringBoot整合WxJava開啟消息推送的實現(xiàn)
- SpringBoot2.0集成WebSocket實現(xiàn)后臺向前端推送信息
- SpringBoot+WebSocket+Netty實現(xiàn)消息推送的示例代碼
- Spring SseEmitter推送消息及常用方法
相關(guān)文章
Springboot如何實現(xiàn)自定義異常數(shù)據(jù)
這篇文章主要介紹了Springboot如何實現(xiàn)自定義異常數(shù)據(jù),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下2020-09-09
如何將DeepSeek 集成到 Java 的 Spring Boot&
本文介紹了如何將DeepSeek集成到Java的SpringBoot項目中,包括準(zhǔn)備工作、集成步驟和示例說明,感興趣的朋友一起看看吧2025-02-02
Java實戰(zhàn)之用hutool-db實現(xiàn)多數(shù)據(jù)源配置
在微服務(wù)搭建中經(jīng)常會使用到多數(shù)據(jù)庫情形這個時候,下面這篇文章主要給大家介紹了關(guān)于Java實戰(zhàn)之用hutool-db實現(xiàn)多數(shù)據(jù)源配置的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),需要的朋友可以參考下2022-12-12
在SpringBoot中實現(xiàn)線程池并行處理任務(wù)的方法詳解
在使用Spring Boot開發(fā)應(yīng)用程序時,我們經(jīng)常需要處理一些耗時的任務(wù),例如網(wǎng)絡(luò)請求、數(shù)據(jù)庫操作或者其他需要花費一定時間的計算任務(wù),本文將介紹如何在Spring Boot中使用線程池來實現(xiàn)任務(wù)的并行處理2023-06-06
spark中使用groupByKey進(jìn)行分組排序的示例代碼
這篇文章主要介紹了spark中使用groupByKey進(jìn)行分組排序的實例代碼,本文通過實例代碼給大家講解的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-03-03
mybatis的mapper.xml中resultMap標(biāo)簽的使用詳解
這篇文章主要介紹了mybatis的mapper.xml中resultMap標(biāo)簽的使用詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-06-06
Springboot AOP對指定敏感字段數(shù)據(jù)加密存儲的實現(xiàn)
本篇文章主要介紹了利用Springboot+AOP對指定的敏感數(shù)據(jù)進(jìn)行加密存儲以及對數(shù)據(jù)中加密的數(shù)據(jù)的解密的方法,代碼詳細(xì),具有一定的價值,感興趣的小伙伴可以了解一下2021-11-11

