學(xué)校網(wǎng)站建設(shè)的優(yōu)勢(shì)和不足成人用品推廣網(wǎng)頁(yè)
STOMP(Simple Text Oriented Messaging Protocol,簡(jiǎn)單面向文本的消息傳遞協(xié)議)是一種輕量級(jí)、基于文本的協(xié)議,旨在為消息代理(消息隊(duì)列)和客戶端之間的通信(websocket)提供一種簡(jiǎn)單的接口。它通常運(yùn)行在TCP或WebSocket之上,并廣泛用于實(shí)現(xiàn)發(fā)布/訂閱、點(diǎn)對(duì)點(diǎn)消息傳遞等模式。
STOMP提供了一種簡(jiǎn)單的機(jī)制來(lái)發(fā)送和接收消息,適用于各種消息中間件系統(tǒng),如ActiveMQ、RabbitMQ和Apache Kafka等。
特點(diǎn):
- 基于文本的協(xié)議,簡(jiǎn)單易用,適合快速開發(fā)。
- 支持多種消息傳遞模式(如發(fā)布/訂閱、點(diǎn)對(duì)點(diǎn))。
- 支持事務(wù)和消息確認(rèn)機(jī)制。
- 輕量級(jí),適用于資源受限的環(huán)境(寬帶低等)。
- 支持多種消息代理(如RabbitMQ、ActiveMQ)。
- 支持客戶端之間的通信(如:websocket)
適用場(chǎng)景:
- 實(shí)時(shí)通信應(yīng)用(如聊天系統(tǒng)、社交網(wǎng)絡(luò))。
- 微服務(wù)之間的異步通信。
- IoT設(shè)備之間的輕量級(jí)通信。
1、STOMP基本概念
(1)、目的地(Destination)
目的地是消息發(fā)送或接收的目標(biāo)地址。
常見的目的地類型包括:
- 隊(duì)列(Queue):點(diǎn)對(duì)點(diǎn)消息傳遞模式,每條消息只會(huì)被一個(gè)消費(fèi)者處理。
- 主題(Topic):發(fā)布/訂閱模式,每條消息會(huì)被所有訂閱者處理。
(2)、命令(Commands)
STOMP定義了若干命令,用于控制消息的發(fā)送、接收和管理。
常見的命令包括:
- CONNECT:建立連接。
- SEND:發(fā)送消息到指定的目的地。
- SUBSCRIBE:訂閱某個(gè)目的地,接收該目的地的消息。
- UNSUBSCRIBE:取消訂閱某個(gè)目的地。
- ACK:確認(rèn)消息已被成功處理。
- NACK:拒絕或無(wú)法處理消息。
- DISCONNECT:斷開連接。
(3)、頭信息(Headers)
頭信息是伴隨每個(gè)命令的鍵值對(duì),用于傳遞額外的元數(shù)據(jù)。
常見的頭信息包括:
- destination:消息的目的地。
- id:訂閱的唯一標(biāo)識(shí)符。
- receipt:請(qǐng)求服務(wù)器返回一個(gè)收據(jù),確認(rèn)命令已執(zhí)行。
(4)、消息體(Body)
消息體包含實(shí)際的消息內(nèi)容,可以是任意格式的數(shù)據(jù)(如JSON、XML、純文本等)。消息體必須以空字節(jié)\u0000結(jié)束。
2、STOMP消息格式
每條STOMP消息由命令行、頭信息和消息體組成,各部分之間用換行符\n分隔,整個(gè)消息以兩個(gè)連續(xù)的換行符\n\n結(jié)束。
示例:CONNECT命令
CONNECT
accept-version:1.2
host:stomp.example.com^@
解釋:
- CONNECT:命令名稱。
- accept-version:1.2:表示支持的STOMP版本。
- host:stomp.example.com:目標(biāo)主機(jī)。
- ^@:表示消息體為空,用一個(gè)空字節(jié)(ASCII碼為0)來(lái)表示。
示例:SEND命令
SEND
destination:/queue/workHello, STOMP!
^@
解釋:
- SEND:命令名稱。
- destination:/queue/work:消息的目的地。
- Hello, STOMP!:消息的內(nèi)容。
- ^@:表示消息結(jié)束。
示例:SUBSCRIBE命令
SUBSCRIBE
id:sub-001
destination:/topic/greetings^@
解釋:
- SUBSCRIBE:命令名稱。
- id:sub-001:訂閱的唯一標(biāo)識(shí)符。
- destination:/topic/greetings:要訂閱的主題地址。
- ^@:表示消息體為空。
3、STOMP工作流程
原理示意圖:
(1)、連接
客戶端首先需要通過CONNECT命令與STOMP服務(wù)器建立連接。如果連接成功,服務(wù)器會(huì)返回一個(gè)CONNECTED響應(yīng)。
客戶端示例:
CONNECT
accept-version:1.2
host:stomp.example.com^@
服務(wù)器響應(yīng)示例:
CONNECTED
version:1.2
session:session-id-12345^@
(2)、訂閱
客戶端可以通過SUBSCRIBE命令訂閱某個(gè)目的地,接收該目的地的消息。
客戶端示例:
SUBSCRIBE
id:sub-001
destination:/topic/greetings^@
(3)、發(fā)送消息
客戶端可以通過SEND命令向某個(gè)目的地發(fā)送消息。
客戶端示例:
SEND
destination:/queue/workHello, STOMP!
^@
(4)、接收消息
當(dāng)有消息到達(dá)客戶端訂閱的目的地時(shí),服務(wù)器會(huì)將消息推送到客戶端。
消息示例:
MESSAGE
subscription:sub-001
message-id:message-id-67890
destination:/topic/greetingsHello, World!
^@
(5)、斷開連接
客戶端可以通過DISCONNECT命令斷開與服務(wù)器的連接。
客戶端示例:
DISCONNECT^@
4、在WebSocket上使用STOMP
在WebSocket之上使用STOMP時(shí),STOMP消息作為WebSocket數(shù)據(jù)幀的有效載荷進(jìn)行傳輸。這種方式結(jié)合了WebSocket的全雙工通信能力和STOMP的結(jié)構(gòu)化消息傳遞功能。
示例:JavaScript中使用WebSocket和STOMP
const socket = new WebSocket('ws://example.com/stomp-endpoint'); // 建立websocket連接socket.onopen = function() {// 發(fā)送CONNECT命令const connectMessage = CONNECT\naccept-version:1.2\nhost:example.com\n\n\u0000;socket.send(connectMessage);// 發(fā)送SUBSCRIBE命令const subscribeMessage = SUBSCRIBE\nid:sub-001\ndestination:/topic/greetings\n\n\u0000;socket.send(subscribeMessage);
};socket.onmessage = function(event) {console.log('Received:', event.data);// 解析收到的STOMP消息if (event.data.startsWith('MESSAGE')) {console.log('New message:', event.data.split('\n\n')[1].trim());}
};
5、代碼示例
依賴庫(kù):
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-messaging</artifactId>
</dependency>
發(fā)送消息:
import org.springframework.messaging.converter.MappingJackson2MessageConverter;
import org.springframework.messaging.simp.stomp.StompFrameHandler;
import org.springframework.messaging.simp.stomp.StompHeaders;
import org.springframework.messaging.simp.stomp.StompSession;
import org.springframework.messaging.simp.stomp.StompSessionHandlerAdapter;
import org.springframework.web.socket.client.standard.StandardWebSocketClient;
import org.springframework.web.socket.messaging.WebSocketStompClient;
import org.springframework.web.socket.sockjs.client.SockJsClient;
import org.springframework.web.socket.sockjs.client.Transport;
import org.springframework.web.socket.sockjs.client.WebSocketTransport;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.concurrent.ExecutionException;public class StompProducer {public static void main(String[] args) throws ExecutionException, InterruptedException {// 創(chuàng)建 WebSocket 客戶端StandardWebSocketClient wsClient = new StandardWebSocketClient();SockJsClient sockJsClient = new SockJsClient(Collections.singletonList(new WebSocketTransport(wsClient)));// 創(chuàng)建STOMP客戶端WebSocketStompClient stompClient = new WebSocketStompClient(sockJsClient);stompClient.setMessageConverter(new MappingJackson2MessageConverter());// 連接到STOMP代理StompSession session = stompClient.connect("ws://localhost:8080/ws", new StompSessionHandlerAdapter() {}).get();// 發(fā)送消息StompHeaders headers = new StompHeaders();headers.setDestination("/app/hello");session.send(headers, "Hello, STOMP!");System.out.println("Message sent");}
}
接收消息:
import org.springframework.messaging.converter.MappingJackson2MessageConverter;
import org.springframework.messaging.simp.stomp.StompFrameHandler;
import org.springframework.messaging.simp.stomp.StompHeaders;
import org.springframework.messaging.simp.stomp.StompSession;
import org.springframework.messaging.simp.stomp.StompSessionHandlerAdapter;
import org.springframework.web.socket.client.standard.StandardWebSocketClient;
import org.springframework.web.socket.messaging.WebSocketStompClient;
import org.springframework.web.socket.sockjs.client.SockJsClient;
import org.springframework.web.socket.sockjs.client.Transport;
import org.springframework.web.socket.sockjs.client.WebSocketTransport;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.concurrent.ExecutionException;public class StompConsumer {public static void main(String[] args) throws ExecutionException, InterruptedException {// 創(chuàng)建WebSocket客戶端StandardWebSocketClient wsClient = new StandardWebSocketClient();SockJsClient sockJsClient = new SockJsClient(Collections.singletonList(new WebSocketTransport(wsClient)));// 創(chuàng)建STOMP客戶端WebSocketStompClient stompClient = new WebSocketStompClient(sockJsClient);stompClient.setMessageConverter(new MappingJackson2MessageConverter());// 連接到STOMP代理StompSession session = stompClient.connect("ws://localhost:8080/ws", new StompSessionHandlerAdapter() {}).get();// 訂閱主題并設(shè)置回調(diào)session.subscribe("/topic/greetings", new StompFrameHandler() {@Overridepublic Type getPayloadType(StompHeaders headers) {return String.class;}@Overridepublic void handleFrame(StompHeaders headers, Object payload) {System.out.println("Received message: " + payload);}});// 保持連接Thread.sleep(Long.MAX_VALUE);}
}
6、總結(jié)
STOMP是一種簡(jiǎn)單而強(qiáng)大的消息傳遞協(xié)議,特別適合于需要靈活消息路由的應(yīng)用場(chǎng)景。通過運(yùn)行在WebSocket或其他傳輸協(xié)議之上。
STOMP提供了以下優(yōu)勢(shì):
- 易用性:基于文本的協(xié)議,易于實(shí)現(xiàn)和調(diào)試。
- 靈活性:支持多種消息傳遞模式,適應(yīng)不同的應(yīng)用場(chǎng)景。
- 跨平臺(tái):可以在多種編程語(yǔ)言和平臺(tái)上使用,具有良好的互操作性。
通過理解STOMP的基本概念、命令和工作流程,開發(fā)者可以有效地利用這一協(xié)議構(gòu)建高效的消息傳遞系統(tǒng)。
乘風(fēng)破浪!Dare to Be!!!