Skip to content

Commit 513461d

Browse files
committed
Update STOMP overview
Issue: SPR-15624
1 parent c53c8bf commit 513461d

File tree

1 file changed

+101
-88
lines changed

1 file changed

+101
-88
lines changed

src/docs/asciidoc/web/websocket.adoc

Lines changed: 101 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -850,23 +850,23 @@ Consider also customizing these server-side SockJS related properties (see Javad
850850
== STOMP
851851

852852
The WebSocket protocol defines two types of messages, text and binary, but their
853-
content is undefined. It's expected that the client and server may agree on using
854-
a sub-protocol (i.e. a higher-level protocol) to define message semantics.
855-
While the use of a sub-protocol with WebSocket is completely optional either way
856-
client and server will need to agree on some kind of protocol to help interpret
857-
messages.
853+
content is undefined. The defines a mechanism for client and server to negotiate a
854+
sub-protocol -- i.e. a higher level messaging protocol, to use on top of WebSocket to
855+
define what kind of messages each can send, what is the format and content for each
856+
message, and so on. The use of a sub-protocol is optional but either way client and
857+
server will need to agree on some protocol that defines message content.
858858

859859

860860

861861
[[websocket-stomp-overview]]
862862
=== Overview
863863

864-
http://stomp.github.io/stomp-specification-1.2.html#Abstract[STOMP] is a simple
864+
http://stomp.github.io/stomp-specification-1.2.html#Abstract[STOMP] is a simple,
865865
text-oriented messaging protocol that was originally created for scripting languages
866866
such as Ruby, Python, and Perl to connect to enterprise message brokers. It is
867-
designed to address a subset of commonly used messaging patterns. STOMP can be
868-
used over any reliable 2-way streaming network protocol such as TCP and WebSocket.
869-
Although STOMP is a text-oriented protocol, the payload of messages can be
867+
designed to address a minimal subset of commonly used messaging patterns. STOMP can be
868+
used over any reliable, 2-way streaming network protocol such as TCP and WebSocket.
869+
Although STOMP is a text-oriented protocol, message payloads can be
870870
either text or binary.
871871

872872
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
951951
STOMP protocol. It is recommended to review the protocol
952952
http://stomp.github.io/stomp-specification-1.2.html[specification] in full.
953953

954-
The benefits of using STOMP as a WebSocket sub-protocol:
955954

956-
* No need to invent a custom message format
957-
* Use existing https://github.com/jmesnil/stomp-websocket[stomp.js] client in the browser
958-
* Ability to route messages to based on destination
959-
* Option to use full-fledged message broker such as RabbitMQ, ActiveMQ, etc. for broadcasting
960955

961-
Most importantly the use of STOMP (vs plain WebSocket) enables the Spring Framework
962-
to provide a programming model for application-level use in the same way that
963-
Spring MVC provides a programming model based on HTTP.
956+
[[websocket-stomp-benefits]]
957+
=== Benefits
958+
959+
Use of STOMP as a sub-protocol enables the Spring Framework and Spring Security to
960+
provide a richer programming model vs using raw WebSockets. The same point can be
961+
made about how HTTP vs raw TCP and how it enables Spring MVC and other web frameworks
962+
to provide rich functionality. The following is a list of benefits:
963+
964+
* No need to invent a custom messaging protocol and message format.
965+
* STOMP clients are available including a <<websocket-stomp-client,Java client>>
966+
in the Spring Framework.
967+
* Message brokers such as RabbitMQ, ActiveMQ, and others can be used (optionally) to
968+
manage subscriptions and broadcast messages.
969+
* Application logic can be organized in any number of ``@Controller``'s and messages
970+
routed to them based on the STOMP destination header vs handling raw WebSocket messages
971+
with a single `WebSocketHandler` for a given connection.
972+
* Use Spring Security to secure messages based on STOMP destinations and message types.
964973

965974

966975

967976
[[websocket-stomp-enable]]
968977
=== Enable STOMP
969978

970-
The Spring Framework provides support for using STOMP over WebSocket through
971-
the +spring-messaging+ and +spring-websocket+ modules.
972-
Here is an example of exposing a STOMP WebSocket/SockJS endpoint at the URL path
973-
`/portfolio` where messages whose destination starts with "/app" are routed to
974-
message-handling methods (i.e. application work) and messages whose destinations
975-
start with "/topic" or "/queue" will be routed to the message broker (i.e.
976-
broadcasting to other connected clients):
979+
STOMP over WebSocket support is available in the `spring-messaging` and the
980+
`spring-websocket` modules. Once you have those dependencies, you can expose a STOMP
981+
endpoints, over WebSocket with <<websocket-fallback>>, as shown below:
977982

978983
[source,java,indent=0]
979984
[subs="verbatim,quotes"]
@@ -987,18 +992,25 @@ broadcasting to other connected clients):
987992
988993
@Override
989994
public void registerStompEndpoints(StompEndpointRegistry registry) {
990-
registry.addEndpoint("/portfolio").withSockJS();
995+
registry.addEndpoint("/portfolio").withSockJS(); // <1>
991996
}
992997
993998
@Override
994999
public void configureMessageBroker(MessageBrokerRegistry config) {
995-
config.setApplicationDestinationPrefixes("/app");
996-
config.enableSimpleBroker("/topic", "/queue");
1000+
config.setApplicationDestinationPrefixes("/app"); // <2>
1001+
config.enableSimpleBroker("/topic", "/queue"); // <3>
9971002
}
9981003
}
9991004
----
10001005

