Spring Boot集成Websocket服务以及连接时需要注意的问题

概述

在现代 Web 应用中,实时通信功能变得越来越重要。无论是聊天应用、实时通知系统还是协作工具,WebSocket 技术都提供了全双工通信能力。本文将详细介绍如何在 Spring Boot 中集成 WebSocket 服务,构建一个完整的实时通信应用。

项目结构

XML 复制代码
websocket-learning/
  ├── src/main/java/com/zgr/websocket/learning/
  │   ├── WebsocketLearningApplication.java     # 主应用类
  │   ├── webscket/
  │   │   ├── WebsocketConfig.java              # WebSocket 配置
  │   │   ├── CustomHandshakeInterceptor.java   # 自定义握手拦截器
  │   │   └── CustomWebsocketHandler.java       # WebSocket 处理器
  │   └── controller/
  │       └── WebSocketController.java          # REST 控制器
  └── src/main/resources/
      └── application.properties                # 应用配置

技术栈

  • Java 17

  • Spring Boot (Spring Boot 4.0.4)

  • Maven 构建工具

  • WebSocket 实时通信协议

Maven 依赖

XML 复制代码
<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-webmvc</artifactId>
  </dependency>
  <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-websocket</artifactId>
  </dependency>
  <dependency>
      <groupId>org.projectlombok</groupId>
      <artifactId>lombok</artifactId>
      <optional>true</optional>
  </dependency>

核心代码实现

1. 应用配置

XML 复制代码
application.properties
server.port=8089
server.servlet.context-path=/api/websocket
spring.application.name=websocket-learning

2.主应用类

java 复制代码
@SpringBootApplication
  public class WebsocketLearningApplication {
      public static void main(String[] args) {
          SpringApplication.run(WebsocketLearningApplication.class, args);
      }
  }

3. WebSocket 配置类

WebsocketConfig.java 是整个 WebSocket 服务的核心配置。

java 复制代码
@Configuration
  @EnableWebSocket
  public class WebsocketConfig {

      @Bean
      public WebSocketConfigurer webSocketConfigurer(
              HandshakeInterceptor[] handshakeInterceptors,
              WebSocketHandler webSocketHandler) {
          return registry -> {
              registry.addHandler(webSocketHandler, "/ws")
                      .addInterceptors(handshakeInterceptors)
                      .setAllowedOrigins("*");
          };
      }

      @Bean
      public WebSocketHandler webSocketHandler() {
          return new CustomWebsocketHandler();
      }

      @Bean
      public HandshakeInterceptor handshakeInterceptor() {
          return new CustomHandshakeInterceptor();
      }
  }

关键点:

  • @EnableWebSocket:启用 WebSocket 支持

  • 注册 WebSocket 端点路径:/ws

  • 配置跨域允许:setAllowedOrigins("*")

  • 注入自定义处理器和拦截器

4. 自定义握手拦截器

CustomHandshakeInterceptor.java 用于在握手前后执行自定义逻辑。

java 复制代码
@Slf4j
  public class CustomHandshakeInterceptor implements HandshakeInterceptor {

      @Override
      public boolean beforeHandshake(ServerHttpRequest request,
                                     ServerHttpResponse response,
                                     WebSocketHandler wsHandler,
                                     Map<String, Object> attributes) {
          log.info("beforeHandshake");
          // 可在此处进行身份验证、参数解析等操作
          return true; // 返回 true 允许连接,false 拒绝连接
      }

      @Override
      public void afterHandshake(ServerHttpRequest request,
                                 ServerHttpResponse response,
                                 WebSocketHandler wsHandler,
                                 @Nullable Exception exception) {
          // 握手完成后的处理逻辑
      }
  }

5. WebSocket 消息处理器

CustomWebsocketHandler.java 继承自 TextWebSocketHandler,处理所有 WebSocket 事件。

java 复制代码
@Component
  @Slf4j
  public class CustomWebsocketHandler extends TextWebSocketHandler {

      // 存储所有活跃的 WebSocket 会话
      private final Map<String, WebSocketSession> sessions = new ConcurrentHashMap<>();

      @Override
      public void afterConnectionEstablished(WebSocketSession session) {
          String sessionId = session.getId();
          sessions.put(sessionId, session);
          log.info("WebSocket连接建立成功:{}", sessionId);
      }

      @Override
      protected void handleTextMessage(WebSocketSession session, TextMessage message) {
          String payload = message.getPayload();
          log.info("收到消息:{}", payload);

          // 发送回复消息
          String replyMessage = "服务器收到消息:" + payload;
          session.sendMessage(new TextMessage(replyMessage));
      }

      @Override
      public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {
          String sessionId = session.getId();
          sessions.remove(sessionId);
          log.info("WebSocket连接关闭:{}", sessionId);
      }

      @Override
      public void handleTransportError(WebSocketSession session, Throwable exception) {
          log.error("WebSocket传输错误", exception);
      }

      // 广播消息到所有连接的客户端
      public void broadcast(String message) {
          for (WebSocketSession session : sessions.values()) {
              try {
                  session.sendMessage(new TextMessage(message));
              } catch (IOException e) {
                  log.error("WebSocket发送消息错误", e);
              }
          }
      }
  }

