Improve STOMP section of documentation

Issue: SPR-12579
This commit is contained in:
Rossen Stoyanchev 2015-07-07 16:16:49 -04:00
parent 08fb62570e
commit 6679120057
3 changed files with 100 additions and 64 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

View File

@ -984,11 +984,12 @@ public class WebSocketConfig extends WebSocketMessageBrokerConfigurationSupport
[[websocket-stomp]] [[websocket-stomp]]
== STOMP Over WebSocket Messaging Architecture == STOMP Over WebSocket Messaging Architecture
The WebSocket protocol defines two main types of messages -- text and binary -- The WebSocket protocol defines two types of messages, text and binary, but their
but leaves their content undefined. Instead it's expected that the client and content is undefined. It's expected that the client and server may agree on using
server may agree on using a sub-protocol, i.e. a higher-level protocol that defines a sub-protocol (i.e. a higher-level protocol) to define message semantics.
the message content. Using a sub-protocol is optional but either way the client While the use of a sub-protocol with WebSocket is completely optional either way
and server both need to understand how to interpret messages. client and server will need to agree on some kind of protocol to help interpret
messages.
@ -996,12 +997,14 @@ and server both need to understand how to interpret messages.
=== Overview of STOMP === Overview of STOMP
http://stomp.github.io/stomp-specification-1.2.html#Abstract[STOMP] is a simple http://stomp.github.io/stomp-specification-1.2.html#Abstract[STOMP] is a simple
text-oriented messaging protocol that was originally created for scripting languages text-oriented messaging protocol that was originally created for scripting languages
(such as Ruby, Python, and Perl) to connect to enterprise message brokers. It is such as Ruby, Python, and Perl to connect to enterprise message brokers. It is
designed to address a subset of commonly used patterns in messaging protocols. STOMP designed to address a subset of commonly used messaging patterns. STOMP can be
can be used over any reliable 2-way streaming network protocol such as TCP and WebSocket. used over any reliable 2-way streaming network protocol such as TCP and WebSocket.
Although STOMP is a text-oriented protocol, the payload of messages can be
either text or binary.
STOMP is a frame based protocol with frames modeled on HTTP. This is the STOMP is a frame based protocol whose frames are modeled on HTTP. The structure
structure of a frame: of a STOMP frame:
---- ----
COMMAND COMMAND
@ -1011,12 +1014,39 @@ header2:value2
Body^@ Body^@
---- ----
For example, a client can use the +SEND+ command to send a message or the Clients can use the +SEND+ or +SUBSCRIBE+ commands to send or subscribe for
+SUBSCRIBE+ command to express interest in receiving messages. Both of these commands messages along with a +"destination"+ header that describes what the
require a +"destination"+ header that indicates where to send a message, or likewise message is about and who should receive it. This enables a simple
what to subscribe to. publish-subscribe mechanism that can be used to send messages through the broker
to other connected clients or to send messages to the server to request that
some work be performed.
Here is an example of a client sending a request to buy stock shares: When using Spring's STOMP support, the Spring WebSocket application acts
as the STOMP broker to clients. Messages are routed to `@Controller` message-handling
methods or to a simple, in-memory broker that keeps track of subscriptions and
broadcasts messages to subscribed users. You can also configure Spring to work
with a dedicated STOMP broker (e.g. RabbitMQ, ActiveMQ, etc) for the actual
broadcasting of messages. In that case Spring maintains
TCP connections to the broker, relays messages to it, and also passes messages
from it down to connected WebSocket clients. Thus Spring web applications can
rely on unified HTTP-based security, common validation, and a familiar programming
model message-handling work.
Here is an example of a client subscribing to receive stock quotes which
the server may emit periodically e.g. via a scheduled task sending messages
through a `SimpMessagingTemplate` to the broker:
----
SUBSCRIBE
id:sub-1
destination:/topic/price.stock.*
^@
----
Here is an example of a client sending a trade request, which the server
may handle through an `@MessageMapping` method and later on, after the execution,
broadcast a trade confirmation message and details down to the client:
---- ----
SEND SEND
@ -1027,24 +1057,12 @@ content-length:44
{"action":"BUY","ticker":"MMM","shares",44}^@ {"action":"BUY","ticker":"MMM","shares",44}^@
---- ----
Here is an example of a client subscribing to receive stock quotes:
----
SUBSCRIBE
id:sub-1
destination:/topic/price.stock.*
^@
----
[NOTE]
====
The meaning of a destination is intentionally left opaque in the STOMP spec. It can The meaning of a destination is intentionally left opaque in the STOMP spec. It can
be any string, and it's entirely up to STOMP servers to define the semantics and be any string, and it's entirely up to STOMP servers to define the semantics and
the syntax of the destinations that they support. It is very common, however, for the syntax of the destinations that they support. It is very common, however, for
destinations to be path-like strings where `"/topic/.."` implies publish-subscribe destinations to be path-like strings where `"/topic/.."` implies publish-subscribe
(__one-to-many__) and `"/queue/"` implies point-to-point (__one-to-one__) message (__one-to-many__) and `"/queue/"` implies point-to-point (__one-to-one__) message
exchanges. exchanges.
====
STOMP servers can use the +MESSAGE+ command to broadcast messages to all subscribers. STOMP servers can use the +MESSAGE+ command to broadcast messages to all subscribers.
Here is an example of a server sending a stock quote to a subscribed client: Here is an example of a server sending a stock quote to a subscribed client:
@ -1058,26 +1076,21 @@ destination:/topic/price.stock.MMM
{"ticker":"MMM","price":129.45}^@ {"ticker":"MMM","price":129.45}^@
---- ----
[NOTE]
====
It is important to know that a server cannot send unsolicited messages. All messages It is important to know that a server cannot send unsolicited messages. All messages
from a server must be in response to a specific client subscription, and the from a server must be in response to a specific client subscription, and the
+"subscription-id"+ header of the server message must match the +"id"+ header of the +"subscription-id"+ header of the server message must match the +"id"+ header of the
client subscription. client subscription.
====
The above overview is intended to provide the most basic understanding of the The above overview is intended to provide the most basic understanding of the
STOMP protocol. It is recommended to review the protocol STOMP protocol. It is recommended to review the protocol
http://stomp.github.io/stomp-specification-1.2.html[specification], which is http://stomp.github.io/stomp-specification-1.2.html[specification] in full.
easy to follow and manageable in terms of size.
The following summarizes the benefits for an application of using STOMP over WebSocket: The benefits of using STOMP as a WebSocket sub-protocol:
* Standard message format * No need to invent a custom message format
* Application-level protocol with support for common messaging patterns * Use existing https://github.com/jmesnil/stomp-websocket[stomp.js] client in the browser
* Client-side support, e.g. https://github.com/jmesnil/stomp-websocket[stomp.js], https://github.com/cujojs/msgs[msgs.js] * Ability to route messages to based on destination
* The ability to interpret, route, and process messages on both the client and server-side * Option to use full-fledged message broker such as RabbitMQ, ActiveMQ, etc. for broadcasting
* The option to plug in a message broker -- RabbitMQ, ActiveMQ, many others -- to broadcast messages (explained later)
Most importantly the use of STOMP (vs plain WebSocket) enables the Spring Framework Most importantly the use of STOMP (vs plain WebSocket) enables the Spring Framework
to provide a programming model for application-level use in the same way that to provide a programming model for application-level use in the same way that
@ -1088,10 +1101,12 @@ Spring MVC provides a programming model based on HTTP.
[[websocket-stomp-enable]] [[websocket-stomp-enable]]
=== Enable STOMP over WebSocket === Enable STOMP over WebSocket
The Spring Framework provides support for using STOMP over WebSocket through The Spring Framework provides support for using STOMP over WebSocket through
the +spring-messaging+ and +spring-websocket+ modules. It's easy to enable it. the +spring-messaging+ and +spring-websocket+ modules.
Here is an example of exposing a STOMP WebSocket/SockJS endpoint at the URL path
Here is an example of configuring a STOMP WebSocket endpoint with SockJS fallback `/portfolio` where messages whose destination starts with "/app" are routed to
options. The endpoint is available for clients to connect to a URL path `/portfolio`: message-handling methods (i.e. application work) and messages whose destinations
start with "/topic" or "/queue" will be routed to the message broker (i.e.
broadcasting to other connected clients):
[source,java,indent=0] [source,java,indent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
@ -1103,23 +1118,21 @@ options. The endpoint is available for clients to connect to a URL path `/portfo
@EnableWebSocketMessageBroker @EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer { public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.setApplicationDestinationPrefixes("/app");
config.enableSimpleBroker("/queue", "/topic");
}
@Override @Override
public void registerStompEndpoints(StompEndpointRegistry registry) { public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/portfolio").withSockJS(); registry.addEndpoint("/portfolio").withSockJS();
} }
// ... @Override
public void configureMessageBroker(MessageBrokerRegistry config) {
config.setApplicationDestinationPrefixes("/app");
config.enableSimpleBroker("/topic", "/queue");
}
} }
---- ----
XML configuration equivalent: and in XML:
[source,xml,indent=0] [source,xml,indent=0]
[subs="verbatim,quotes,attributes"] [subs="verbatim,quotes,attributes"]
@ -1137,13 +1150,29 @@ XML configuration equivalent:
<websocket:stomp-endpoint path="/portfolio"> <websocket:stomp-endpoint path="/portfolio">
<websocket:sockjs/> <websocket:sockjs/>
</websocket:stomp-endpoint> </websocket:stomp-endpoint>
<websocket:simple-broker prefix="/queue, /topic"/> <websocket:simple-broker prefix="/topic, /queue"/>
...
</websocket:message-broker> </websocket:message-broker>
</beans> </beans>
---- ----
[NOTE]
====
The "/app" prefix is arbitrary. You can pick any prefix. It's simply meant to differentiate
messages to be routed to message-handling methods to do application work vs messages
to be routed to the broker to broadcast to subscribed clients.
The "/topic" and "/queue" prefixes depend on the broker in use. In the case of the simple,
in-memory broker the prefixes do not have any special meaning; it's merely a convention
that indicates how the destination is used (pub-sub targetting many subscribers or
point-to-point messages typically targeting an individual recipient).
In the case of using a dedicated broker, most brokers use "/topic" as
a prefix for destinations with pub-sub semantics and "/queue" for destinations
with point-to-point semantics. Check the STOMP page of the broker to see the destination
semantics it supports.
====
On the browser side, a client might connect as follows using On the browser side, a client might connect as follows using
https://github.com/jmesnil/stomp-websocket[stomp.js] and the https://github.com/jmesnil/stomp-websocket[stomp.js] and the
https://github.com/sockjs/sockjs-client[sockjs-client]: https://github.com/sockjs/sockjs-client[sockjs-client]:
@ -1180,10 +1209,11 @@ sections <<websocket-stomp-handle-broker-relay-configure>> and
=== Flow of Messages === Flow of Messages
When a STOMP endpoint is configured, the Spring application acts as the STOMP broker When a STOMP endpoint is configured, the Spring application acts as the STOMP broker
to connected clients. It handles incoming messages and sends messages back. to connected clients. This section provides a big picture overview of how messages flow
This section provides a big picture overview of how messages flow inside the application. within the application.
The `spring-messaging` module contains a number of abstractions that originated in the The `spring-messaging` module provides the foundation for asynchronous message processing.
It contains a number of abstractions that originated in the
https://spring.io/spring-integration[Spring Integration] project and are intended https://spring.io/spring-integration[Spring Integration] project and are intended
for use as building blocks in messaging applications: for use as building blocks in messaging applications:
@ -1199,16 +1229,22 @@ extends `MessageChannel` and sends messages to registered `MessageHandler` subsc
a concrete implementation of `SubscribableChannel` that can deliver messages a concrete implementation of `SubscribableChannel` that can deliver messages
asynchronously via a thread pool. asynchronously via a thread pool.
The provided STOMP over WebSocket config, both Java and XML, uses the above to The `@EnableWebSocketMessageBroker` Java config and the `<websocket:message-broker>` XML config
assemble a concrete message flow including the following 3 channels: both assemble a concrete message flow. Below is a diagram of the part of the setup when using
the simple, in-memory broker:
* `"clientInboundChannel"` -- for messages from WebSocket clients. Every incoming image::images/message-flow-simple-broker.png[width=640]
WebSocket message carrying a STOMP frame is sent through this channel.
* `"clientOutboundChannel"` -- for messages to WebSocket clients. Every outgoing The above setup that includes 3 message channels:
STOMP message from the broker is sent through this channel before getting sent
to a client's WebSocket session. * `"clientInboundChannel"` for messages from WebSocket clients.
* `"brokerChannel"` -- for messages to the broker from within the application. * `"clientOutboundChannel"` for messages to WebSocket clients.
Every message sent from the application to the broker passes through this channel. * `"brokerChannel"` for messages to the broker from within the application.
The same three channels are also used with a dedicated broker except here a
"broker relay" takes the place of the simple broker:
image::images/message-flow-broker-relay.png[width=640]
Messages on the `"clientInboundChannel"` can flow to annotated Messages on the `"clientInboundChannel"` can flow to annotated
methods for application handling (e.g. a stock trade execution request) or can methods for application handling (e.g. a stock trade execution request) or can