1. 简介

本文将介绍 Quarkus 框架中的 quarkus-websockets-next 扩展。这是一个新的实验性扩展,用于在应用中支持 WebSocket 协议。

⚠️ Quarkus WebSockets Next 是替代旧版 Quarkus WebSockets 扩展的解决方案,使用更简单且性能更优。

与 Quarkus 的一贯风格不同,该扩展不支持 Jakarta WebSockets API,而是提供了一套简化且更现代化的 WebSocket 操作方式。它通过自定义注解和方法实现功能,不仅增强了灵活性,还内置了 JSON 支持等特性。

尽管如此,Quarkus WebSockets Next 仍基于 Quarkus 核心构建,这意味着我们既能获得预期的性能和可扩展性,又能享受 Quarkus 带来的开发体验。

2. 依赖配置

新建项目

如果创建全新项目,可直接使用 Maven 命令生成已包含 websockets-next 扩展的项目结构:

$ mvn io.quarkus.platform:quarkus-maven-plugin:3.16.4:create \
    -DprojectGroupId=com.example.demo \
    -DprojectArtifactId=quarkus-websockets-demo \
    -Dextensions='websockets-next'

⚠️ 必须使用 io.quarkus.platform:quarkus-maven-plugin,因为该扩展仍处于实验阶段。

现有项目集成

对于已有项目,只需在 pom.xml 添加依赖:

<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-websockets-next</artifactId>
</dependency>

3. 服务端接口

安装扩展后,即可开始使用 WebSocket。服务端接口通过 @WebSocket 注解创建:

@WebSocket(path = "/echo")
public class EchoWebsocket {
    // WebSocket 实现代码
}

这会创建一个监听指定路径的接口。和 Quarkus 其他特性一样,路径支持参数和固定路径。

3.1 消息回调

要让接口发挥作用,需要处理消息。WebSocket 支持文本和二进制两种消息类型,通过 @OnTextMessage@OnBinaryMessage 注解处理:

@OnTextMessage
public String onMessage(String message) {
    return message;
}

这个简单示例实现了回声服务器:将收到的消息原样返回。

若需处理二进制消息,使用 @OnBinaryMessage 注解:

@OnBinaryMessage
public Buffer onMessage(Buffer message) {
    return message;
}

此时接收和返回的是 io.vertx.core.buffer.Buffer 对象,包含原始消息字节。

3.2 方法参数与返回值

消息处理方法不仅能接收原始消息,Quarkus 还能注入多种参数:

规则: 消息处理方法必须且只能有一个表示消息载荷的参数,其类型决定访问方式:

  • Bufferbyte[]:原始字节
  • String:解码后的字符串
  • JsonObject/JsonArray:JSON 解码
  • 其他类型:自动反序列化为 JSON
@OnTextMessage
public Message onTextMessage(Message message) {
    return message;
}
record Message(String message) {}

返回值规则:

  • 支持上述所有类型
  • 返回 void 表示不发送响应

其他可用参数

  1. 路径参数(通过 @PathParam):

    @WebSocket(path = "/chat/:user")
    public class ChatWebsocket {
    
     @OnTextMessage(broadcast = true)
     public String onTextMessage(String message, @PathParam("user") String user) {
         return user + ": " + message;
     }
    }
    
  2. 连接对象WebSocketConnection):

    @OnTextMessage
    public Map<String, String> onTextMessage(String message, WebSocketConnection connection) {
     return Map.of(
         "message", message,
         "connection", connection.toString()
     );
    }
    

通过连接对象可获取:

  • 唯一连接 ID
  • 连接建立时间
  • URL 路径参数
  • 手动发送消息或关闭连接
@OnTextMessage
public void onTextMessage(String message, WebSocketConnection connection) {
    if ("close".equals(message)) {
        connection.sendTextAndAwait("Goodbye");
        connection.closeAndAwait();
    }
}

3.3 连接生命周期回调

除了消息处理,还能监听连接打开(@OnOpen)和关闭(@OnClose)事件:

@OnOpen
public void onOpen() {
    LOG.info("连接已建立");
}

@OnClose
public void onClose() {
    LOG.info("连接已关闭");
}

这些回调不能接收消息载荷,但支持其他参数类型。

@OnOpen 方法可返回值,连接建立后立即发送给客户端:

@OnOpen
public String onOpen(WebSocketConnection connection) {
    return "欢迎, " + connection.id();
}

3.4 访问连接对象

方式一:CDI 注入

WebSocketConnection 作为 CDI Bean 注入(仅限 WebSocket 上下文):

@ApplicationScoped
public class CdiConnectionService {
    @Inject
    WebSocketConnection connection;
}

❌ 在非 WebSocket 上下文(如 HTTP 请求)中调用会抛出 ContextNotActiveException

方式二:所有连接管理

注入 OpenConnections 管理所有活跃连接:

@Inject
OpenConnections connections;

可用于查询或广播消息:

public void sendToAll(String message) {
    connections.forEach(conn -> conn.sendTextAndAwait(message));
}

✅ 此方式可在任何上下文中使用

3.5 错误处理

通过 @OnError 注解处理回调中的异常:

@OnError
public String onError(RuntimeException e) {
    return e.toString();
}

规则:

  • 必须包含异常参数
  • 支持多异常处理(按精确度匹配)
@OnError
public String onIoException(IOException e) {
    // 处理 IOException 及其子类
}

@OnError
public String onException(Exception e) {
    // 处理其他 Exception(排除 IOException)
}

4. 客户端 API

Quarkus 同样支持编写 WebSocket 客户端。

4.1 基础连接器

使用 BasicWebSocketConnector 实现原始消息收发:

@Inject
BasicWebSocketConnector connector;

建立连接并注册消息回调:

WebSocketClientConnection connection = connector
  .baseUri(serverUrl)
  .executionModel(BasicWebSocketConnector.ExecutionModel.NON_BLOCKING)
  .onTextMessage((c, m) -> {
      // 处理服务端消息
  })
  .connectAndAwait();

⚠️ WebSocket 是全双工异步协议,必须注册回调处理服务端消息

发送消息示例:

connection.sendTextAndAwait("Hello, World!");

❌ 连接对象非线程安全,避免多线程并发写入

4.2 增强型客户端

类似服务端接口,可通过注解创建结构化客户端:

@WebSocketClient(path = "/json")
class RichWebsocketClient {
    // 客户端实现
}

使用相同注解处理事件:

@OnTextMessage
void onMessage(String message, WebSocketClientConnection connection) {
    // 处理消息
}

参数规则与服务端一致,但连接对象类型为 WebSocketClientConnection

通过 WebSocketConnector<T> 建立连接:

@Inject
WebSocketConnector<RichWebsocketClient> connector;

WebSocketClientConnection connection = connector
  .baseUri(serverUrl)
  .connectAndAwait();

获取客户端实例:

@Inject
Instance<RichWebsocketClient> clients;

⚠️ 客户端实例仅在特定上下文可用,需注意异步事件顺序

5. 总结

本文介绍了 Quarkus 中 websockets-next 扩展的基础用法,包括服务端接口和客户端实现。虽然只覆盖了基本功能,但该库已能处理多种高级场景。对于需要高性能 WebSocket 通信的 Quarkus 应用,这是一个值得尝试的现代化解决方案。


原始标题:Quarkus WebSockets Next | Baeldung