iT邦幫忙

2025 iThome 鐵人賽

DAY 17
0
Software Development

事件驅動電力交易平台:Spring Boot 實戰系列 第 17

Day 17|WebSocket/STOMP 架構與設定:讓行情用「訂閱」取代輪詢

  • 分享至 

  • xImage
  •  

本文聚焦「架構與 WebSocket 設定」,說明為什麼用 STOMP/WebSocket、端點與前綴怎麼切、首次訂閱如何回推一次快照。
次日會介紹「實際推送內容」:市場摘要、訂單簿、逐筆成交、最近成交等 DTO 與 payload實際內容。

背景:為什麼不用 REST 輪詢?

  • 效能差:每秒輪詢造成大量重複查詢。

  • 延遲高:行情講究秒級/毫秒級,輪詢跟不上。

解法:STOMP over WebSocket,前端「訂閱一次、持續接收」。

設計總覽

  • 端點:/ws(支援 SockJS 降級)

  • Application Prefix:/app(對應 @MessageMapping)

  • Broker Prefix:/topic、/queue(前端訂閱用)

消息推送:SimpMessagingTemplate.convertAndSend(...)

首次訂閱即回推:使用 @SubscribeMapping 在訂閱當下回一次快照

1) WebSocket 設定(程式碼節錄)

端點 /ws、/app 前綴、/topic//queue broker
以下為我的程式碼節錄內容

@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {

    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        config.enableSimpleBroker("/topic", "/queue");   // 前端訂閱
        config.setApplicationDestinationPrefixes("/app"); // 後端處理入口
    }

    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        registry.addEndpoint("/ws")
                .setAllowedOriginPatterns("*")
                .withSockJS(); // 有需要可移除 SockJS 僅保留原生 WS
    }
}

設定重點

  • /ws:前端 WebSocket/SockJS 連線入口。

  • /app:前端送指令到後端(@MessageMapping)。

  • /topic:後端推送資料的訂閱目的地(convertAndSend("/topic/…", data))。

2) 訂閱入口與指令(程式碼節錄)

首次訂閱即推一次,之後根據排程設定的時間做訊息推播,並提供心跳與手動刷新

@Controller
@RequiredArgsConstructor
@Slf4j
public class WebSocketController {

    private final MarketDataService marketDataService;

    // === 訂閱 ===
    @SubscribeMapping("/topic/market")
    public void subscribeMarketData() {
        log.info("客戶端訂閱市場數據");
        marketDataService.pushMarketData();      // 首次訂閱回一次
    }

    @SubscribeMapping("/topic/trades")
    public void subscribeRealtimeTrades() {
        log.info("客戶端訂閱實時成交數據");
        marketDataService.pushRecentTrades();    // 先把最近成交補齊
    }

    @SubscribeMapping("/topic/trades/recent")
    public void subscribeRecentTrades() {
        log.info("客戶端訂閱最近成交記錄");
        marketDataService.pushRecentTrades();
    }

    @SubscribeMapping("/topic/orderbook")
    public void subscribeOrderBook() {
        log.info("客戶端訂閱訂單簿數據");
        marketDataService.pushOrderBook();       // 推一份即時簿
    }

    // === 指令 ===
    @MessageMapping("/heartbeat")
    @SendTo("/topic/heartbeat")
    public String handleHeartbeat(String message) {
        log.debug("收到心跳消息: {}", message);
        return "pong";
    }

    @MessageMapping("/market/refresh")
    public void handleMarketRefresh() {
        log.info("客戶端請求刷新市場數據");
        marketDataService.pushMarketData();
        marketDataService.pushOrderBook();
    }
}

訂閱/指令一覽

  • 訂閱:/topic/market、/topic/orderbook、/topic/trades、/topic/trades/recent

  • 指令:/app/heartbeat(回 /topic/heartbeat)、/app/market/refresh

3) 前端最小接法(STOMP + SockJS)範例

筆者為後端工程,對於前端熟悉度較低,這邊採用我學過的js簡單串接方法做介紹

<script src="https://cdn.jsdelivr.net/npm/sockjs-client/dist/sockjs.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/stompjs/lib/stomp.min.js"></script>
<script>
  const sock = new SockJS('/ws');
  const client = Stomp.over(sock); // 開發期可關閉 debug: client.debug = () => {};

  client.connect({}, () => {
    // 訂閱主題
    client.subscribe('/topic/market',    m => console.log('market', JSON.parse(m.body)));
    client.subscribe('/topic/orderbook', m => console.log('orderbook', JSON.parse(m.body)));
    client.subscribe('/topic/trades',    m => console.log('trade', JSON.parse(m.body)));
    client.subscribe('/topic/trades/recent', m => console.log('recent', JSON.parse(m.body)));
    client.subscribe('/topic/heartbeat', m => console.log('heartbeat', m.body));

    // 心跳 + 手動刷新一次
    client.send('/app/heartbeat', {}, 'ping');
    client.send('/app/market/refresh', {});
  });
</script>

4) 驗收步驟

啟動 order-service;瀏覽器/前端接上 /ws。

確認訂閱 /topic/market 時,立即收到一次快照。

送 /app/heartbeat 應回 "pong" 到 /topic/heartbeat。

送 /app/market/refresh,應收到市場/訂單簿最新資料。

明天會把「實際推送內容」全部攤開:來源、DTO、payload 例子、撮合事件觸發的即時推播流程。


上一篇
Day 16|多節點不搶單:Queue 分工 × Lua 原子更新的雙保險
下一篇
Day 18 |推送面向與主題一覽 市場摘要、 訂單簿近況、 逐筆成交、 最近成交
系列文
事件驅動電力交易平台:Spring Boot 實戰18
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言