Skip to content

Commit 6679120

Browse files
committed
Improve STOMP section of documentation
Issue: SPR-12579
1 parent 08fb625 commit 6679120

File tree

3 files changed

+100
-64
lines changed

3 files changed

+100
-64
lines changed
78.2 KB
Loading
63.8 KB
Loading

src/asciidoc/web-websocket.adoc

Lines changed: 100 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -984,24 +984,27 @@ public class WebSocketConfig extends WebSocketMessageBrokerConfigurationSupport
984984

985985
[[websocket-stomp]]
986986
== STOMP Over WebSocket Messaging Architecture
987-
The WebSocket protocol defines two main types of messages -- text and binary --
988-
but leaves their content undefined. Instead it's expected that the client and
989-
server may agree on using a sub-protocol, i.e. a higher-level protocol that defines
990-
the message content. Using a sub-protocol is optional but either way the client
991-
and server both need to understand how to interpret messages.
987+
The WebSocket protocol defines two types of messages, text and binary, but their
988+
content is undefined. It's expected that the client and server may agree on using
989+
a sub-protocol (i.e. a higher-level protocol) to define message semantics.
990+
While the use of a sub-protocol with WebSocket is completely optional either way
991+
client and server will need to agree on some kind of protocol to help interpret
992+
messages.
992993

993994

994995

995996
[[websocket-stomp-overview]]
996997
=== Overview of STOMP
997998
http://stomp.github.io/stomp-specification-1.2.html#Abstract[STOMP] is a simple
998999
text-oriented messaging protocol that was originally created for scripting languages
999-
(such as Ruby, Python, and Perl) to connect to enterprise message brokers. It is
1000-
designed to address a subset of commonly used patterns in messaging protocols. STOMP
1001-
can be used over any reliable 2-way streaming network protocol such as TCP and WebSocket.
1000+
such as Ruby, Python, and Perl to connect to enterprise message brokers. It is
1001+
designed to address a subset of commonly used messaging patterns. STOMP can be
1002+
used over any reliable 2-way streaming network protocol such as TCP and WebSocket.
1003+
Although STOMP is a text-oriented protocol, the payload of messages can be
1004+
either text or binary.
10021005

1003-
STOMP is a frame based protocol with frames modeled on HTTP. This is the
1004-
structure of a frame:
1006+
STOMP is a frame based protocol whose frames are modeled on HTTP. The structure
1007+
of a STOMP frame:
10051008

10061009
----
10071010
COMMAND
@@ -1011,23 +1014,28 @@ header2:value2
10111014
Body^@
10121015
----
10131016

1014-
For example, a client can use the +SEND+ command to send a message or the
1015-
+SUBSCRIBE+ command to express interest in receiving messages. Both of these commands
1016-
require a +"destination"+ header that indicates where to send a message, or likewise
1017-
what to subscribe to.
1017+
Clients can use the +SEND+ or +SUBSCRIBE+ commands to send or subscribe for
1018+
messages along with a +"destination"+ header that describes what the
1019+
message is about and who should receive it. This enables a simple
1020+
publish-subscribe mechanism that can be used to send messages through the broker
1021+
to other connected clients or to send messages to the server to request that
1022+
some work be performed.
10181023

1019-
Here is an example of a client sending a request to buy stock shares:
1024+
When using Spring's STOMP support, the Spring WebSocket application acts
1025+
as the STOMP broker to clients. Messages are routed to `@Controller` message-handling
1026+
methods or to a simple, in-memory broker that keeps track of subscriptions and
1027+
broadcasts messages to subscribed users. You can also configure Spring to work
1028+
with a dedicated STOMP broker (e.g. RabbitMQ, ActiveMQ, etc) for the actual
1029+
broadcasting of messages. In that case Spring maintains
1030+
TCP connections to the broker, relays messages to it, and also passes messages
1031+
from it down to connected WebSocket clients. Thus Spring web applications can
1032+
rely on unified HTTP-based security, common validation, and a familiar programming
1033+
model message-handling work.
10201034

1021-
----
1022-
SEND
1023-
destination:/queue/trade
1024-
content-type:application/json
1025-
content-length:44
1026-
1027-
{"action":"BUY","ticker":"MMM","shares",44}^@
1028-
----
1035+
Here is an example of a client subscribing to receive stock quotes which
1036+
the server may emit periodically e.g. via a scheduled task sending messages
1037+
through a `SimpMessagingTemplate` to the broker:
10291038

1030-
Here is an example of a client subscribing to receive stock quotes:
10311039
----
10321040
SUBSCRIBE
10331041
id:sub-1
@@ -1036,15 +1044,25 @@ destination:/topic/price.stock.*
10361044
^@
10371045
----
10381046

