spring-messaging 和 spring-websocket 模块中提供了 STOMP over WebSocket 支持。一旦你有了这些依赖,你就可以通过 SockJS Fallback 在WebSocket上暴露 STOMP 端点,如下例所示:
先添加依赖:
// spring websocket 支持
implementation 'org.springframework:spring-websocket:5.3.15'
// spring-messaging 可以提供 STOMP 支持
implementation 'org.springframework:spring-messaging:5.3.15'
暴露 STOMP 端点
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
@Configuration
@EnableWebSocketMessageBroker
public class WebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
// /portfolio 是 WebSocket(或 SockJS)的端点的 HTTP URL。客户端需要连接以进行 WebSocket 握手。
registry.addEndpoint("/portfolio").withSockJS();
}
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
// 目标标头以 /app 开头的 STOMP 消息会被路由到 @Controller 类中的 @MessageMapping 方法。
config.setApplicationDestinationPrefixes("/app");
// 使用内置的消息代理进行订阅和广播,并且 将目标标头以 /topic 或 /queue 开头的消息路由到代理。
config.enableSimpleBroker("/topic", "/queue");
}
}
下面是等效的 XML 配置:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:websocket="http://www.springframework.org/schema/websocket"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/websocket
https://www.springframework.org/schema/websocket/spring-websocket.xsd">
<websocket:message-broker application-destination-prefix="/app">
<websocket:stomp-endpoint path="/portfolio">
<websocket:sockjs/>
</websocket:stomp-endpoint>
<websocket:simple-broker prefix="/topic, /queue"/>
</websocket:message-broker>
</beans>
:::info 对于内置的简单代理,/topic 和 /queue 前缀没有任何特殊含义。它们只是一个惯例,用来区分 pub-sub 和 point-to-point 的消息传递(即许多订阅者和一个消费者)。当你使用外部代理时,请检查该代理的 STOMP 页面,以了解它支持什么样的 STOMP 目的地和前缀。 :::
要从浏览器连接,对于 SockJS,你可以使用 sockjs-client。对于 STOMP,许多应用程序都使用了 jmesnil/stomp-websocket 库(也称为stomp.js),该库功能完整,已在生产中使用多年,但已不再维护。目前,JSteunou/webstomp-client 是该库最积极维护和发展的后继者。下面的示例代码是基于它的:
var socket = new SockJS("/spring-websocket-portfolio/portfolio");
var stompClient = webstomp.over(socket);
stompClient.connect({}, function(frame) {
})
另外,如果你通过 WebSocket(没有 SockJS)连接,你可以使用以下代码:
var socket = new WebSocket("/spring-websocket-portfolio/portfolio");
var stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame) {
})
请注意,前面的例子中的 stompClient 不需要指定登录和密码头文件。即使它这样做了,它们在服务器端也会被忽略(或者说,被覆盖)。关于认证的更多信息,请参见 连接到 Broker 和 认证。
更多的示例代码见:
- 使用 WebSocket 建立一个交互式网络应用程序:一个入门指南
- Stock Portfolio(股票组合) :一个示例程序
一个例子
本章内容虽然描述得还算清楚,但是在实际实现的过程中,发现还是有一些小坑,不知道是否是由于我的环境对不上的问题。
后端还在在 前面的例子 基础上修改。将 webSocket 的配置改成了如下方式
package cn.mrcode.study.springdocsread.websocket;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.messaging.simp.config.MessageBrokerRegistry;
import org.springframework.web.socket.config.annotation.EnableWebSocketMessageBroker;
import org.springframework.web.socket.config.annotation.StompEndpointRegistry;
import org.springframework.web.socket.config.annotation.WebSocketMessageBrokerConfigurer;
import org.springframework.web.socket.server.standard.TomcatRequestUpgradeStrategy;
/**
* @author mrcode
*/
@Configuration
@EnableWebSocketMessageBroker
public class MyWebSocketConfig implements WebSocketMessageBrokerConfigurer {
@Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
// portfolio 是 WebSocket(或 SockJS)的端点的 HTTP URL。客户端需要连接以进行 WebSocket 握手。
registry.addEndpoint("/portfolio")
.setAllowedOriginPatterns("*") // 注意跨域设置
.withSockJS();
}
@Override
public void configureMessageBroker(MessageBrokerRegistry config) {
// 目标标头以 /app 开头的 STOMP 消息会被路由到 @Controller 类中的 @MessageMapping 方法。
config.setApplicationDestinationPrefixes("/app");
// 使用内置的消息代理进行订阅和广播,并且 将目标标头以 /topic 或 /queue 开头的消息路由到代理。
config.enableSimpleBroker("/topic", "/queue");
}
/**
* spring 为了支持每种容器自己的 websocket 升级策略,抽象了 RequestUpgradeStrategy,
* <p>对 tomcat 提供了 TomcatRequestUpgradeStrategy 策略</p>
* 如果不申明这个,就会在启动的时候抛出异常:No suitable default RequestUpgradeStrategy found
*/
@Bean
public TomcatRequestUpgradeStrategy tomcatRequestUpgradeStrategy() {
return new TomcatRequestUpgradeStrategy();
}
}
前端由于 JSteunou/webstomp-client 没有提供直接的 cdn 相关的 js 文件引用,只能使用 npm 的方式先下载,然后引用
npm install webstomp-client
然后在 html 中引用,这里你可以想成是,通过 npm install 下载之后,将 webstomp.min.js 拷贝到自己的项目中引用
<script type="text/javascript" src="node_modules/webstomp-client/dist/webstomp.min.js"></script>
下面是 html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>STOMP</title>
</head>
<body>
</body>
<script type="text/javascript" src="/node_modules/webstomp-client/dist/webstomp.min.js"></script>
<script type="text/javascript" src="https://cdnjs.loli.net/ajax/libs/sockjs-client/1.6.0/sockjs.js"></script>
<script>
// 这里使用 sockJs 库链接
var socket = new SockJS("http://localhost:8080/portfolio");
// 文章上说可以使用 WebSocket 链接。 实际上我这里测试不可以,会报错
// var socket = new WebSocket("ws://localhost:8080/portfolio");
var stompClient = webstomp.over(socket);
stompClient.connect({}, function(frame) {
console.log(frame)
})
</script>
</html>
打开该页面后,就能看到控制台输出如下的信息