STOMP案例

wen java案例 2

本文目录导读:

STOMP案例

  1. 案例场景:股票行情实时推送系统
  2. 第一部分:后端实现(Spring Boot + STOMP)
  3. 第二部分:前端实现(JavaScript + STOMP.js)
  4. 第三部分:协议帧分析(底层数据流)
  5. 总结与关键点

这是一个关于 STOMP 协议的全面解析案例,STOMP(Simple Text Oriented Messaging Protocol,简单文本定向消息协议)是一种轻量级的消息传输协议,广泛应用于实时通信系统(如WebSocket聊天、股票行情推送、游戏对战等)。

下面我将通过场景设定代码案例协议分析三个维度,为你详细拆解一个典型的 STOMP 案例。


案例场景:股票行情实时推送系统

假设我们要开发一个金融App,后端有一个 Java 服务(使用 Spring Boot),前端是 Web 浏览器,我们需要将股票价格实时推送给订阅了该股票的用户。

选择 STOMP 的理由: 由于 WebSocket 是底层协议,它只负责传输字节流,不关心消息内容,使用 STOMP 作为 WebSocket 之上的消息协议,可以定义消息格式(帧)、路由(目的地)和订阅机制,让前后端解耦。


第一部分:后端实现(Spring Boot + STOMP)

后端需要配置 WebSocket 端点及消息代理。

配置类(WebSocketConfig.java)

import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
    @Override
    public void configureMessageBroker(MessageBrokerRegistry config) {
        // 设置消息代理的前缀:消息以 /topic 开头会广播给所有订阅者
        // 消息以 /queue 开头会点对点发送(一对一)
        config.enableSimpleBroker("/topic", "/queue");
        // 设置前端发送消息时的前缀:前端发到 /app/sendMessage,后端的处理类通过 @MessageMapping("/sendMessage") 接收
        config.setApplicationDestinationPrefixes("/app");
    }
    @Override
    public void registerStompEndpoints(StompEndpointRegistry registry) {
        // 设置 WebSocket 连接端点,前端通过这个地址建立连接
        // .withSockJS() 是为了兼容不支持 WebSocket 的浏览器,提供降级方案
        registry.addEndpoint("/ws-stomp").setAllowedOrigins("*").withSockJS();
    }
}

消息处理控制器(StockController.java)

这里模拟一个定时推送给特定频道的服务。

import org.springframework.messaging.handler.annotation.MessageMapping;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
@Controller
public class StockController {
    // 注入模板类,用于主动推送消息
    private final SimpMessagingTemplate messagingTemplate;
    public StockController(SimpMessagingTemplate messagingTemplate) {
        this.messagingTemplate = messagingTemplate;
    }
    // 模拟客户端订阅某只股票
    @MessageMapping("/subscribe/stock/{code}") // 客户端发送到 /app/subscribe/stock/AAPL
    @SendTo("/topic/stocks") // 订阅结果广播到 /topic/stocks
    public String subscribeStock(@PathVariable String code) {
        // 这里返回给所有订阅者的初始响应
        return "订阅成功: " + code;
    }
    // 模拟定时推送股票价格
    @Scheduled(fixedRate = 5000) // 每5秒推送一次
    public void pushStockPrices() {
        // 模拟实时价格波动
        String applePrice = String.format("AAPL: $%.2f", Math.random() * 100 + 150);
        String googlePrice = String.format("GOOG: $%.2f", Math.random() * 100 + 2800);
        // 转换并携带目的地 "/topic/stocks",前端只要订阅了这个目的地就能收到
        messagingTemplate.convertAndSend("/topic/stocks", applePrice);
        messagingTemplate.convertAndSend("/topic/stocks", googlePrice);
        // 如果需要点对点推送,可以使用:
        // messagingTemplate.convertAndSendToUser("userId", "/queue/replies", message);
    }
}

第二部分:前端实现(JavaScript + STOMP.js)

前端通过 STOMP 客户端连接后端。

代码示例(使用原生 WebSocket + STOMP 客户端库)