1039-
[NOTE]
1040-
====
1047+
Here is an example of a client sending a trade request, which the server
1048+
may handle through an `@MessageMapping` method and later on, after the execution,
1049+
broadcast a trade confirmation message and details down to the client:
1050+
1051+
----
1052+
SEND
1053+
destination:/queue/trade
1054+
content-type:application/json
1055+
content-length:44
1056+
1057+
{"action":"BUY","ticker":"MMM","shares",44}^@
1058+
----
1059+
10411060
The meaning of a destination is intentionally left opaque in the STOMP spec. It can
10421061
be any string, and it's entirely up to STOMP servers to define the semantics and
10431062
the syntax of the destinations that they support. It is very common, however, for
10441063
destinations to be path-like strings where `"/topic/.."` implies publish-subscribe
10451064
(__one-to-many__) and `"/queue/"` implies point-to-point (__one-to-one__) message
10461065
exchanges.
1047-
====
10481066

10491067
STOMP servers can use the +MESSAGE+ command to broadcast messages to all subscribers.
10501068
Here is an example of a server sending a stock quote to a subscribed client:
@@ -1058,26 +1076,21 @@ destination:/topic/price.stock.MMM
10581076
{"ticker":"MMM","price":129.45}^@
10591077
----
10601078

1061-
[NOTE]
1062-
====
10631079
It is important to know that a server cannot send unsolicited messages. All messages
10641080
from a server must be in response to a specific client subscription, and the
10651081
+"subscription-id"+ header of the server message must match the +"id"+ header of the
10661082
client subscription.
1067-
====
10681083

10691084
The above overview is intended to provide the most basic understanding of the
10701085
STOMP protocol. It is recommended to review the protocol
1071-
http://stomp.github.io/stomp-specification-1.2.html[specification], which is
1072-
easy to follow and manageable in terms of size.
1086+
http://stomp.github.io/stomp-specification-1.2.html[specification] in full.
10731087

1074-
The following summarizes the benefits for an application of using STOMP over WebSocket:
1088+
The benefits of using STOMP as a WebSocket sub-protocol:
10751089

1076-
* Standard message format
1077-
* Application-level protocol with support for common messaging patterns
1078-
* Client-side support, e.g. https://github.com/jmesnil/stomp-websocket[stomp.js], https://github.com/cujojs/msgs[msgs.js]
1079-
* The ability to interpret, route, and process messages on both the client and server-side
1080-
* The option to plug in a message broker -- RabbitMQ, ActiveMQ, many others -- to broadcast messages (explained later)
1090+
* No need to invent a custom message format
1091+
* Use existing https://github.com/jmesnil/stomp-websocket[stomp.js] client in the browser
1092+
* Ability to route messages to based on destination
1093+
* Option to use full-fledged message broker such as RabbitMQ, ActiveMQ, etc. for broadcasting
10811094

10821095
Most importantly the use of STOMP (vs plain WebSocket) enables the Spring Framework
10831096
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.
10881101
[[websocket-stomp-enable]]
10891102
=== Enable STOMP over WebSocket
10901103
The Spring Framework provides support for using STOMP over WebSocket through
1091-
the +spring-messaging+ and +spring-websocket+ modules. It's easy to enable it.
1092-
1093-
Here is an example of configuring a STOMP WebSocket endpoint with SockJS fallback
1094-
options. The endpoint is available for clients to connect to a URL path `/portfolio`:
1104+
the +spring-messaging+ and +spring-websocket+ modules.
1105+
Here is an example of exposing a STOMP WebSocket/SockJS endpoint at the URL path
1106+
`/portfolio` where messages whose destination starts with "/app" are routed to
1107+
message-handling methods (i.e. application work) and messages whose destinations
1108+
start with "/topic" or "/queue" will be routed to the message broker (i.e.
1109+
broadcasting to other connected clients):
10951110

