Websocket是客户端和服务器之间的全双工(持久)连接,因此两者可以彼此共享信息,而无需重复建立新的连接。 这消除了从客户端重复轮询以从服务器获取更新的需要。
并非所有浏览器都支持Websocket,因此我们利用SockJS javascript库创建WebSocket连接。 SockJS充当抽象层,它首先检查是否对WebSockets提供本机支持,如果不支持,它将尝试使用浏览器支持的协议模仿WebSocket行为。
Spring使用STOMP协议提供了对Websocket的支持,因此我们将使用STOMP.js (用于STOMP协议的javascript实现)与服务器进行交互。
在这篇文章中,客户端将与服务器建立一个websocket连接,并调用在服务器应用程序中注册的websocket端点以接收一些消息。 除此之外,服务器还会从服务器中触发的后台活动向客户端发送一些实时消息。
首先配置服务器。 首先进入start.spring.io并使用以下设置创建一个新的spring boot项目:
配置Websocket
基本的websocket配置包括:
- 创建用于发布消息的主题地址(
/topic/messages
) - 客户端用于调用服务器(
/ws
)中的WebSocket端点的URL的可选前缀 - 定义客户端用于与服务器建立WebSocket连接的URL。 (
/connect
)
@Configuration @EnableWebSocketMessageBroker public class WebsocketConfiguration implements WebSocketMessageBrokerConfigurer { @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint( "/connect" ).withSockJS(); } @Override public void configureMessageBroker(MessageBrokerRegistry registry) { registry.enableSimpleBroker( "/topic/messages" ); registry.setApplicationDestinationPrefixes( "/ws" ); } }
创建Websocket端点
我们将创建一个Spring控制器,它将具有两个WebSocket端点,如下所示。 这些端点之一将创建一个无限运行的任务,向客户端发送消息,而另一个端点将取消正在运行的任务。
@Controller public class WebsocketController { @Autowired SimpMessagingTemplate simpMessagingTemplate; String destination = "/topic/messages" ; ExecutorService executorService = Executors.newFixedThreadPool(1); Future<?> submittedTask; @MessageMapping( "/start" ) public void startTask(){ if ( submittedTask != null ){ simpMessagingTemplate.convertAndSend(destination, "Task already started" ); return ; } simpMessagingTemplate.convertAndSend(destination, "Started task" ); submittedTask = executorService.submit(() -> { while ( true ){ simpMessagingTemplate.convertAndSend(destination, LocalDateTime.now().toString() + ": doing some work" ); Thread.sleep(10000); } }); } @MessageMapping( "/stop" ) @SendTo( "/topic/messages" ) public String stopTask(){ if ( submittedTask == null ){ return "Task not running" ; } try { submittedTask.cancel( true ); } catch (Exception ex){ ex.printStackTrace(); return "Error occurred while stopping task due to: " + ex.getMessage(); } return "Stopped task" ; } }
我已使用上述两种方法将消息发送到配置中定义的主题URL:
- 通过注释为
@MessageMapping
的方法的返回值 - 使用
SimpMessagingTemplate
Spring boot配置了一个SimpMessagingTemplate
实例,我们可以利用它来向主题发送消息。
就像我们定义REST API端点或查看端点的方式一样,通过传递端点URL来使用@MessageMapping
注释websocket端点。
用Javascript创建Websocket客户端
我们将首先创建一个HTML页面,其中包含用于启动连接的按钮,然后调用我们定义的websocket端点,如下所示:
< div class = "content" id= "websocket" > < div > </ div > < div class = "row" > < div class = "col" > <button class = "btn btn-sm btn-info" @click= "connect" >Create connection</button> <button class = "btn btn-sm btn-success" @click= "startTask" >Start Task</button> <button class = "btn btn-sm btn-danger" @click= "stopTask" >Stop Task</button> <button class = "btn btn-sm btn-primary" @click= "disconnect" >Close connection</button> </ div > </ div > < div > </ div > < div class = "row" > < div class = "col" > <ul class = "list-group" style= "height: 500px; overflow:scroll;" > <li class = "list-group-item d-flex justify-content-between align-items-center" v- for = "(m,idx) in messages" :key= "'m-'+idx" > {{m}} </li> </ul> </ div > </ div > </ div >
重要的是要注意上面HTML中链接的sockjs和STOMP js库。
所有工作都在Javascript代码中进行,如下所示:
var stompClient = null; $(function(){ new Vue({ el: "#websocket" , data: { messages: [] }, methods: { connect: function(){ var socket = new SockJS( '/connect' ); stompClient = Stomp.over(socket); var that = this ; stompClient.connect({}, function(frame) { that.handleMessageReceipt( "Connected" ); stompClient.subscribe( '/topic/messages' , function(messageOutput) { that.handleMessageReceipt(messageOutput.body); }); }); }, disconnect: function(){ if (stompClient != null) { stompClient.disconnect(); } this .handleMessageReceipt( "Disconnected" ); }, startTask: function(){ if ( stompClient != null ){ stompClient.send( "/ws/start" ); } else { alert( "Please connect first" ); } }, stopTask: function(){ if ( stompClient != null ){ stompClient.send( "/ws/stop" ); } else { alert( "Please connect first" ); } }, handleMessageReceipt: function (messageOutput) { this .messages.push(messageOutput); } } }); });
connect方法使用/connect
端点启动websocket连接。 开始任务方法和停止任务方法调用我们在WebsocketController
定义的两个websocket端点
stompClient
接收到的消息由`handleMessageReceipt`方法处理。
运行应用程序后,可以创建连接,启动任务,停止任务并获取如下所示的消息:
完整应用程序的代码可以在这里找到。
翻译自: https://www.javacodegeeks.com/2020/02/using-websocket-with-spring-framework-and-vuejs.html