STOMP(Simple Text Oriented Messaging Protocol,简单文本定向消息协议)是一种轻量级、基于文本的应用层消息通信协议,运行在TCP、WebSocket等可靠双向流协议之上,用于客户端与消息代理(Broker)之间的异步消息交互。它的设计灵感源自HTTP,以极简的帧结构和标准化的消息语义,解决了原生传输层缺乏消息路由、订阅、确认等能力的问题。
STOMP 最早诞生于脚本语言生态(Ruby、Python、Perl),目标是让各类语言都能低成本接入企业级消息中间件,目前已成为WebSocket场景下最主流的消息子协议之一。
一、版本演进
STOMP 目前有三个正式版本,主流 Broker 均兼容全版本:
- STOMP 1.0:初始版本,定义了基础帧结构、连接、收发、订阅等核心能力
- STOMP 1.1:新增心跳保活、NACK 否定确认、事务增强、重复头部处理等能力
- STOMP 1.2:最新稳定版(2012年发布),优化了心跳协商、流控逻辑、内容长度解析规则,是当前工业界的主流版本
二、核心架构与模型
STOMP 采用经典的客户端-消息代理(Broker) 架构,协议本身只定义通信格式,不强制消息路由语义,具体交付逻辑由 Broker 实现。
1. 角色定义
- 生产者 :客户端通过
SEND命令向指定目的地发送消息 - 消费者 :客户端通过
SUBSCRIBE命令订阅目的地,接收 Broker 推送的MESSAGE帧 - 消息代理(Broker):负责接收、路由、投递消息,管理订阅关系和会话状态
2. 目的地(Destination)
目的地是 STOMP 中的核心抽象,协议仅将其定义为不透明字符串,具体语义由 Broker 实现决定,行业通用约定:
/queue/xxx:队列模式,点对点消息,一条消息只会投递给一个消费者,支持负载均衡/topic/xxx:主题模式,发布/订阅广播,一条消息投递给所有订阅者/user/xxx:用户专属队列,用于定向给单个用户推送消息(Spring 扩展约定)
三、帧格式详解
STOMP 是面向帧的协议,所有通信都通过帧完成,帧结构与 HTTP 高度相似,整体分为 5 个部分:
COMMAND # 命令行(大写)
header1:value1 # 头部,每行一个 key:value
header2:value2
# 空行(\n\n),分隔头部和消息体
Body^@ # 消息体 + 结束符(NULL 字节,ASCII 0x00,写作 ^@)
各部分规则说明
- 命令行:必须为大写英文字母,以换行符结尾,代表帧的操作类型
- 头部 :
key:value格式,每行一个,顺序无关;支持重复头部,同 key 按顺序拼接content-length:特殊头部,指定消息体的字节长度;若存在则严格按长度读取,不受 NULL 字节影响;若不存在则读到第一个 NULL 字节结束- 常用头部:
destination、content-type、message-id、receipt、ack等
- 空行:头部和消息体的分隔符,必须存在(即使没有消息体)
- 消息体:可选,支持文本或二进制数据
- 结束符:必须以 NULL 字节(0x00)结尾,标记帧的结束
典型帧示例
客户端连接帧
CONNECT
accept-version:1.2
host:localhost
login:admin
passcode:admin
^@
服务端连接响应
CONNECTED
version:1.2
session:abc-12345
heart-beat:10000,10000
^@
客户端发送消息
SEND
destination:/queue/order
content-type:application/json
content-length:39
{"orderId":"1001","amount":99.9}
^@
服务端推送消息
MESSAGE
destination:/topic/news
message-id:msg-001
content-type:text/plain
Hello STOMP Protocol
^@
四、核心命令集
STOMP 命令分为客户端发起命令 和服务端响应命令两类。
1. 客户端命令
| 命令 | 作用 |
|---|---|
CONNECT |
与 Broker 建立连接,协商版本、认证、心跳 |
SEND |
向指定目的地发送消息 |
SUBSCRIBE |
订阅指定目的地,接收消息推送 |
UNSUBSCRIBE |
取消已有的订阅 |
ACK |
确认已成功消费消息,用于可靠投递 |
NACK |
否定确认,通知 Broker 消息未正常消费 |
BEGIN |
开启事务,后续消息进入事务上下文 |
COMMIT |
提交事务,批量生效事务内的所有消息 |
ABORT |
回滚事务,丢弃事务内的所有消息 |
DISCONNECT |
主动断开连接,可附带回执保证优雅断开 |
2. 服务端命令
| 命令 | 作用 |
|---|---|
CONNECTED |
连接成功响应,返回会话 ID、协商后的版本和心跳 |
MESSAGE |
向订阅者推送的消息帧 |
RECEIPT |
回执响应,对应客户端 receipt 头部请求,确认操作完成 |
ERROR |
错误响应,连接失败、协议错误时返回,通常附带错误信息 |
五、关键工作机制
1. 连接建立流程
- 客户端发送
CONNECT帧,携带支持的版本、认证信息、心跳参数 - Broker 校验通过后返回
CONNECTED帧,确认协议版本、分配会话 ID - 校验失败则返回
ERROR帧并关闭连接
2. 消息确认机制
为保证消息可靠投递,STOMP 提供三级确认模式,订阅时通过 ack 头部指定:
- auto(默认):自动确认,客户端收到消息即视为消费成功,无需手动 ACK
- client:客户端批量确认,调用 ACK 时会确认该订阅下所有未确认的消息
- client-individual:单条消息独立确认,每条消息都需要单独 ACK/NACK
3. 事务机制
通过 BEGIN / COMMIT / ABORT 实现消息的原子性批量操作:
- 事务开启后,所有 SEND、ACK、NACK 都会进入事务上下文
- 执行 COMMIT 后,所有操作一次性生效;执行 ABORT 则全部作废
- 事务仅在当前会话内有效,连接断开自动回滚
4. 回执机制
客户端发送命令时可携带 receipt:id 头部,Broker 处理完成后会返回对应 ID 的 RECEIPT 帧,用于保证关键操作(如发送、断开)的可靠性。
5. 心跳保活
STOMP 1.1+ 支持心跳机制,连接时通过 heart-beat:cx,sx 协商:
cx:客户端期望服务端每 x 毫秒发一次心跳sx:客户端每 x 毫秒向服务端发一次心跳- 心跳仅为单个换行符,不占用帧结构,用于检测连接断开、维持长连接
六、典型应用场景
1. WebSocket 实时通信(最主流场景)
原生 WebSocket 仅提供双向字节流,没有消息语义,需要自行设计格式、路由和订阅逻辑。STOMP 作为 WebSocket 子协议,可直接提供标准化的发布/订阅、消息路由、确认机制,是 Spring WebSocket 的默认方案,广泛用于:
- 在线聊天室、即时通知
- 实时数据大屏、股票行情推送
- 协作编辑、状态同步
2. 跨语言消息队列接入
STOMP 客户端实现成本极低,脚本语言、前端 JS 等生态可以快速接入 ActiveMQ、RabbitMQ 等企业级消息中间件,无需复杂的二进制协议解析。
3. 轻量级微服务通信
对于不需要 AMQP 复杂路由能力的微服务场景,STOMP 可以作为轻量替代,实现服务间的异步解耦。
七、与同类协议对比
| 维度 | STOMP | AMQP | MQTT | 原生 WebSocket |
|---|---|---|---|---|
| 协议类型 | 文本协议 | 二进制协议 | 二进制协议 | 传输层协议 |
| 设计目标 | 简单通用、易实现 | 企业级可靠消息 | 物联网低带宽 | 双向字节传输 |
| 消息语义 | 发布/订阅、队列 | 交换器、队列、路由、事务 | 发布/订阅、QoS | 无,需自行实现 |
| 实现难度 | 极低,客户端几十行代码 | 高,协议复杂 | 较低 | 低,但上层逻辑需自研 |
| 调试成本 | 低,文本可读,telnet 可直连 | 高,需专用工具 | 中 | 低,但业务数据难解析 |
| 典型场景 | Web实时通信、脚本语言接入 | 金融、企业级消息中间件 | 物联网、传感器 | 简单双向通信 |
八、主流实现
Broker 端
- Apache ActiveMQ / ActiveMQ Artemis:原生完整支持 STOMP 全版本
- RabbitMQ:通过
rabbitmq_stomp插件支持 - Spring Boot:内置简单 STOMP Broker,适合轻量 Web 场景
- Apollo:高性能 STOMP 专用 Broker
客户端库
- Java:Spring Messaging(Spring WebSocket 集成)
- JavaScript:
@stomp/stompjs(浏览器/Node.js 通用)、stomp.js - Python:
stomp.py - Go、Ruby、C# 等语言均有成熟客户端实现
九、优缺点总结
优点
- 极简易用:类 HTTP 的文本格式,学习成本低,客户端开发速度快
- 调试友好:明文传输,可直接通过 telnet、wscat 等工具调试交互
- 传输层无关:可运行在 TCP、TLS、WebSocket 等任意可靠流之上
- 生态完善:几乎所有主流消息中间件和开发语言都支持
- 能力均衡:覆盖消息确认、事务、心跳等核心可靠能力,又不至于过于复杂
缺点
- 文本协议开销:相比二进制协议,相同 payload 下传输体积更大,性能略低
- 语义依赖 Broker:协议本身不定义队列、主题的具体行为,不同 Broker 实现可能有差异
- 高级特性不足:缺乏 AMQP 那样标准化的路由、死信、优先级等企业级特性