10961111
[source,java,indent=0]
10971112
[subs="verbatim,quotes"]
@@ -1103,23 +1118,21 @@ options. The endpoint is available for clients to connect to a URL path `/portfo
11031118
@EnableWebSocketMessageBroker
11041119
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
11051120
1106-
@Override
1107-
public void configureMessageBroker(MessageBrokerRegistry config) {
1108-
config.setApplicationDestinationPrefixes("/app");
1109-
config.enableSimpleBroker("/queue", "/topic");
1110-
}
1111-
11121121
@Override
11131122
public void registerStompEndpoints(StompEndpointRegistry registry) {
11141123
registry.addEndpoint("/portfolio").withSockJS();
11151124
}
11161125
1117-
// ...
1126+
@Override
1127+
public void configureMessageBroker(MessageBrokerRegistry config) {
1128+
config.setApplicationDestinationPrefixes("/app");
1129+
config.enableSimpleBroker("/topic", "/queue");
1130+
}
11181131
11191132
}
11201133
----
11211134

1122-
XML configuration equivalent:
1135+
and in XML:
11231136

11241137
[source,xml,indent=0]
11251138
[subs="verbatim,quotes,attributes"]
@@ -1137,13 +1150,29 @@ XML configuration equivalent:
11371150
<websocket:stomp-endpoint path="/portfolio">
11381151
<websocket:sockjs/>
11391152
</websocket:stomp-endpoint>
1140-
<websocket:simple-broker prefix="/queue, /topic"/>
1141-
...
1153+
<websocket:simple-broker prefix="/topic, /queue"/>
11421154
</websocket:message-broker>
11431155
11441156
</beans>
11451157
----
11461158

1159+
[NOTE]
1160+
====
1161+
The "/app" prefix is arbitrary. You can pick any prefix. It's simply meant to differentiate
1162+
messages to be routed to message-handling methods to do application work vs messages
1163+
to be routed to the broker to broadcast to subscribed clients.
1164+
1165+
The "/topic" and "/queue" prefixes depend on the broker in use. In the case of the simple,
1166+
in-memory broker the prefixes do not have any special meaning; it's merely a convention
1167+
that indicates how the destination is used (pub-sub targetting many subscribers or
1168+
point-to-point messages typically targeting an individual recipient).
1169+
In the case of using a dedicated broker, most brokers use "/topic" as
1170+
a prefix for destinations with pub-sub semantics and "/queue" for destinations
1171+
with point-to-point semantics. Check the STOMP page of the broker to see the destination
1172+
semantics it supports.
1173+
====
1174+
1175+
11471176
On the browser side, a client might connect as follows using
11481177
https://github.com/jmesnil/stomp-websocket[stomp.js] and the
11491178
https://github.com/sockjs/sockjs-client[sockjs-client]:
@@ -1180,10 +1209,11 @@ sections <<websocket-stomp-handle-broker-relay-configure>> and
11801209
=== Flow of Messages
11811210

11821211
When a STOMP endpoint is configured, the Spring application acts as the STOMP broker
1183-
to connected clients. It handles incoming messages and sends messages back.
1184-
This section provides a big picture overview of how messages flow inside the application.
1212+
to connected clients. This section provides a big picture overview of how messages flow
1213+
within the application.
11851214

1186-
The `spring-messaging` module contains a number of abstractions that originated in the
1215+
The `spring-messaging` module provides the foundation for asynchronous message processing.
1216+
It contains a number of abstractions that originated in the
11871217
https://spring.io/spring-integration[Spring Integration] project and are intended
11881218
for use as building blocks in messaging applications:
11891219

@@ -1199,16 +1229,22 @@ extends `MessageChannel` and sends messages to registered `MessageHandler` subsc
11991229
a concrete implementation of `SubscribableChannel` that can deliver messages
12001230
asynchronously via a thread pool.
12011231

1202-
The provided STOMP over WebSocket config, both Java and XML, uses the above to
1203-
assemble a concrete message flow including the following 3 channels:
1232+
The `@EnableWebSocketMessageBroker` Java config and the `<websocket:message-broker>` XML config
1233+
both assemble a concrete message flow. Below is a diagram of the part of the setup when using
1234+
the simple, in-memory broker:
1235+
1236+
image::images/message-flow-simple-broker.png[width=640]
1237+
1238+
The above setup that includes 3 message channels:
1239+
1240+
* `"clientInboundChannel"` for messages from WebSocket clients.
1241+
* `"clientOutboundChannel"` for messages to WebSocket clients.
1242+
* `"brokerChannel"` for messages to the broker from within the application.
1243+
1244+
The same three channels are also used with a dedicated broker except here a
1245+
"broker relay" takes the place of the simple broker:
12041246

1205-
* `"clientInboundChannel"` -- for messages from WebSocket clients. Every incoming
1206-
WebSocket message carrying a STOMP frame is sent through this channel.
1207-
* `"clientOutboundChannel"` -- for messages to WebSocket clients. Every outgoing
1208-
STOMP message from the broker is sent through this channel before getting sent
1209-
to a client's WebSocket session.
1210-
* `"brokerChannel"` -- for messages to the broker from within the application.
1211-
Every message sent from the application to the broker passes through this channel.
1247+
image::images/message-flow-broker-relay.png[width=640]
12121248

12131249
Messages on the `"clientInboundChannel"` can flow to annotated
12141250
methods for application handling (e.g. a stock trade execution request) or can

0 commit comments

Comments
 (0)