websocket是什么以及它要怎么用
WebSocket 實(shí)時(shí)通知機(jī)制。
在高并發(fā)、異步下單場(chǎng)景中,它是提升用戶體驗(yàn)、降低輪詢壓力的關(guān)鍵組件。
接下來(lái)會(huì)從原理講清楚,再結(jié)合“秒殺活動(dòng)”落地展示完整的前后端交互與代碼實(shí)現(xiàn)(Java Spring Boot + 前端示例)。
一、WebSocket 是什么?
WebSocket 是一種在瀏覽器和服務(wù)器之間建立「全雙工、持久連接」的通信協(xié)議。
與傳統(tǒng) HTTP 不同,HTTP 是短連接、請(qǐng)求響應(yīng)式的;而 WebSocket 建立連接后,服務(wù)器可以主動(dòng)推送消息給客戶端,不需要客戶端輪詢。
?? 對(duì)比傳統(tǒng) HTTP 輪詢
| 特點(diǎn) | HTTP 輪詢 | WebSocket |
|---|---|---|
| 連接模式 | 短連接,請(qǐng)求-響應(yīng) | 持久連接,雙向通信 |
| 服務(wù)器能否主動(dòng)發(fā)消息 | ? 不能 | ? 可以 |
| 性能 | 頻繁請(qǐng)求,壓力大 | 長(zhǎng)連接,輕量、實(shí)時(shí) |
| 適用場(chǎng)景 | 請(qǐng)求量小、實(shí)時(shí)性低 | 聊天、訂單狀態(tài)、秒殺、行情推送 |
二、在「秒殺活動(dòng)」中為什么用 WebSocket?
在你的秒殺設(shè)計(jì)中:
-
用戶點(diǎn)擊“立即搶購(gòu)” → 系統(tǒng)用 Redis + MQ 異步下單;
-
請(qǐng)求立即返回
"QUEUED"狀態(tài); -
后端在隊(duì)列消費(fèi)者落單后,才知道成功或失敗。
如果不用 WebSocket:
-
用戶只能輪詢
GET /seckill/status?messageId=...; -
并發(fā)量高時(shí),會(huì)造成 Web 層 + Redis + DB 壓力。
如果用 WebSocket:
-
用戶請(qǐng)求秒殺后,保持 WebSocket 長(zhǎng)連接;
-
后端消費(fèi)者落單完成時(shí),通過(guò) WebSocket 通道主動(dòng)推送訂單狀態(tài)變更;
-
用戶即時(shí)看到結(jié)果,不需要反復(fù)刷新。
?? 實(shí)時(shí)、節(jié)流、省資源。
三、WebSocket 在秒殺系統(tǒng)中的使用流程(圖解)
┌──────────────────────┐
│ 瀏覽器端 │
│ 1. 建立 WebSocket │
│ 2. 發(fā)送秒殺請(qǐng)求 │
│ 3. 等待推送結(jié)果 │
└──────────┬───────────┘
│
WebSocket 連接
│
┌──────────▼──────────┐
│ 秒殺服務(wù) (Spring) │
│ - 維護(hù)連接映射表 │
│ - 通過(guò) MQ 消費(fèi)結(jié)果 │
│ - 推送狀態(tài)到客戶端 │
└──────────┬───────────┘
│
MQ/Rabbit
│
┌──────────▼──────────┐
│ 消費(fèi)者服務(wù) │
│ - 創(chuàng)建訂單 │
│ - 發(fā)送結(jié)果通知 │
└──────────────────────┘
四、詳細(xì)實(shí)現(xiàn)(Java + 前端)
?? 前端實(shí)現(xiàn)(示例)
假設(shè)前端是一個(gè)簡(jiǎn)單的 Vue / HTML 頁(yè)面。
<!DOCTYPE html> <html> <head> <title>秒殺活動(dòng)</title> </head> <body> <h2>秒殺活動(dòng)</h2> <button id="buyBtn">立即搶購(gòu)</button> <div id="status"></div> <script> const userId = "user_123"; const ws = new WebSocket(`ws://localhost:8080/ws/seckill/${userId}`); ws.onopen = () => { console.log("? WebSocket connected"); }; ws.onmessage = (event) => { const msg = JSON.parse(event.data); if (msg.type === "ORDER_STATUS") { document.getElementById("status").innerText = "訂單狀態(tài): " + msg.status; } }; document.getElementById("buyBtn").onclick = async () => { const res = await fetch("/api/seckill/buy?eventId=1001&userId=" + userId, { method: "POST" }); const data = await res.json(); document.getElementById("status").innerText = "請(qǐng)求結(jié)果: " + data.status; }; </script> </body> </html>
說(shuō)明:
-
頁(yè)面在加載時(shí)與服務(wù)器建立 WebSocket 長(zhǎng)連接;
-
當(dāng)用戶點(diǎn)擊“立即搶購(gòu)”后,發(fā)起 HTTP 下單請(qǐng)求;
-
后端在訂單異步完成后,通過(guò) WebSocket 主動(dòng)推送消息;
-
前端即時(shí)更新顯示。
?? 后端實(shí)現(xiàn)(Spring Boot)
我們需要三個(gè)部分:
-
WebSocket 服務(wù)端(負(fù)責(zé)連接與推送)
-
秒殺請(qǐng)求 Controller(發(fā)起秒殺邏輯)
-
訂單消費(fèi)者(異步落單后推送結(jié)果)
1?? WebSocket 配置與服務(wù)端類
// WebSocketConfig.java @Configuration @EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { @Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(new SeckillWebSocketHandler(), "/ws/seckill/{userId}") .setAllowedOrigins("*"); // 生產(chǎn)環(huán)境記得做白名單 } }
// SeckillWebSocketHandler.java @Component public class SeckillWebSocketHandler extends TextWebSocketHandler { // 存儲(chǔ)所有用戶連接(簡(jiǎn)單實(shí)現(xiàn),可換為 Redis + Channel 分布式映射) private static final Map<String, WebSocketSession> userSessions = new ConcurrentHashMap<>(); @Override public void afterConnectionEstablished(WebSocketSession session) { String userId = getUserId(session); userSessions.put(userId, session); System.out.println("用戶 " + userId + " 已連接"); } @Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) { String userId = getUserId(session); userSessions.remove(userId); System.out.println("用戶 " + userId + " 已斷開(kāi)"); } private String getUserId(WebSocketSession session) { return session.getUri().getPath().split("/ws/seckill/")[1]; } public void sendMessageToUser(String userId, String message) { WebSocketSession session = userSessions.get(userId); if (session != null && session.isOpen()) { try { session.sendMessage(new TextMessage(message)); } catch (IOException e) { e.printStackTrace(); } } } }
2?? 秒殺 Controller(異步下單 + 即時(shí)返回)

浙公網(wǎng)安備 33010602011771號(hào)