一旦STOMP的接口被公布,Spring应用程序就成为连接客户端的STOMP代理。本节描述服务端消息处理的流程。
spring-messaging
模块包含消息类应用的基础功能,这些功能起源于Spring Integration项目。并且,后来被提取整合到Spring框架,以便在更多的Spring项目和应用场景中更广泛地使用。下面列出了模块里包含的消息抽象:
- Message: 消息的简单表示,包括头部和有效载荷。
- MessageHandler: 消息处理器。
- MessageChannel: 发送消息的渠道,实现生产者和消费者的解耦。
- SubscribableChannel: 带有
MessageHandler
订阅者的MessageChannel
。 - ExecutorSubscribableChannel: 使用
Executor
线程池来发送消息的SubscribableChannel
。
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
开头的消息可能会直接路由到消息代理。
带了@Controller
注解的程序,在处理客户端发的STOMP消息后,可以通过brokerChannel
向消息代理发送消息,代理通过clientOutboundChannel
将消息广播给匹配的订阅者。同一个controller也可以对HTTP请求做出相同的响应,因此客户端可以执行一个HTTP POST请求,处理请求的@PostMapping
方法可以向消息代理发送消息,然后广播给订阅的客户端。
我们通过一个简单的例子来熟悉流程。下面是配置服务端的示例:
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/portfolio");
}
@Override
public void configureMessageBroker(MessageBrokerRegistry registry) {
registry.setApplicationDestinationPrefixes("/app");
registry.enableSimpleBroker("/topic");
}
}
@Controller
public class GreetingController {
@MessageMapping("/greeting")
public String handle(String greeting) {
return "[" + getTimestamp() + ": " + greeting;
}
}
前面的示例支持以下流程:
- 客户端连接到
http://localhost:8080/portfolio
,一旦建立WebSocket连接,STOMP帧就开始在它上面传输。 - 客户端发送一个destination消息头值为
/topic/greeting
的SUBSCRIBE帧。一旦接收并解码,消息被发送到clientInboundChannel
,然后被路由到消息代理,代理存储了客户端订阅信息。 - 客户端向
/app/greeting
发送一个SEND帧。/app
前缀的消息会路由到带注解的控制器上。在删掉了/app
前缀之后,destination值剩余的部分/greeting
会映射到GreetingController
中的@MessageMapping
方法上。 - 从
GreetingController
返回的值被转换成一个SpringMessage
对象,方法的返回值放入消息的payload,消息的默认destination消息头是/topic/greeting
(从输入的destination派生而来,规则是用/topic
替换/app
)。返回的消息被发送到brokerChannel
并由消息代理处理。 - 消息代理找到所有匹配的订阅者,并通过
clientOutboundChannel
向所有订阅者发送一个MESSAGE帧,发送前消息被编码为STOMP帧,并通过WebSocket连接发送。