查看原文
其他

使用 Spring Boot3.3 + Thymeleaf + WebSocket 实现服务端数据推送及心跳机制

编程疏影 路条编程
2024-09-05


使用 Spring Boot3.3 + Thymeleaf + WebSocket 实现服务端数据推送及心跳机制

在现代 Web 应用中,实时通信成为越来越多场景的核心需求。例如,在线聊天、股票行情推送、游戏服务器的实时状态更新等,都需要在客户端和服务器之间保持实时数据交互。传统的 HTTP 请求/响应模型虽然能够满足部分需求,但由于其基于轮询或长连接的实现方式,往往存在效率低、延迟高等问题。为了解决这些不足,WebSocket 应运而生。

WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议,它能够在客户端和服务器之间建立一条持久的连接通道,允许双方随时推送数据,而不需要像传统 HTTP 那样频繁地建立和关闭连接。WebSocket 在降低通信延迟、减少网络资源消耗等方面具有显著优势,因此在高实时性、高并发的应用场景中被广泛采用。

然而,WebSocket 的优势也带来了新的挑战。由于 WebSocket 连接的长期性,如何保持连接的稳定性,尤其是在网络不稳定或长时间未进行数据传输的情况下,是一个需要重点关注的问题。如果不进行适当的处理,WebSocket 连接可能会因为各种原因被中断,导致数据无法及时推送到客户端,影响用户体验。因此,在实际开发中,我们通常需要引入“心跳机制”,通过定期发送心跳消息来检测连接的状态,并及时采取措施恢复中断的连接。

本文将通过一个基于 Spring Boot 和 Thymeleaf 的示例项目,深入探讨如何实现 WebSocket 服务端数据推送及心跳机制。我们将展示如何在服务端处理 WebSocket 连接的建立、消息的发送与接收、以及心跳机制的实现。同时,还将结合前端的实现,演示如何通过 JavaScript 与 WebSocket 进行交互,从而实现一个完整的实时通信系统。本篇文章将为大家提供一个全面、深入的指导,帮助在实际项目中高效实现 WebSocket 通信及其稳定性保障。

运行效果:

若想获取项目完整代码以及其他文章的项目源码,且在代码编写时遇到问题需要咨询交流,欢迎加入下方的知识星球。

项目配置

pom.xml 配置

首先,我们需要在 Spring Boot 项目的 pom.xml 文件中引入相关依赖,包括 WebSocket 支持、Thymeleaf 模板引擎以及前端相关的依赖:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">

<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.3</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.icoderoad</groupId>
<artifactId>heartbeat</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>heartbeat</name>
<description>Demo project for Spring Boot</description>

<properties>
<java.version>17</java.version>
</properties>
<dependencies>

<!-- Spring Boot Starter Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

<!-- Spring Boot WebSocket -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

<!-- Thymeleaf -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>

<!-- Lombok (用于简化代码) -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>

<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

</project>
2.2 application.yml 配置

在 application.yml 中,我们可以配置 WebSocket 的相关属性以及其他必要的配置项:

server:
port: 8080

spring:
thymeleaf:
cache: false
mode: HTML
encoding: UTF-8
servlet:
content-type: text/html

logging:
level:
org.springframework.web.socket: DEBUG

WebSocket 服务端实现

应用启动类 HeartbeatApplication:

package com.icoderoad.heartbeat;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication
@EnableScheduling // 启用定时任务
public class HeartbeatApplication {

public static void main(String[] args) {
SpringApplication.run(HeartbeatApplication.class, args);
}

}
配置 WebSocket 处理器

我们首先定义一个 WebSocket 处理器,用于处理客户端的连接、消息以及关闭事件:

package com.icoderoad.heartbeat.handler;

import java.util.HashSet;
import java.util.Set;

import org.springframework.stereotype.Component;
import org.springframework.web.socket.CloseStatus;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

@Component
public class WebSocketHandler extends TextWebSocketHandler {

// 存储当前活跃的会话
private static final Set<WebSocketSession> sessions = new HashSet<>();

@Override
public void afterConnectionEstablished(WebSocketSession session) throws Exception {
// 连接建立时添加到 sessions 集合
sessions.add(session);
System.out.println("连接建立:" + session.getId());
}

@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) throws Exception {
// 接收到消息时处理逻辑
System.out.println("接收到消息:" + message.getPayload());
session.sendMessage(new TextMessage("服务端响应:" + message.getPayload()));
}

@Override
public void afterConnectionClosed(WebSocketSession session, CloseStatus status) throws Exception {
// 连接关闭时从 sessions 集合移除
sessions.remove(session);
System.out.println("连接关闭:" + session.getId());
}

