parent
08fb62570e
commit
6679120057
Binary file not shown.
After Width: | Height: | Size: 78 KiB |
Binary file not shown.
After Width: | Height: | Size: 64 KiB |
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue