Flow of Messages
公开 STOMP 端点后,Spring 应用程序会为连接的客户端成为 STOMP 代理。本部分介绍服务器端的邮件流。
`spring-messaging`模块包含对源自 Spring Integration的消息传递应用程序的基本支持,后来被提取出来并合并到 Spring 框架中,以便在许多https://spring.io/projects[Spring 项目]和应用程序场景中更广泛地使用。以下列表简要描述了几个可用的消息传递抽象:
-
Message:消息的简单表示,包括标题和有效负载。
-
MessageHandler:处理消息的契约。
-
MessageChannel:发送消息的契约,能够在生产者和消费者之间实现松散耦合。
-
SubscribableChannel:
MessageChannel
withMessageHandler
subscribers. -
ExecutorSubscribableChannel:
SubscribableChannel
,它使用 `Executor`传递消息。
Java 配置(即 @EnableWebSocketMessageBroker
)和 XML 命名空间配置(即 <websocket:message-broker>
)都使用前述组件来组合消息工作流。以下示意图显示了启用简单内置消息代理时所用的组件:
上述示意图显示了三个消息通道:
-
clientInboundChannel
:用于传递从 WebSocket 客户端接收到的消息。 -
clientOutboundChannel
:用于向 WebSocket 客户端发送服务器消息。 -
brokerChannel
:用于将消息从服务器端应用程序代码发送到消息代理。
下一张示意图显示了针对管理订阅和广播消息而配置的外部代理(例如 RabbitMQ)时所用的组件:
两个前述示意图之间的主要区别在于,使用“broker relay
”将消息通过 TCP 传递至外部 STOMP 代理,和将消息从代理传递至已订阅的客户端。
从 WebSocket 连接接收消息后,这些消息会被解码成 STOMP 帧,转换成 Spring Message
表示形式,并发送至 clientInboundChannel
以作进一步处理。例如,目标头以 /app
开头的 STOMP 消息可能会路由至带注释控制器的 @MessageMapping
方法,而 /topic
和 /queue
消息可能会直接路由至消息代理。
处理来自客户端的 STOMP 消息的带注释 @Controller
可能会通过 brokerChannel
向消息代理发送消息,而代理会通过 clientOutboundChannel
将消息广播至相匹配的订阅方。同样的控制器也可在响应 HTTP 请求时执行相同的操作,因此,客户端可以执行 HTTP POST,然后 @PostMapping
方法可向消息代理发送消息,以便广播至已订阅的客户端。
我们可以通过一个简单的例子来跟踪流程。考虑一下以下设置一个服务器的例子:
-
Java
-
Kotlin
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfiguration implements WebSocketMessageBrokerConfigurer {
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/portfolio");
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.setApplicationDestinationPrefixes("/app");
registry.enableSimpleBroker("/topic");
}
}
@Configuration
@EnableWebSocketMessageBroker
class WebSocketConfiguration : WebSocketMessageBrokerConfigurer {
override fun registerStompEndpoints(registry: StompEndpointRegistry) {
registry.addEndpoint("/portfolio")
}
override fun configureMessageBroker(registry: MessageBrokerRegistry) {
registry.setApplicationDestinationPrefixes("/app")
registry.enableSimpleBroker("/topic")
}
}
-
Java
-
Kotlin
@Controller
public class GreetingController {
@MessageMapping("/greeting")
public String handle(String greeting) {
return "[" + getTimestamp() + ": " + greeting;
}
private String getTimestamp() {
return new SimpleDateFormat("MM/dd/yyyy h:mm:ss a").format(new Date());
}
}
@Controller
class GreetingController {
@MessageMapping("/greeting")
fun handle(greeting: String): String {
return "[${getTimestamp()}: $greeting"
}
private fun getTimestamp(): String {
return SimpleDateFormat("MM/dd/yyyy h:mm:ss a").format(Date())
}
}
前面的例子支持以下流程:
-
客户端连接到
http://localhost:8080/portfolio
,一旦建立 WebSocket 连接,STOMP 帧就开始在上面流动。 -
客户端发送一个 SUBSCRIBE 帧,其目标标题为
/topic/greeting
。一旦接收并解码,消息就会发送到clientInboundChannel
,然后路由到消息代理,它存储客户端订阅。 -
客户端向
/app/greeting`发送一个 SEND 帧。
/app`前缀有助于将其路由到标注的控制器。在剥离掉 `/app`前缀后,目标的剩余 `/greeting`部分被映射到 `GreetingController`中的 `@MessageMapping`方法。 -
从
GreetingController`返回的值将转换为 Spring `Message
,其有效负载基于返回值和/topic/greeting`的默认目标标题(通过用 `/topic`替换 `/app`得自输入目标)。生成的消息会被发送到 `brokerChannel
,并由消息代理处理。 -
消息代理找到所有匹配的订阅者,并通过 `clientOutboundChannel`向每个订阅者发送 MESSAGE 帧,消息通过它被编码为 STOMP 帧,并在 WebSocket 连接上发送。
下一部分提供关于注释方法的更多详细信息,包括支持的参数类型和返回值。