Update STOMP overview

Issue: SPR-15624
This commit is contained in:
Rossen Stoyanchev 2018-01-18 13:04:43 -05:00
parent c53c8bfc5a
commit 513461d4f1
1 changed files with 101 additions and 88 deletions

View File

@ -850,23 +850,23 @@ Consider also customizing these server-side SockJS related properties (see Javad
== STOMP == STOMP
The WebSocket protocol defines two types of messages, text and binary, but their The WebSocket protocol defines two types of messages, text and binary, but their
content is undefined. It's expected that the client and server may agree on using content is undefined. The defines a mechanism for client and server to negotiate a
a sub-protocol (i.e. a higher-level protocol) to define message semantics. sub-protocol -- i.e. a higher level messaging protocol, to use on top of WebSocket to
While the use of a sub-protocol with WebSocket is completely optional either way define what kind of messages each can send, what is the format and content for each
client and server will need to agree on some kind of protocol to help interpret message, and so on. The use of a sub-protocol is optional but either way client and
messages. server will need to agree on some protocol that defines message content.
[[websocket-stomp-overview]] [[websocket-stomp-overview]]
=== Overview === Overview
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 messaging patterns. STOMP can be designed to address a minimal subset of commonly used messaging patterns. STOMP 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 Although STOMP is a text-oriented protocol, message payloads can be
either text or binary. either text or binary.
STOMP is a frame based protocol whose frames are modeled on HTTP. The structure STOMP is a frame based protocol whose frames are modeled on HTTP. The structure
@ -951,29 +951,34 @@ 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] in full. http://stomp.github.io/stomp-specification-1.2.html[specification] in full.
The benefits of using STOMP as a WebSocket sub-protocol:
* No need to invent a custom message format
* Use existing https://github.com/jmesnil/stomp-websocket[stomp.js] client in the browser
* Ability to route messages to based on destination
* Option to use full-fledged message broker such as RabbitMQ, ActiveMQ, etc. for broadcasting
Most importantly the use of STOMP (vs plain WebSocket) enables the Spring Framework [[websocket-stomp-benefits]]
to provide a programming model for application-level use in the same way that === Benefits
Spring MVC provides a programming model based on HTTP.
Use of STOMP as a sub-protocol enables the Spring Framework and Spring Security to
provide a richer programming model vs using raw WebSockets. The same point can be
made about how HTTP vs raw TCP and how it enables Spring MVC and other web frameworks
to provide rich functionality. The following is a list of benefits:
* No need to invent a custom messaging protocol and message format.
* STOMP clients are available including a <<websocket-stomp-client,Java client>>
in the Spring Framework.
* Message brokers such as RabbitMQ, ActiveMQ, and others can be used (optionally) to
manage subscriptions and broadcast messages.
* Application logic can be organized in any number of ``@Controller``'s and messages
routed to them based on the STOMP destination header vs handling raw WebSocket messages
with a single `WebSocketHandler` for a given connection.
* Use Spring Security to secure messages based on STOMP destinations and message types.
[[websocket-stomp-enable]] [[websocket-stomp-enable]]
=== Enable STOMP === Enable STOMP
The Spring Framework provides support for using STOMP over WebSocket through STOMP over WebSocket support is available in the `spring-messaging` and the
the +spring-messaging+ and +spring-websocket+ modules. `spring-websocket` modules. Once you have those dependencies, you can expose a STOMP
Here is an example of exposing a STOMP WebSocket/SockJS endpoint at the URL path endpoints, over WebSocket with <<websocket-fallback>>, as shown below:
`/portfolio` where messages whose destination starts with "/app" are routed to
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"]
@ -987,18 +992,25 @@ broadcasting to other connected clients):
@Override @Override
public void registerStompEndpoints(StompEndpointRegistry registry) { public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/portfolio").withSockJS(); registry.addEndpoint("/portfolio").withSockJS(); // <1>
} }
@Override @Override
public void configureMessageBroker(MessageBrokerRegistry config) { public void configureMessageBroker(MessageBrokerRegistry config) {
config.setApplicationDestinationPrefixes("/app"); config.setApplicationDestinationPrefixes("/app"); // <2>
config.enableSimpleBroker("/topic", "/queue"); config.enableSimpleBroker("/topic", "/queue"); // <3>
} }
} }
---- ----
and in XML: <1> `"/portfolio"` is the HTTP URL for the endpoint to which a WebSocket (or SockJS)
client will need to connect to for the WebSocket handshake.
<2> STOMP messages whose destination header begins with `"/app"` are routed to
`@MessageMapping` methods in `@Controller` classes.
<3> Use the built-in, message broker for subscriptions and broadcasting;
Route messages whose destination header begins with "/topic" or "/queue" to the broker.
The same configuration in XML:
[source,xml,indent=0] [source,xml,indent=0]
[subs="verbatim,quotes,attributes"] [subs="verbatim,quotes,attributes"]
@ -1024,18 +1036,11 @@ and in XML:
[NOTE] [NOTE]
==== ====
The "/app" prefix is arbitrary. You can pick any prefix. It's simply meant to differentiate For the built-in, simple broker the "/topic" and "/queue" prefixes do not have any special
messages to be routed to message-handling methods to do application work vs messages meaning. They're merely a convention to differentiate between pub-sub vs point-to-point
to be routed to the broker to broadcast to subscribed clients. messaging (i.e. many subscribers vs one consumer). When using an external broker, please
check the STOMP page of the broker to understand what kind of STOMP destinations and
The "/topic" and "/queue" prefixes depend on the broker in use. In the case of the simple, prefixes it supports.
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.
==== ====
@ -1075,60 +1080,64 @@ sections <<websocket-stomp-handle-broker-relay-configure>> and
[[websocket-stomp-message-flow]] [[websocket-stomp-message-flow]]
=== Flow of Messages === Flow of Messages
When a STOMP endpoint is configured, the Spring application acts as the STOMP broker Once a STOMP endpoint is exposed, the Spring application becomes a STOMP broker for
to connected clients. This section provides a big picture overview of how messages flow connected clients. This section describes the flow of messages on the server side.
within the application.
The `spring-messaging` module provides the foundation for asynchronous message processing. The `spring-messaging` module contains foundational support for messaging applications
It contains a number of abstractions that originated in the that originated in https://spring.io/spring-integration[Spring Integration] and was
https://spring.io/spring-integration[Spring Integration] project and are intended later extracted and incorporated into the Spring Framework for broader use across many
for use as building blocks in messaging applications: https://spring.io/projects[Spring projects] and application scenarios.
Below is a list of a few of the available messaging abstractions:
* {api-spring-framework}/messaging/Message.html[Message] -- * {api-spring-framework}/messaging/Message.html[Message] --
a message with headers and a payload. simple representation for a message including headers and payload.
* {api-spring-framework}/messaging/MessageHandler.html[MessageHandler] -- * {api-spring-framework}/messaging/MessageHandler.html[MessageHandler] --
a contract for handling a message. contract for handling a message.
* {api-spring-framework}/messaging/MessageChannel.html[MessageChannel] -- * {api-spring-framework}/messaging/MessageChannel.html[MessageChannel] --
a contract for sending a message enabling loose coupling between senders and receivers. contract for sending a message that enables loose coupling between producers and consumers.
* {api-spring-framework}/messaging/SubscribableChannel.html[SubscribableChannel] -- * {api-spring-framework}/messaging/SubscribableChannel.html[SubscribableChannel] --
extends `MessageChannel` and sends messages to registered `MessageHandler` subscribers. `MessageChannel` with `MessageHandler` subscribers.
* {api-spring-framework}/messaging/support/ExecutorSubscribableChannel.html[ExecutorSubscribableChannel] -- * {api-spring-framework}/messaging/support/ExecutorSubscribableChannel.html[ExecutorSubscribableChannel] --
a concrete implementation of `SubscribableChannel` that can deliver messages `SubscribableChannel` that uses an `Executor` for delivering messages.
asynchronously via a thread pool.
The `@EnableWebSocketMessageBroker` Java config and the `<websocket:message-broker>` XML config Both the Java config (i.e. `@EnableWebSocketMessageBroker`) and the XML namespace config
both assemble a concrete message flow. Below is a diagram of the part of the setup when using (i.e. `<websocket:message-broker>`) use the above components to assemble a message
the simple, in-memory broker: workflow. The diagram below shows the components used when the simple, built-in message
broker is enabled:
image::images/message-flow-simple-broker.png[] image::images/message-flow-simple-broker.png[]
The above setup that includes 3 message channels: There are 3 message channels in the above diagram:
* `"clientInboundChannel"` for messages from WebSocket clients. * `"clientInboundChannel"` -- for passing messages received from WebSocket clients.
* `"clientOutboundChannel"` for messages to WebSocket clients. * `"clientOutboundChannel"` -- for sending server messages to WebSocket clients.
* `"brokerChannel"` for messages to the broker from within the application. * `"brokerChannel"` -- for sending messages to the message broker from within
server-side, application code.
The same three channels are also used with a dedicated broker except here a The next diagram shows the components used when an external broker (e.g. RabbitMQ)
"broker relay" takes the place of the simple broker: is configured for managing subscriptions and broadcasting messages:
image::images/message-flow-broker-relay.png[] image::images/message-flow-broker-relay.png[]
Messages on the `"clientInboundChannel"` can flow to annotated The main difference in the above diagram is the use of the "broker relay" for passing
methods for application handling (e.g. a stock trade execution request) or can messages up to the external STOMP broker over TCP, and for passing messages down from the
be forwarded to the broker (e.g. client subscribing for stock quotes). broker to subscribed clients.
The STOMP destination is used for simple prefix-based routing. For example
the "/app" prefix could route messages to annotated methods while the "/topic"
and "/queue" prefixes could route messages to the broker.
When a message-handling annotated method has a return type, its return When messages are received from a WebSocket connectin, they're decoded to STOMP frames,
value is sent as the payload of a Spring `Message` to the `"brokerChannel"`. then turned into a Spring `Message` representation, and sent to the
The broker in turn broadcasts the message to clients. Sending a message `"clientInboundChannel"` for further processing. For example STOMP messages whose
to a destination can also be done from anywhere in the application with destination header starts with `"/app"` may be routed to `@MessageMapping` methods in
the help of a messaging template. For example, an HTTP POST handling method annotated controllers, while `"/topic"` and `"/queue"` messages may be routed directly
can broadcast a message to connected clients, or a service component may to the message broker.
periodically broadcast stock quotes.
Below is a simple example to illustrate the flow of messages: An annotated `@Controller` handling a STOMP message from a client may send a message to
the message broker through the `"brokerChannel"`, and the broker will broadcast the
message to matching subscribers through the `"clientOutboundChannel"`. The same
controller can also do the same in response to HTTP requests, so a client may perform an
HTTP POST and then an `@PostMapping` method can send a message to the message broker
to broadcast to subscribed clients.
Let's trace the flow through a simple example. Given the following server setup:
[source,java,indent=0] [source,java,indent=0]
[subs="verbatim,quotes"] [subs="verbatim,quotes"]
@ -1162,18 +1171,22 @@ Below is a simple example to illustrate the flow of messages:
---- ----
The following explains the message flow for the above example: . Client connects to `"http://localhost:8080/portfolio"` and once a WebSocket connection
is established, STOMP frames begin to flow on it.
* WebSocket clients connect to the WebSocket endpoint at "/portfolio". . Client sends SUBSCRIBE frame with destination header `"/topic/greeting"`. Once received
* Subscriptions to "/topic/greeting" pass through the "clientInboundChannel" and decoded, the message is sent to the `"clientInboundChannel"`, then routed to the
and are forwarded to the broker. message broker which stores the client subscription.
* Greetings sent to "/app/greeting" pass through the "clientInboundChannel" . Client sends SEND frame to `"/app/greeting"`. The `"/app"` prefix helps to route it to
and are forwarded to the `GreetingController`. The controller adds the current annotated controllers. After the `"/app"` prefix is stripped, the remaining `"/greeting"`
time, and the return value is passed through the "brokerChannel" as a message part of the destination is mapped to the `@MessageMapping` method in `GreetingController`.
to "/topic/greeting" (destination is selected based on a convention but can be . The value returned from `GreetingController` is turned into a Spring `Message` with
overridden via `@SendTo`). a payload based on the return value and a default destination header of
* The broker in turn broadcasts messages to subscribers, and they pass through `"/topic/greeting"` (derived from the input destination with `"/app"` replaced by
the `"clientOutboundChannel"`. `"/topic"`). The resulting message is sent to the "brokerChannel" and handled
by the message broker.
. The message broker finds all matching subscribers, and sends a MESSAGE frame to each
through the `"clientOutboundChannel"` from where messages are encoded as STOMP frames
and sent on the WebSocket connection.
The next section provides more details on annotated methods including the The next section provides more details on annotated methods including the
kinds of arguments and return values supported. kinds of arguments and return values supported.