likes
comments
collection
share

Spring Boot 中的 WebSocket:如何构建实时应用程序

作者站长头像
站长
· 阅读数 3

介绍

WebSocket 利用单个长连接提供全双工通信通道。它是一个强大的工具,可以实现客户端和服务器之间的实时通信。在 Java 生态系统中,Spring 框架为基于 WebSocket 的通信提供良好的支持。在这篇文章中,我们将探讨如何使用 @EnableWebSocket 注释在 Spring 应用程序中开始使用 WebSocket。

为什么选择WebSocket

HTTP 的局限性

HTTP(超文本传输​协议)是一种请求-响应协议,自诞生以来一直是 Web 通信的支柱。然而,HTTP 有其局限性,尤其是在实时、双向通信方面。在传统的 用HTTP 请求的程序中,客户端发送请求,服务器发回响应。对于服务器需要在没有收到客户端请求的情况下,也要将数据推送到客户端的场景,http显然效率不高。人们开发了各种技术(例如长轮询和服务器发送事件)来规避此问题,但也有一些额外的局限性。

WebSocket 的诞生

引入 WebSocket 是为了克服这些限制并实现更具交互性的 Web 体验。它通过单个长连接在客户端和服务器之间建立全双工双向通信通道。这与 HTTP 不同,HTTP 中每次交互都需要一个新的请求-响应周期。 WebSocket 协议本身构建在 HTTP 之上,利用初始 HTTP 握手来建立 WebSocket 连接。

减少网络开销

WebSocket 的主要优点之一是它减少了网络开销。在 HTTP 中,每个请求都带有一组标头,其中包含有关请求的元数据。每次发出请求时都会发送这些标头,这对于偶尔的交互来说很好,但对于频繁的通信来说效率很低。使用 WebSocket,标头在初始握手期间仅发送一次,从而减少了通过网络传输的冗余数据量。

实时数据传输

WebSocket 在需要实时更新的场景中大放异彩。无论是聊天应用程序、实时体育比分更新还是股票市场仪表板,WebSocket 都可以在新数据可用时立即提供新数据。这在即使一秒钟的延迟都可能造成高昂代价的环境中至关重要。例如,在交易应用程序中,股票价格需要实时更新,以便交易者做出准确的决策。

可扩展性和资源利用率

在频繁更新的基于 HTTP 的应用程序中,服务器必须同时处理多个传入请求和传出响应,这会消耗更多的 CPU 和内存资源。使用 WebSocket,持久的单个连接可以更好地利用资源,因为服务器不必连续打开和关闭多个连接。这意味着可扩展性的提高,使您能够使用相同的硬件资源为更多的客户端提供服务。

双向通讯

WebSocket不仅允许服务器向客户端推送更新,还允许客户端与服务器实时通信。这种双向功能使 WebSocket 成为协作应用程序的绝佳选择。例如,在类似 在线 文档的应用程序中,多个用户可以编辑同一文档,并且一个用户的更改可以立即反映在其他人的界面上。

构建 Spring Boot 应用

Spring Boot 应用程序是任何基于 Spring 的项目的基础。让我们一步步了解构建websocket项目,创建到添加所需的依赖项。其中每个步骤对于在 Spring 中成功设置支持 WebSocket 的应用程序都至关重要。

添加maven依赖

下一步涉及将必要的依赖项添加到 Maven pom.xml 文件中。对于 WebSocket 支持,Spring 提供了 spring-boot-starter-websocket 依赖项。以下是将其包含在 Maven 配置中的方法:

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-websocket</artifactId>
</dependency>

这个包包含在 Spring 中开始使用 WebSocket的资源 ,无需额外配置单独的依赖项。它自动添加 WebSocket 库并提供自动配置

带有@EnableWebSocket的配置类

在 Spring 中,配置类充当设置各种功能的支柱。这些类用 @Configuration 进行注释,向 Spring 发出信号,表明它们定义了项目所需的 bean 和其他设置。使用 WebSocket 时,@EnableWebSocket 注释对于激活必要的组件并为实时通信奠定基础

@EnableWebSocket的作用

当您使用 @EnableWebSocket 注解配置类时,您实际上是在 Spring 应用程序中打开 WebSocket 功能。此注释注册 WebSocket 处理程序并设置处理 WebSocket 消息的基础。这是一种启用 WebSocket 功能的声明式方法,无需深入研究手动设置 WebSocket 服务器的具体细节。

实现WebSocketConfigurer

@EnableWebSocket 的配置类通常会实现 WebSocketConfigurer 接口。该接口为您提供了一个方法,registerWebSocketHandlers( WebSocketHandlerRegistryregistry),您需要重写该方法。您可以在该方法中指定 URL 映射并将它们与其相应的 WebSocket 处理程序关联起来。

@Configuration
@EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer {
  
  @Override
  public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
    registry.addHandler(myHandler(), "/myHandler");
  }
  
}

添加自定义设置

WebSocketHandlerRegistry 允许您添加特定于 WebSocket 处理程序的自定义设置和配置。例如,您可以指定允许的来源来控制可以从哪些来源访问您的 WebSocket 服务器,如下所示:

@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
  registry.addHandler(myHandler(), "/myHandler")
          .setAllowedOrigins("http://example.com");
}

定义 Bean

还可以定义与 WebSocket 处理相关的 bean。您可以在此处添加其他自定义,例如为涉及向多个客户端广播消息的更复杂场景配置消息代理。

例如,您可以定义一个 WebSocketHandler bean:

@Bean
public WebSocketHandler myHandler() {
  return new MyHandler();
}

在上面的代码片段中,定义了一个 WebSocketHandler 类型的 bean,Spring 将管理该 bean。该处理程序在 registerWebSocketHandlers 方法中注册。

实现 WebSocketHandler