1001-
and in XML:
1006+
<1> `"/portfolio"` is the HTTP URL for the endpoint to which a WebSocket (or SockJS)
1007+
client will need to connect to for the WebSocket handshake.
1008+
<2> STOMP messages whose destination header begins with `"/app"` are routed to
1009+
`@MessageMapping` methods in `@Controller` classes.
1010+
<3> Use the built-in, message broker for subscriptions and broadcasting;
1011+
Route messages whose destination header begins with "/topic" or "/queue" to the broker.
1012+
1013+
The same configuration in XML:
10021014

10031015
[source,xml,indent=0]
10041016
[subs="verbatim,quotes,attributes"]
@@ -1024,18 +1036,11 @@ and in XML:
10241036

10251037
[NOTE]
10261038
====
1027-
The "/app" prefix is arbitrary. You can pick any prefix. It's simply meant to differentiate
1028-
messages to be routed to message-handling methods to do application work vs messages
1029-
to be routed to the broker to broadcast to subscribed clients.
1030-
1031-
The "/topic" and "/queue" prefixes depend on the broker in use. In the case of the simple,
1032-
in-memory broker the prefixes do not have any special meaning; it's merely a convention
1033-
that indicates how the destination is used (pub-sub targetting many subscribers or
1034-
point-to-point messages typically targeting an individual recipient).
1035-
In the case of using a dedicated broker, most brokers use "/topic" as
1036-
a prefix for destinations with pub-sub semantics and "/queue" for destinations
1037-
with point-to-point semantics. Check the STOMP page of the broker to see the destination
1038-
semantics it supports.
1039+
For the built-in, simple broker the "/topic" and "/queue" prefixes do not have any special
1040+
meaning. They're merely a convention to differentiate between pub-sub vs point-to-point
1041+
messaging (i.e. many subscribers vs one consumer). When using an external broker, please
1042+
check the STOMP page of the broker to understand what kind of STOMP destinations and
1043+
prefixes it supports.
10391044
====
10401045

10411046

@@ -1075,60 +1080,64 @@ sections <<websocket-stomp-handle-broker-relay-configure>> and
10751080
[[websocket-stomp-message-flow]]
10761081
=== Flow of Messages
10771082

1078-
When a STOMP endpoint is configured, the Spring application acts as the STOMP broker
1079-
to connected clients. This section provides a big picture overview of how messages flow
1080-
within the application.
1083+
Once a STOMP endpoint is exposed, the Spring application becomes a STOMP broker for
1084+
connected clients. This section describes the flow of messages on the server side.
10811085

