STOMP
WebSocket 协议定义了两种类型的消息(文本和二进制),但它们的内容是未定义的。该协议定义了一个机制,使客户端和服务器能够协商一个子协议(即,更高级别的消息传递协议),以便在 WebSocket 之上使用,定义每个可以发送的消息类型、格式、每条消息的内容等。使用子协议是可选的,但无论如何,客户端和服务器需要就某种协议达成一致,以定义消息内容。
章节摘要
概述
STOMP (简单文本导向消息协议) 最初是为脚本语言(如 Ruby、Python 和 Perl)创建的,以连接企业消息中间件。它旨在解决一小部分常用的消息模式。STOMP 可以在任何可靠的双向流网络协议上使用,例如 TCP 和 WebSocket。虽然 STOMP 是一种面向文本的协议,但消息负载可以是文本或二进制。
好处
使用 STOMP 作为子协议使得 Spring 框架和 Spring Security 提供了比使用原始 WebSockets 更丰富的编程模型。关于 HTTP 与原始 TCP 之间的比较也可以得出相同的结论,这使得 Spring MVC 和其他 Web 框架能够提供丰富的功能。以下是一些好处的列表:
启用 STOMP
STOMP 通过 WebSocket 的支持在 spring-messaging 和 spring-websocket 模块中可用。一旦你拥有了这些依赖项,你可以通过 WebSocket 暴露一个 STOMP 端点,如下例所示:
WebSocket 传输
本节解释如何配置底层的 WebSocket 服务器传输。
消息流
一旦暴露了 STOMP 端点,Spring 应用程序就成为连接客户端的 STOMP 代理。本节描述了服务器端的消息流。
注释控制器
应用程序可以使用带注解的 @Controller 类来处理来自客户端的消息。这些类可以声明 @MessageMapping、@SubscribeMapping 和 @ExceptionHandler 方法,如以下主题所述:
发送消息
如果您想从应用程序的任何部分向连接的客户端发送消息,该怎么办?任何应用程序组件都可以向 brokerChannel 发送消息。最简单的方法是注入一个 SimpMessagingTemplate 并使用它发送消息。通常,您会按类型注入,如以下示例所示:
简单经纪人
内置的简单消息代理处理来自客户端的订阅请求,将其存储在内存中,并向具有匹配目的地的连接客户端广播消息。该代理支持类似路径的目的地,包括对 Ant 风格目的地模式的订阅。
外部经纪人
简单的代理非常适合入门,但仅支持 STOMP 命令的一个子集(不支持 acks、receipts 和其他一些功能),依赖于简单的消息发送循环,并且不适合集群。作为替代方案,您可以升级您的应用程序以使用功能齐全的消息代理。
连接到代理服务器
一个 STOMP 代理中继维护一个单一的“系统” TCP 连接到代理。该连接仅用于来自服务器端应用程序的消息,不用于接收消息。您可以为此连接配置 STOMP 凭据(即 STOMP 帧登录和密码头)。这在 XML 命名空间和 Java 配置中都以 systemLogin 和 systemPasscode 属性的形式暴露,默认值为 guest 和 guest。
点作为分隔符
当消息被路由到 @MessageMapping 方法时,它们会与 AntPathMatcher 进行匹配。默认情况下,模式预期使用斜杠 (/) 作为分隔符。这在 Web 应用程序中是一个良好的约定,并且类似于 HTTP URLs。然而,如果您更习惯于消息传递约定,您可以切换为使用点 (.) 作为分隔符。
认证
每个 STOMP over WebSocket 消息会话都以一个 HTTP 请求开始。这可以是一个升级到 WebSocket 的请求(即 WebSocket 握手),或者在 SockJS 回退的情况下,一系列 SockJS HTTP 传输请求。
令牌认证
Spring Security OAuth 提供了基于令牌的安全性支持,包括 JSON Web Token (JWT)。您可以将其用作 Web 应用程序中的身份验证机制,包括前一节中描述的 STOMP 通过 WebSocket 的交互(即,通过基于 cookie 的会话来维护身份)。
授权
Spring Security 提供了 WebSocket 子协议授权,它使用 ChannelInterceptor 根据消息中的用户头进行授权。此外,Spring Session 提供了 WebSocket 集成,确保用户的 HTTP 会话在 WebSocket 会话仍然处于活动状态时不会过期。
用户目的地
一个应用程序可以发送针对特定用户的消息,而 Spring 的 STOMP 支持识别以 /user/ 为前缀的目的地。比如,一个客户端可能会订阅 /user/queue/position-updates 目的地。UserDestinationMessageHandler 处理这个目的地,并将其转换为一个唯一的用户会话目的地(例如 /queue/position-updates-user123)。这提供了订阅一个通用命名目的地的便利,同时确保与其他订阅相同目的地的用户之间没有冲突,从而使每个用户都能接收到独特的股票位置更新。
消息顺序
来自代理的消息被发布到 clientOutboundChannel,从那里它们被写入 WebSocket 会话。由于该通道由 ThreadPoolExecutor 支持,消息在不同的线程中处理,因此客户端接收到的结果序列可能与发布的确切顺序不匹配。
事件
多个 ApplicationContext 事件被发布,并可以通过实现 Spring 的 ApplicationListener 接口来接收:
拦截
事件提供了 STOMP 连接生命周期的通知,但并不针对每个客户端消息。应用程序还可以注册一个 ChannelInterceptor 来拦截任何消息以及处理链中的任何部分。以下示例演示了如何拦截来自客户端的入站消息:
STOMP 客户端
Spring 提供了一个基于 WebSocket 的 STOMP 客户端和一个基于 TCP 的 STOMP 客户端。
WebSocket 范围
每个 WebSocket 会话都有一个属性映射。该映射作为头部附加到传入的客户端消息中,并可以从控制器方法中访问,如下例所示:
性能
在性能方面,没有灵丹妙药。许多因素会影响性能,包括消息的大小和数量、应用程序方法是否执行需要阻塞的工作,以及外部因素(例如网络速度和其他问题)。本节的目标是提供可用配置选项的概述,以及一些关于如何考虑扩展的想法。
监控
当您使用 @EnableWebSocketMessageBroker 或 websocket:message-broker 时,关键基础设施组件会自动收集统计信息和计数器,这些信息提供了有关应用程序内部状态的重要洞察。该配置还声明了一个类型为 WebSocketMessageBrokerStats 的 bean,该 bean 将所有可用信息集中在一个地方,并默认每 30 分钟以 INFO 级别记录一次。此 bean 可以通过 Spring 的 MBeanExporter 导出到 JMX,以便在运行时查看(例如,通过 JDK 的 jconsole)。以下列表总结了可用的信息:
测试
在使用 Spring 的 STOMP-over-WebSocket 支持时,测试应用程序主要有两种方法。第一种是编写服务器端测试,以验证控制器及其注释的消息处理方法的功能。第二种是编写完整的端到端测试,涉及运行客户端和服务器。