<!DOCTYPE html>
<html>
<head>实时股票行情</title>
    <script src="https://cdn.jsdelivr.net/npm/sockjs-client@1/dist/sockjs.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/stompjs@2.3.3/lib/stomp.min.js"></script>
</head>
<body>
    <h1>股票实时行情</h1>
    <div id="output"></div>
    <script type="text/javascript">
        var stompClient = null;
        // 建立连接
        function connect() {
            // 注意这里使用的是 SockJS,对应后端的 .withSockJS()
            var socket = new SockJS('http://localhost:8080/ws-stomp');
            stompClient = Stomp.over(socket);
            // 建立连接(可以传用户名密码,也可以不传)
            stompClient.connect({}, function (frame) {
                console.log('Connected: ' + frame);
                // 订阅公共频道
                // 1. 订阅后端推送的股票价格
                stompClient.subscribe('/topic/stocks', function (message) {
                    showMessage("更新: " + message.body);
                });
                // 2. 发送订阅请求到后端
                // 注意:前缀是 /app 映射到后端的 @MessageMapping
                stompClient.send("/app/subscribe/stock/AAPL", {}, {});
                // 这样就完成了 请求->响应->持续推送 的通路
            }, function (error) {
                alert('连接失败: ' + error);
            });
        }
        function showMessage(text) {
            var output = document.getElementById('output');
            output.innerHTML += '<p>' + new Date().toLocaleTimeString() + ' - ' + text + '</p>';
        }
        // 断开连接
        function disconnect() {
            if (stompClient !== null) {
                stompClient.disconnect();
            }
            console.log("Disconnected");
        }
        // 页面加载后自动连接
        window.onload = connect;
    </script>
</body>
</html>

第三部分:协议帧分析(底层数据流)

当你在浏览器控制台开启日志(stompClient.debug = function(str) { console.log(str); };),你会看到客户端发往服务器的原始 STOMP 帧格式。

连接帧(客户端 -> 服务端)

CONNECT
accept-version:1.1,1.0
heart-beat:10000,10000
\0     <!-- 空字符表示帧结束 -->

对应后端的 client.connect({}, ...)

订阅帧(客户端 -> 服务端)

SUBSCRIBE
id:sub-0
destination:/topic/stocks
\0

对应前端的 stompClient.subscribe('/topic/stocks', ...)

发送帧(客户端 -> 服务端)

SEND
destination:/app/subscribe/stock/AAPL
content-length:0
\0

对应前端的 stompClient.send(...)

消息帧(服务端 -> 客户端,推送价格时)

MESSAGE
subscription:sub-0
message-id:abc123
destination:/topic/stocks
AAPL: $175.23
\0

对应后端 messagingTemplate.convertAndSend() 执行后,客户端收到的 message.body


总结与关键点

  1. 架构层级

    • 传输层:底层是 WebSocket(或 SockJS 降级的 HTTP 长轮询)。
    • 协议层:STOMP 定义了帧格式(命令 + 头 + 体)。
    • 业务层:前后端根据 destination 进行路由。
  2. 核心概念

    • Destination:目的地。/topic(广播)和 /queue(点对点)。
    • Send vs Publish:客户端用 SEND 发到 /app,服务端用 MESSAGE 推送到 /topic
  3. 优点

    • 简单易用,基于文本调试方便。
    • 提供了ack(消息确认)机制,保证消息可靠送达(可选)。
    • 支持事务(BEGIN/COMMIT),适合复杂的消息操作。
  4. 缺点

    性能低于纯二进制协议(如自定义的 Protocol Buffers 或 MessagePack 直接走 WebSocket),因为它基于文本且有冗余头信息。

在实际项目中,如果业务逻辑简单(如仅聊天室),直接用 WebSocket + JSON 即可;如果涉及复杂的消息路由、多端订阅或需要与企业级消息队列(如 RabbitMQ、ActiveMQ)无缝对接,STOMP 会是更标准、更省力的选择。

上一篇Java SSE案例

下一篇Dubbo案例

抱歉,评论功能暂时关闭!