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 的集成方式。如果有任何问题或建议,欢迎在评论区讨论!

相关推荐
长栎2 小时前
写 for 循环写了十年,你却从没用过迭代器模式最狠的那一面
后端
LiaCode2 小时前
Redis 在生产项目的使用
前端·后端
用户559822481222 小时前
Docker Compose Down 导致容器数据误删——ext4 日志恢复全记录
后端
LiaCode2 小时前
一天学完 redis 的爽翻版核心知识总结
前端·后端
大刚测试开发实战2 小时前
如何内网穿透访问本地私有化部署的TestHub
前端·后端·github
xiaodaoluanzha3 小时前
迄今為止,最簡單的編程語言 Nolang
前端·后端
Csvn3 小时前
Docker 容器管理入门 — 从镜像到容器编排
后端
用户762352425913 小时前
ShardingJDBC
后端
行者全栈架构师3 小时前
IDEA 中 Maven 项目的 15 个红色报错快速解决方法
java·后端
Colin草率地做慢慢地改3 小时前
关于QuickStore这个项目的重构(2)- 数据库建表文件
后端·面试·架构