@Override
public void handleTransportError(WebSocketSession session, Throwable exception) throws Exception {
// 处理连接错误
System.err.println("连接出错:" + exception.getMessage());
sessions.remove(session);
}

// 发送心跳消息
public void sendHeartbeat() {
for (WebSocketSession session : sessions) {
if (session.isOpen()) {
try {
session.sendMessage(new TextMessage("heartbeat"));
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
WebSocket 配置类

接下来,我们配置 WebSocket 的映射路径并注册处理器:

package com.icoderoad.heartbeat.config;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.config.annotation.EnableWebSocket;
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;

import com.icoderoad.heartbeat.handler.WebSocketHandler;

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {

private final WebSocketHandler myWebSocketHandler;

public WebSocketConfig(WebSocketHandler myWebSocketHandler) {
this.myWebSocketHandler = myWebSocketHandler;
}

@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
// 注册 WebSocket 处理器并映射到 /ws 路径
registry.addHandler(myWebSocketHandler, "/ws").setAllowedOrigins("*");
}
}

心跳机制的实现

为了保持 WebSocket 连接的活跃,我们可以通过定时发送心跳消息来检测连接状态。可以使用 Spring 的 @Scheduled 注解实现定时任务:

package com.icoderoad.heartbeat.scheduler;

import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import com.icoderoad.heartbeat.handler.WebSocketHandler;

@Component
public class HeartbeatScheduler {

private final WebSocketHandler myWebSocketHandler;

public HeartbeatScheduler(WebSocketHandler myWebSocketHandler) {
this.myWebSocketHandler = myWebSocketHandler;
}

@Scheduled(fixedRate = 30000) // 每隔30秒发送一次心跳
public void sendHeartbeat() {
myWebSocketHandler.sendHeartbeat();
}
}

视图控制类:

package com.icoderoad.heartbeat.controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class IndexController {

@GetMapping("/")
public String index(Model model) {
return "index";
}

}

前端实现

前端使用 Thymeleaf 模板引擎结合 Bootstrap 进行简单的界面展示,并通过 JavaScript 实现与 WebSocket 的通信。

Thymeleaf 模板

在 src/main/resources/templates 目录下创建 index.html 模板文件作为前端页面。

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>WebSocket 示例</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<div class="container">
<h1>WebSocket 示例</h1>
<button id="connectBtn" class="btn btn-primary">连接</button>
<button id="disconnectBtn" class="btn btn-danger">断开</button>
<div class="mt-3">
<input type="text" id="messageInput" class="form-control" placeholder="输入消息">
<button id="sendBtn" class="btn btn-success mt-2">发送消息</button>
</div>
<div class="mt-3">
<h3>接收消息:</h3>
<div id="messages"></div>
</div>
</div>

<script>
let websocket;

document.getElementById('connectBtn').addEventListener('click', function () {
websocket = new WebSocket("ws://localhost:8080/ws");

websocket.onopen = function () {
console.log("WebSocket 连接成功");
};

websocket.onmessage = function (event) {
console.log("收到消息:" + event.data);
document.getElementById('messages').innerHTML += '<p>' + event.data + '</p>';
};

websocket.onclose = function () {
console.log("WebSocket 连接关闭");
};
});

document.getElementById('disconnectBtn').addEventListener('click', function () {
if (websocket) {
websocket.close();
}
});

document.getElementById('sendBtn').addEventListener('click', function () {
const message = document.getElementById('messageInput').value;
if (websocket && websocket.readyState === WebSocket.OPEN) {
websocket.send(message);
}
});
</script>
</body>
</html>

总结

本文详细介绍了如何在 Spring Boot 项目中使用 WebSocket 实现服务端数据推送和心跳机制。通过结合 Thymeleaf 模板引擎和 Bootstrap,我们实现了一个简单的前端页面,并展示了 WebSocket 的基本用法。WebSocket 的心跳机制有助于保持连接的稳定性,在实际项目中可以根据需要进一步优化心跳策略和消息处理逻辑。


今天就讲到这里,如果有问题需要咨询,大家可以直接留言或扫下方二维码来知识星球找我,我们会尽力为你解答。


AI资源聚合站已经正式上线,该平台不仅仅是一个AI资源聚合站,更是一个为追求知识深度和广度的人们打造的智慧聚集地。通过访问 AI 资源聚合网站 https://ai-ziyuan.techwisdom.cn/,你将进入一个全方位涵盖人工智能和语言模型领域的宝藏库。


作者:路条编程(转载请获本公众号授权,并注明作者与出处)


继续滑动看下一个
路条编程
向上滑动看下一个

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存