1082-
The `spring-messaging` module provides the foundation for asynchronous message processing.
1083-
It contains a number of abstractions that originated in the
1084-
https://spring.io/spring-integration[Spring Integration] project and are intended
1085-
for use as building blocks in messaging applications:
1086+
The `spring-messaging` module contains foundational support for messaging applications
1087+
that originated in https://spring.io/spring-integration[Spring Integration] and was
1088+
later extracted and incorporated into the Spring Framework for broader use across many
1089+
https://spring.io/projects[Spring projects] and application scenarios.
1090+
Below is a list of a few of the available messaging abstractions:
10861091

10871092
* {api-spring-framework}/messaging/Message.html[Message] --
1088-
a message with headers and a payload.
1093+
simple representation for a message including headers and payload.
10891094
* {api-spring-framework}/messaging/MessageHandler.html[MessageHandler] --
1090-
a contract for handling a message.
1095+
contract for handling a message.
10911096
* {api-spring-framework}/messaging/MessageChannel.html[MessageChannel] --
1092-
a contract for sending a message enabling loose coupling between senders and receivers.
1097+
contract for sending a message that enables loose coupling between producers and consumers.
10931098
* {api-spring-framework}/messaging/SubscribableChannel.html[SubscribableChannel] --
1094-
extends `MessageChannel` and sends messages to registered `MessageHandler` subscribers.
1099+
`MessageChannel` with `MessageHandler` subscribers.
10951100
* {api-spring-framework}/messaging/support/ExecutorSubscribableChannel.html[ExecutorSubscribableChannel] --
1096-
a concrete implementation of `SubscribableChannel` that can deliver messages
1097-
asynchronously via a thread pool.
1101+
`SubscribableChannel` that uses an `Executor` for delivering messages.
10981102

1099-
The `@EnableWebSocketMessageBroker` Java config and the `<websocket:message-broker>` XML config
1100-
both assemble a concrete message flow. Below is a diagram of the part of the setup when using
1101-
the simple, in-memory broker:
1103+
Both the Java config (i.e. `@EnableWebSocketMessageBroker`) and the XML namespace config
1104+
(i.e. `<websocket:message-broker>`) use the above components to assemble a message
1105+
workflow. The diagram below shows the components used when the simple, built-in message
1106+
broker is enabled:
11021107

11031108
image::images/message-flow-simple-broker.png[]
11041109

1105-
The above setup that includes 3 message channels:
1110+
There are 3 message channels in the above diagram:
11061111

1107-
* `"clientInboundChannel"` for messages from WebSocket clients.
1108-
* `"clientOutboundChannel"` for messages to WebSocket clients.
1109-
* `"brokerChannel"` for messages to the broker from within the application.
1112+
* `"clientInboundChannel"` -- for passing messages received from WebSocket clients.
1113+
* `"clientOutboundChannel"` -- for sending server messages to WebSocket clients.
1114+
* `"brokerChannel"` -- for sending messages to the message broker from within
1115+
server-side, application code.
11101116

1111-
The same three channels are also used with a dedicated broker except here a
1112-
"broker relay" takes the place of the simple broker:
1117+
The next diagram shows the components used when an external broker (e.g. RabbitMQ)
1118+
is configured for managing subscriptions and broadcasting messages:
11131119

11141120
image::images/message-flow-broker-relay.png[]
11151121