生命周期方法:

  1. afterConnectionEstablished:连接建立时调用

  2. handleTextMessage:处理客户端发送的文本消息

  3. afterConnectionClosed:连接关闭时调用

  4. handleTransportError:传输错误时调用

6. REST 控制器

WebSocketController.java 提供 HTTP 接口,用于向所有 WebSocket 客户端广播消息。

java 复制代码
@RestController
  @RequestMapping()
  public class WebSocketController {

      @Resource
      private CustomWebsocketHandler webSocketHandler;

      @PostMapping("/broadcast")
      public ResponseEntity<String> broadcastMessage(@RequestBody String message) {
          webSocketHandler.broadcast(message);
          return ResponseEntity.ok("消息广播成功");
      }
  }

测试应用

启动应用,应用将在 http://localhost:8089/api/websocket 启动,WebSocket 端点位于 ws://localhost:8089***/api/websocket***/ws,连接时,需要主要两个点,这个两个点是我遇到的问题,在这里做一个分享。

1.连接地址,需要加上Spring Boot配置中的server.servlet.context-path地址;

2.如果使用了Spring Security,一定要在配置中允许直接访问websocket的地址,配置的地址就是WebsocketConfig中配置注册WebsocketConfigurer中的paths。

使用Apifo连接Websocket服务并发送http请求广播消息

性能优化建议

  1. 连接数限制:设置最大连接数,防止资源耗尽

  2. 心跳机制:定期发送心跳包,检测无效连接

  3. 消息队列:高并发场景下使用消息队列缓冲

  4. 集群支持:使用 Redis 或专门的消息中间件支持多实例部署

  5. 压缩传输:对消息内容进行压缩,减少带宽占用

常见问题解决

1. 跨域问题

// 允许特定域

.setAllowedOrigins("https://example.com")

// 或允许所有域(开发环境)

.setAllowedOrigins("*")

2. 连接超时

// 配置 WebSocket 会话超时时间

session.setMaxIdleTimeout(30000); // 30秒

3. 消息大小限制

// 配置消息缓冲区大小

session.setTextMessageSizeLimit(64 * 1024); // 64KB

总结

通过本文的示例,我们实现了一个完整的 Spring Boot WebSocket 应用,包括:

  • ✅ WebSocket 基本配置和端点注册

  • ✅ 自定义握手拦截器

  • ✅ 完整的消息处理器(连接管理、消息处理、错误处理)

  • ✅ REST API 接口进行消息广播

  • ✅ 会话管理和消息广播功能

这个示例项目为构建实时通信应用提供了坚实的基础架构,可以根据实际需求进行扩展和优化。无论是构建聊天系统、实时通知服务还是在线协作工具,WebSocket 都是一个强大且高效的选择。

希望这篇博客能帮助你理解 Spring Boot 中 WebSocket 的集成方式。如果有任何问题或建议,欢迎在评论区讨论!

相关推荐
bearpping1 小时前
Spring Boot 整合 MyBatis 与 PostgreSQL 实战指南
spring boot·postgresql·mybatis
mygljx1 小时前
Spring Boot从0到1 -day02
java·spring boot·后端
程序员小郭831 小时前
Spring Ai 04 解决 ChatClient 初始化冲突问题
java·后端·spring
SuniaWang1 小时前
《Spring AI + 大模型全栈实战》学习手册系列 · 专题八:《RAG 系统安全与权限管理:企业级数据保护方案》
java·前端·人工智能·spring boot·后端·spring·架构
不吃香菜学java2 小时前
苍穹外卖-菜品分页查询
数据库·spring boot·tomcat·log4j·maven·mybatis
2301_805962932 小时前
ESP32远程OTA升级:从局域网到公网部署
网络·后端·http·esp32
cyforkk2 小时前
Spring Boot 3 集成 Swagger 踩坑实录:解决 doc.html 404 与 Unauthorized 拦截
spring boot·后端·html
十七号程序猿2 小时前
Java图书管理系统 | 无需配置任何环境,双击一键启动,开箱即用
java·spring boot·vue·毕业设计·毕设·源代码管理
小码哥_常2 小时前
当@RequestBody遇上Request:数据去哪儿了?
后端
umeelove352 小时前
Spring boot整合quartz方法
java·前端·spring boot