下一个关键步骤就是实现 WebSocketHandler 接口。该接口是 Spring 中 WebSocket 消息处理的核心。它定义了应用程序将用于管理 WebSocket 会话和消息事件的核心方法。

WebSocketHandler接口

Spring 中的 WebSocketHandler 接口提供了各种方法,您可以重写这些方法来处理不同的 WebSocket 事件,例如打开连接、发送消息、接收消息和错误处理。

afterConnectionEstablished(WebSocketSession session):在建立新的 WebSocket 会话后调用此方法。

handleTextMessage(WebSocketSession session, TextMessage message):此方法处理传入的文本消息。

handleBinaryMessage(WebSocketSession session, BinaryMessage message):此方法处理二进制消息。

afterConnectionClosed(WebSocketSession session, CloseStatus status):在 WebSocket 会话关闭后调用此方法。

创建自定义处理程序

创建自定义 WebSocket 处理程序涉及实现 WebSocketHandler 接口并根据您的特定要求重写其方法。下面是自定义 WebSocketHandler 的框架示例

import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;

public class MyHandler extends TextWebSocketHandler {

  @Override
  public void afterConnectionEstablished(WebSocketSession session) {

  }

  @Override
  protected void handleTextMessage(WebSocketSession session, TextMessage message) {

  }

  @Override
  public void afterConnectionClosed(WebSocketSession session, CloseStatus status) {

  }
  
}

与业务逻辑集成

在重写的方法中,您可以与 Spring 应用程序的其他服务和组件集成。这可以是简单地广播接收到的消息,也可以是更复杂的操作,例如与数据库或外部 API 集成。例如:

@Override
protected void handleTextMessage(WebSocketSession session, TextMessage message) {
  String payload = message.getPayload();
  someService.processPayload(payload);
}

异常处理

连接可能会断开,并且可能会发送格式错误的消息。因此,强大的错误处理至关重要。您可以重写handleTransportError方法来处理此类情况:

@Override
public void handleTransportError(WebSocketSession session, Throwable exception) {
  // 处理异常
}

会话属性和用户标识

传递到这些方法中的 WebSocketSession 对象使您可以访问会话级属性、标头和其他有用的数据点。这对于识别用户和维护会话状态特别有帮助,特别是在需要身份验证和授权的应用程序中。

public void afterConnectionEstablished(WebSocketSession session) {
  String username = session.getAttributes().get("username").toString();

}

线程安全和并发

注意多个线程可能同时访问 WebSocketHandler 的方法。如果您要维护处理程序中的任何状态,请确保它是线程安全的,以避免并发问题。

注册处理handler

注册 WebSocket 处理handler的过程是使 Spring Boot 应用程序能够处理 WebSocket 消息的最后一部分。可以通过 @EnableWebSocket 注解用了 WebSocket 支持。注册过程将您的自定义处理程序绑定到特定的 URL 端点,使客户端可以建立到该端点的 WebSocket 连接。

WebSocketHandlerRegistry

正如前面在配置类部分中提到的,WebSocketHandlerRegistry 是注册所有 WebSocket 处理程序的中心。通过这个注册表,Spring 可以识别您的自定义处理程序,并将其映射到应用程序中的特定路径。

如何注册handler

要注册您的处理程序,您通常会在传递到 registerWebSocketHandlers 方法的 WebSocketHandlerRegistry 对象上调用 addHandler 方法。 addHandler 的第一个参数是处理程序对象,第二个参数是可访问处理程序的 URL 映射。

@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
  registry.addHandler(myHandler(), "/ws");
}

自定义handler设置

addHandler 方法返回一个 WebSocketHandlerRegistration 对象,可以设置允许的来源来指定允许哪些网站连接到您的 WebSocket 端点

@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
  registry.addHandler(myHandler(), "/ws")
          .setAllowedOrigins("http://example.com");
}

拦截器

拦截器提供了一种在 WebSocket 握手之前和之后执行操作的方法,为您提供了实现身份验证或日志记录等功能的地方。要注册拦截器,可以使用 WebSocketHandlerRegistration 对象上的 addInterceptors 方法。

@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
  registry.addHandler(myHandler(), "/ws")
          .addInterceptors(new MyHandshakeInterceptor());
}

条件注册

有时,您可能需要根据运行时设置或配置文件有条件地注册处理程序。 Spring 的编程方法使您能够灵活地动态做出这些决策。

@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
  if (someCondition) {
    registry.addHandler(myHandler(), "/ws");
  }
}

多个handler

在更复杂的应用程序中,您可能有多个提供不同功能的 WebSocket 处理程序。注册多个处理程序很简单:

@Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) {
  registry.addHandler(myChatHandler(), "/chat");
  registry.addHandler(myNotificationHandler(), "/notifications");
}

测试配置

单元测试

开始测试 WebSocket 配置的最简单方法是对各个组件执行单元测试。 Spring 强大的测试框架有助于轻松地对 WebSocket 处理程序进行单元测试。 下面是一个可用于测试 WebSocketHandler 的单元测试示例:

@SpringBootTest
public class MyHandlerTest {

  @Autowired
  private MyHandler myHandler;

  @Test
  public void testHandleTextMessage() {
    WebSocketSession session = mock(WebSocketSession.class);
    TextMessage message = new TextMessage("Test message");
    myHandler.handleTextMessage(session, message);
    
  }
}

总结

Spring 框架通过 @EnableWebSocket 等易于使用的注释简化了 WebSocket 实现。这篇文章提供了如何在 Spring 中设置和配置 WebSocket 的快速指南,从初始设置到完整配置。 通过结合 Spring 和 WebSocket 的强大功能,您可以轻松创建高度交互的实时 Web 应用程序。只需几行配置,您就可以利用 WebSocket 来满足您的所有实时需求。