1116-
Messages on the `"clientInboundChannel"` can flow to annotated
1117-
methods for application handling (e.g. a stock trade execution request) or can
1118-
be forwarded to the broker (e.g. client subscribing for stock quotes).
1119-
The STOMP destination is used for simple prefix-based routing. For example
1120-
the "/app" prefix could route messages to annotated methods while the "/topic"
1121-
and "/queue" prefixes could route messages to the broker.
1122+
The main difference in the above diagram is the use of the "broker relay" for passing
1123+
messages up to the external STOMP broker over TCP, and for passing messages down from the
1124+
broker to subscribed clients.
1125+
1126+
When messages are received from a WebSocket connectin, they're decoded to STOMP frames,
1127+
then turned into a Spring `Message` representation, and sent to the
1128+
`"clientInboundChannel"` for further processing. For example STOMP messages whose
1129+
destination header starts with `"/app"` may be routed to `@MessageMapping` methods in
1130+
annotated controllers, while `"/topic"` and `"/queue"` messages may be routed directly
1131+
to the message broker.
11221132

1123-
When a message-handling annotated method has a return type, its return
1124-
value is sent as the payload of a Spring `Message` to the `"brokerChannel"`.
1125-
The broker in turn broadcasts the message to clients. Sending a message
1126-
to a destination can also be done from anywhere in the application with
1127-
the help of a messaging template. For example, an HTTP POST handling method
1128-
can broadcast a message to connected clients, or a service component may
1129-
periodically broadcast stock quotes.
1133+
An annotated `@Controller` handling a STOMP message from a client may send a message to
1134+
the message broker through the `"brokerChannel"`, and the broker will broadcast the
1135+
message to matching subscribers through the `"clientOutboundChannel"`. The same
1136+
controller can also do the same in response to HTTP requests, so a client may perform an
1137+
HTTP POST and then an `@PostMapping` method can send a message to the message broker
1138+
to broadcast to subscribed clients.
11301139

1131-
Below is a simple example to illustrate the flow of messages:
1140+
Let's trace the flow through a simple example. Given the following server setup:
11321141

11331142
[source,java,indent=0]
11341143
[subs="verbatim,quotes"]
@@ -1162,18 +1171,22 @@ Below is a simple example to illustrate the flow of messages:
11621171
11631172
----
11641173

1165-
The following explains the message flow for the above example:
1166-
1167-
* WebSocket clients connect to the WebSocket endpoint at "/portfolio".
1168-
* Subscriptions to "/topic/greeting" pass through the "clientInboundChannel"
1169-
and are forwarded to the broker.
1170-
* Greetings sent to "/app/greeting" pass through the "clientInboundChannel"
1171-
and are forwarded to the `GreetingController`. The controller adds the current
1172-
time, and the return value is passed through the "brokerChannel" as a message
1173-
to "/topic/greeting" (destination is selected based on a convention but can be
1174-
overridden via `@SendTo`).
1175-
* The broker in turn broadcasts messages to subscribers, and they pass through
1176-
the `"clientOutboundChannel"`.
1174+
. Client connects to `"http://localhost:8080/portfolio"` and once a WebSocket connection
1175+
is established, STOMP frames begin to flow on it.
1176+
. Client sends SUBSCRIBE frame with destination header `"/topic/greeting"`. Once received
1177+
and decoded, the message is sent to the `"clientInboundChannel"`, then routed to the
1178+
message broker which stores the client subscription.
1179+
. Client sends SEND frame to `"/app/greeting"`. The `"/app"` prefix helps to route it to
1180+
annotated controllers. After the `"/app"` prefix is stripped, the remaining `"/greeting"`
1181+
part of the destination is mapped to the `@MessageMapping` method in `GreetingController`.
1182+
. The value returned from `GreetingController` is turned into a Spring `Message` with
1183+
a payload based on the return value and a default destination header of
1184+
`"/topic/greeting"` (derived from the input destination with `"/app"` replaced by
1185+
`"/topic"`). The resulting message is sent to the "brokerChannel" and handled
1186+
by the message broker.
1187+
. The message broker finds all matching subscribers, and sends a MESSAGE frame to each
1188+
through the `"clientOutboundChannel"` from where messages are encoded as STOMP frames
1189+
and sent on the WebSocket connection.
11771190

11781191
The next section provides more details on annotated methods including the
11791192
kinds of arguments and return values supported.

0 commit comments

Comments
 (0)