WebSockets Next extension reference guide

Unresolved directive in websockets-next-reference.adoc - include::{includes}/extension-status.adoc[]

`quarkus-websockets-next`扩展提供了用于定义 WebSocket 服务器和客户端端点的现代声明性 API。

The quarkus-websockets-next extension provides a modern declarative API to define WebSocket server and client endpoints.

The WebSocket protocol

_WebSocket_协议,在 RFC6455中有文档说明,建立了一个通过单一 TCP 连接在客户端和服务器之间创建双向通信通道的标准化方法。与 HTTP 不同,WebSocket 作为一种独立的 TCP 协议运行,但被设计成可以与 HTTP 无缝协作。例如,它重用了相同的端口,并与相同安全机制兼容。

The WebSocket protocol, documented in the RFC6455, establishes a standardized method for creating a bidirectional communication channel between a client and a server through a single TCP connection. Unlike HTTP, WebSocket operates as a distinct TCP protocol but is designed to function seamlessly alongside HTTP. For example, it reuses the same ports and is compatible with the same security mechanisms.

使用 WebSocket 进行交互会以使用“升级”标头的 HTTP 请求开始,以过渡到 WebSocket 协议。服务器不会用 `200 OK`响应,而是用 `101 Switching Protocols`响应来升级到 WebSocket 连接。在握手成功后,最初的 HTTP 升级请求中使用的 TCP 套接字仍然处于打开状态,这允许客户端和服务器持续地双向交换消息。

The interaction using WebSocket initiates with an HTTP request employing the 'Upgrade' header to transition to the WebSocket protocol. Instead of a 200 OK response, the server replies with a 101 Switching Protocols response to upgrade the HTTP connection to a WebSocket connection. Following this successful handshake, the TCP socket utilized in the initial HTTP upgrade request remains open, allowing both client and server to exchange messages in both direction continually.

HTTP and WebSocket architecture styles

尽管 WebSocket 与 HTTP 兼容并在 HTTP 请求中启动,但至关重要的是意识到这两个协议导致不同的架构和编程模型。

Despite WebSocket’s compatibility with HTTP and its initiation through an HTTP request, it’s crucial to recognize that the two protocols lead to distinctly different architectures and programming models.

使用 HTTP/REST 时,应用程序围绕处理各种 HTTP 方法和路径的资源/端点进行构造。客户端交互通过使用适当的方法和路径发出 HTTP 请求实现,遵循请求-响应模式。服务器根据路径、方法和标头将传入请求路由到相应处理程序,然后以明确定义的响应进行回复。

With HTTP/REST, applications are structured around resources/endpoints that handle various HTTP methods and paths. Client interaction occurs through emitting HTTP requests with appropriate methods and paths, following a request-response pattern. The server routes incoming requests to corresponding handlers based on path, method, and headers and then replies with a well-defined response.

相反,WebSocket 通常涉及一个端点,用于初始 HTTP 连接,之后所有消息都使用同一条 TCP 连接。它引入了一个完全不同的交互模型:异步且消息驱动的。

Conversely, WebSocket typically involves a single endpoint for the initial HTTP connection, after which all messages utilize the same TCP connection. It introduces an entirely different interaction model: asynchronous and message-driven.

与 HTTP 相反,WebSocket 是一个低级传输协议。消息格式、路由或处理要求客户端与服务器之间就消息语义达成事先协议。

WebSocket is a low-level transport protocol, in contrast to HTTP. Message formats, routing, or processing require prior agreement between the client and server regarding message semantics.

对于 WebSocket 客户端和服务器,HTTP 握手请求中的 Sec-WebSocket-Protocol 标头允许多个级别的消息协议协商。如果不存在,服务器和客户端必须建立自己的约定。

For WebSocket clients and servers, the Sec-WebSocket-Protocol header in the HTTP handshake request allows negotiation of a higher-level messaging protocol. In its absence, the server and client must establish their own conventions.

Quarkus WebSockets vs. Quarkus WebSockets Next

本指南使用 quarkus-websockets-next 扩展,它是 WebSocket API 的一个实现,与旧版 quarkus-websockets 扩展相比,效率和可用性更高。原始的 quarkus-websockets 扩展仍然可以访问,将获得持续的支持,但不太可能获得功能开发。

This guide utilizes the quarkus-websockets-next extension, an implementation of the WebSocket API boasting enhanced efficiency and usability compared to the legacy quarkus-websockets extension. The original quarkus-websockets extension remains accessible, will receive ongoing support, but it’s unlikely to receive to feature development.

quarkus-websockets 不同的是,quarkus-websockets-next 扩展 not 不会实现 Jakarta WebSocket 规范。相反,它引入了现代 API,优先考虑使用简单性。此外,它经过专门设计,可与 Quarkus 的反应架构和网络层无缝集成。

Unlike quarkus-websockets, the quarkus-websockets-next extension does not implement the Jakarta WebSocket specification. Instead, it introduces a modern API, prioritizing simplicity of use. Additionally, it’s tailored to integrate with Quarkus' reactive architecture and networking layer seamlessly.

Quarkus WebSockets Next 扩展使用的注解不同于 JSR 356 中的注解,尽管有时候它们使用相同的名称。JSR 注解带有 Quarkus WebSockets Next 扩展不遵循的语义。

The annotations utilized by the Quarkus WebSockets next extension differ from those in JSR 356 despite, sometimes, sharing the same name. The JSR annotations carry a semantic that the Quarkus WebSockets Next extension does not follow.

Project setup

要使用 websockets-next 扩展,您需要将 io.quarkus:quarkus-websockets-next 依赖项添加到项目中。

To use the websockets-next extension, you need to add the io.quarkus:quarkus-websockets-next depencency to your project.

pom.xml
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-websockets-next</artifactId>
</dependency>
build.gradle
implementation("io.quarkus:quarkus-websockets-next")

Endpoints

服务器和客户端 API 都允许您定义 endpoints,用于接收和发送消息。端点实现为 CDI bean,并支持注入。端点声明了 callback methods,其使用 @OnTextMessage@OnBinaryMessage@OnPong@OnOpen@OnClose@OnError 进行注解。这些方法用于处理各种 WebSocket 事件。通常,当已连接的客户端向服务器发送消息时,会调用用 @OnTextMessage 注解的方法,反之亦然。

Both the server and client APIs allow you to define endpoints that are used to consume and send messages. The endpoints are implemented as CDI beans and support injection. Endpoints declare callback-methods annotated with @OnTextMessage, @OnBinaryMessage, @OnPong, @OnOpen, @OnClose and @OnError. These methods are used to handle various WebSocket events. Typically, a method annotated with @OnTextMessage is called when the connected client sends a message to the server and vice versa.

客户端 API 还包括 connectors,用于配置和创建新的 WebSocket 连接。

The client API also includes client-connectors that are used to configure and create new WebSocket connections.

Server endpoints

服务器端点是使用 @io.quarkus.websockets.next.WebSocket 注解的类。WebSocket#path() 的值为用来定义端点的路径。

Server endpoints are classes annotated with @io.quarkus.websockets.next.WebSocket. The value of WebSocket#path() is used to define the path of the endpoint.

package org.acme.websockets;

import io.quarkus.websockets.next.WebSocket;
import jakarta.inject.Inject;

@WebSocket(path = "/chat/{username}") 1
public class ChatWebSocket {

}

因此,客户端可以使用 ws://localhost:8080/chat/your-name 连接到此 Web 套接字端点。如果使用 TLS,URL 是 wss://localhost:8443/chat/your-name

Thus, client can connect to this web socket endpoint using ws://localhost:8080/chat/your-name. If TLS is used, the URL is wss://localhost:8443/chat/your-name.

endpoint 路径相对于由 quarkus.http.root-path (默认为 /) 配置的 root context。例如,如果您向 application.properties 中添加 quarkus.http.root-path=/api,则客户端可以使用 http://localhost:8080/api/chat/the-name 连接到此端点。

The endpoint path is relative to the root context configured by the quarkus.http.root-path (which is / by default). For example, if you add quarkus.http.root-path=/api to your application.properties then a client can connect to this endpoint using http://localhost:8080/api/chat/the-name.

Client endpoints

客户端端点是使用 @io.quarkus.websockets.next.WebSocketClient 注解的类。WebSocketClient#path() 的值为用来定义此客户端将连接到的端点的路径。

Client endpoints are classes annotated with @io.quarkus.websockets.next.WebSocketClient. The value of WebSocketClient#path() is used to define the path of the endpoint this client will be connected to.

package org.acme.websockets;

import io.quarkus.websockets.next.WebSocketClient;
import jakarta.inject.Inject;

@WebSocketClient(path = "/chat/{username}") 1
public class ChatWebSocket {

}

客户端端点用于接收和发送消息。您需要使用 connectors API 来配置和打开新的 WebSocket 连接。

Client endpoints are used to consume and send messages. You’ll need the client-connectors to configure and open new WebSocket connections.

Path parameters

WebSocket 端点的路径可以包含路径参数。语法与 JAX-RS 资源相同: {parameterName}

The path of a WebSocket endpoint can contain path parameters. The syntax is the same as for JAX-RS resources: {parameterName}.

您可以分别使用 io.quarkus.websockets.next.WebSocketConnection#pathParam(String) 方法或 io.quarkus.websockets.next.WebSocketClientConnection#pathParam(String) 访问路径参数值。或者,自动注入用 @io.quarkus.websockets.next.PathParam 注解的端点回调方法参数。

You can access the path parameter values using the io.quarkus.websockets.next.WebSocketConnection#pathParam(String) method, or io.quarkus.websockets.next.WebSocketClientConnection#pathParam(String) respectively. Alternatively, an endpoint callback method parameter annotated with @io.quarkus.websockets.next.PathParam is injected automatically.

WebSocketConnection#pathParam(String) example
@Inject io.quarkus.websockets.next.WebSocketConnection connection;
// ...
String value = connection.pathParam("parameterName");

路径参数值始终为字符串。如果路径中不存在路径参数,则 WebSocketConnection#pathParam(String)/WebSocketClientConnection#pathParam(String) 方法会返回 null。如果存在一个用 @PathParam 注解的端点回调方法参数,而参数名称在端点路径中未定义,则构建会失败。

Path parameter values are always strings. If the path parameter is not present in the path, the WebSocketConnection#pathParam(String)/WebSocketClientConnection#pathParam(String) method returns null. If there is an endpoint callback method parameter annotated with @PathParam and the parameter name is not defined in the endpoint path, then the build fails.

不支持查询参数。但是,您可以使用 WebSocketConnection#handshakeRequest().query() 访问查询

Query parameters are not supported. However, you can access the query using WebSocketConnection#handshakeRequest().query()

CDI scopes

端点被管理为 CDI Bean。默认情况下,使用了 @Singleton 范围。但是,开发者可以指定备用范围来满足其特定要求。

Endpoints are managed as CDI beans. By default, the @Singleton scope is used. However, developers can specify alternative scopes to fit their specific requirements.

@Singleton@ApplicationScoped 端点在所有 WebSocket 连接之间共享。因此,实现应该无状态或线程安全。

@Singleton and @ApplicationScoped endpoints are shared accross all WebSocket connections. Therefore, implementations should be either stateless or thread-safe.

import jakarta.enterprise.context.SessionScoped;

@WebSocket(path = "/ws")
@SessionScoped 1
public class MyWebSocket {

}
1 This server endpoint is not shared and is scoped to the session.

每个 WebSocket 连接都与其自己的 session 上下文相关联。当调用 @OnOpen 方法时,将创建与 WebSocket 连接对应的会话上下文。对 @On[Text|Binary]Message@OnClose 方法的后续调用将使用此相同的会话上下文。会话上下文保持活动状态,直到 @OnClose 方法完成执行,此时会话上下文会终止。

Each WebSocket connection is associated with its own session context. When the @OnOpen method is invoked, a session context corresponding to the WebSocket connection is created. Subsequent calls to @On[Text|Binary]Message or @OnClose methods utilize this same session context. The session context remains active until the @OnClose method completes execution, at which point it is terminated.

如果 WebSocket 端点未声明 @OnOpen 方法,则仍会创建会话上下文。无论是否具有 @OnClose 方法,会话上下文都将保持活动状态,直到连接终止。

In cases where a WebSocket endpoint does not declare an @OnOpen method, the session context is still created. It remains active until the connection terminates, regardless of the presence of an @OnClose method.

@OnTextMessage,@OnBinaryMessage,@OnOpen@OnClose 注释的方法在方法执行期间(直至生成结果)也将激活请求范围。

Methods annotated with @OnTextMessage, @OnBinaryMessage, @OnOpen, and @OnClose also have the request scope activated for the duration of the method execution (until it produced its result).

Callback methods

WebSocket 端点可以声明:

A WebSocket endpoint may declare:

  • At most one @OnTextMessage method: Handles the text messages from the connected client/server.

  • At most one @OnBinaryMessage method: Handles the binary messages from the connected client/server.

  • At most one @OnPongMessage method: Handles the pong messages from the connected client/server.

  • At most one @OnOpen method: Invoked when a connection is opened.

  • At most one @OnClose method: Executed when the connection is closed.

  • Any number of @OnError methods: Invoked when an error occurs; that is when an endpoint callback throws a runtime error, or when a conversion errors occurs, or when a returned io.smallrye.mutiny.Uni/io.smallrye.mutiny.Multi receives a failure.

并非所有端点都需要包含所有方法。但是,它必须至少包含 @On[Text|Binary]Message@OnOpen

Only some endpoints need to include all methods. However, it must contain at least @On[Text|Binary]Message or @OnOpen.

如果任何端点违反这些规则,则会在构建时抛出错误。表示子 Websocket 的静态嵌套类遵循相同的准则。

An error is thrown at build time if any endpoint violates these rules. The static nested classes representing sub-websockets adhere to the same guidelines.

在 WebSocket 端点外部使用 @OnTextMessage@OnBinaryMessage@OnOpen@OnClose 注释的任何方法都被视为错误,并且会导致构建失败,并显示适当的错误消息。

Any methods annotated with @OnTextMessage, @OnBinaryMessage, @OnOpen, and @OnClose outside a WebSocket endpoint are considered erroneous and will result in the build failing with an appropriate error message.

Processing messages

从客户端接收消息的方法使用 @OnTextMessage@OnBinaryMessage 注释。

Method receiving messages from the client are annotated with @OnTextMessage or @OnBinaryMessage.

对于从客户端接收的每个 text 消息,都会调用 OnTextMessage。对于客户端接收的每个 binary 消息,都会调用 OnBinaryMessage

OnTextMessage are invoked for every text message received from the client. OnBinaryMessage are invoked for every binary message the client receives.

Invocation rules

调用这些带注释的方法时,链接到 WebSocket 连接的 session 作用域保持活动状态。此外,请求作用域将持续处于活动状态,直至方法完成(或直至它为异步和反应式方法生成其结果)。

When invoking these annotated methods, the session scope linked to the WebSocket connection remains active. In addition, the request scope is active until the completion of the method (or until it produces its result for async and reactive methods).

与 Quarkus REST 类似,Quarkus WebSocket Next 支持 blockingnon-blocking 逻辑,这由方法签名和附加注释(如 @Blocking@NonBlocking)决定。

Quarkus WebSocket Next supports blocking and non-blocking logic, akin to Quarkus REST, determined by the method signature and additional annotations such as @Blocking and @NonBlocking.

以下规则控制执行:

Here are the rules governing execution:

  • Non-blocking methods must execute on the connection’s event loop.

  • Methods annotated with @RunOnVirtualThread are considered blocking and should execute on a virtual thread.

  • Blocking methods must execute on a worker thread if not annotated with @RunOnVirtualThread.

  • When @RunOnVirtualThread is employed, each invocation spawns a new virtual thread.

  • Methods returning CompletionStage, Uni and Multi are considered non-blocking.

  • Methods returning void or plain objects are considered blocking.

  • Kotlin suspend functions are considered non-blocking.

Method parameters

该方法必须精确接受一个消息参数:

The method must accept exactly one message parameter:

  • The message object (of any type).

  • A Multi<X> with X as the message type.

但是,它还可以接受以下参数:

However, it may also accept the following parameters:

  • WebSocketConnection/WebSocketClientConnection

  • HandshakeRequest

  • String parameters annotated with @PathParam

消息对象表示发送的数据,可以作为原始内容 (StringJsonObjectJsonArrayBufferbyte[]) 访问,也可以访问反序列化的高级对象,这是推荐的方法。

The message object represents the data sent and can be accessed as either raw content (String, JsonObject, JsonArray, Buffer or byte[]) or deserialized high-level objects, which is the recommended approach.

接收 Multi 时,会为每个连接调用一次该方法,并且所提供的 Multi 接收此连接传输的项。该方法必须订阅 Multi 以接收这些项(或返回一个 Multi)。

When receiving a Multi, the method is invoked once per connection, and the provided Multi receives the items transmitted by this connection. The method must subscribe to the Multi to receive these items (or return a Multi).

Supported return types

@OnTextMessage@OnBinaryMessage 注释的方法可以返回各种类型,以有效地处理 WebSocket 通信:

Methods annotated with @OnTextMessage or @OnBinaryMessage can return various types to handle WebSocket communication efficiently:

  • void: Indicates a blocking method where no explicit response is sent back to the client.

  • Uni<Void>: Denotes a non-blocking method where the completion of the returned Uni signifies the end of processing. No explicit response is sent back to the client.

  • An object of type X represents a blocking method in which the returned object is serialized and sent back to the client as a response.

  • Uni<X>: Specifies a non-blocking method where the item emitted by the non-null Uni is sent to the client as a response.

  • Multi<X>: Indicates a non-blocking method where the items emitted by the non-null Multi are sequentially sent to the client until completion or cancellation.

  • Kotlin suspend function returning Unit: Denotes a non-blocking method where no explicit response is sent back to the client.

  • Kotlin suspend function returning X: Specifies a non-blocking method where the returned item is sent to the client as a response.

以下是这些方法的一些示例:

Here are some examples of these methods:

@OnTextMessage
void consume(Message m) {
// Process the incoming message. The method is called on an executor thread for each incoming message.
}

@OnTextMessage
Uni<Void> consumeAsync(Message m) {
// Process the incoming message. The method is called on an event loop thread for each incoming message.
// The method completes when the returned Uni emits its item.
}

@OnTextMessage
ResponseMessage process(Message m) {
// Process the incoming message and send a response to the client.
// The method is called for each incoming message.
// Note that if the method returns `null`, no response will be sent to the client.
}

@OnTextMessage
Uni<ResponseMessage> processAsync(Message m) {
// Process the incoming message and send a response to the client.
// The method is called for each incoming message.
// Note that if the method returns `null`, no response will be sent to the client. The method completes when the returned Uni emits its item.
}

@OnTextMessage
Multi<ResponseMessage> stream(Message m) {
// Process the incoming message and send multiple responses to the client.
// The method is called for each incoming message.
// The method completes when the returned Multi emits its completion signal.
// The method cannot return `null` (but an empty multi if no response must be sent)
}

返回 Multi 时,Quarkus 会自动订阅返回的 Multi,并一直写入已发出的项,直到完成、失败或取消。失败或取消将终止连接。

When returning a Multi, Quarkus subscribes to the returned Multi automatically and writes the emitted items until completion, failure, or cancellation. Failure or cancellation terminates the connection.

Streams

除了各个消息之外,WebSocket 端点还可以处理消息流。在这种情况下,该方法接收一个 Multi<X> 作为参数。每个 X 实例都使用上面列出的相同规则进行反序列化。

In addition to individual messages, WebSocket endpoints can handle streams of messages. In this case, the method receives a Multi<X> as a parameter. Each instance of X is deserialized using the same rules listed above.

接收 Multi 的方法可以返回另一个 Multivoid。如果该方法返回 Multi,则不必订阅传入的 multi

The method receiving the Multi can either return another Multi or void. If the method returns a Multi, it does not have to subscribe to the incoming multi:

@OnTextMessage
public Multi<ChatMessage> stream(Multi<ChatMessage> incoming) {
    return incoming.log();
}

这种方法允许双向流。

This approach allows bi-directional streaming.

当方法返回 void 时,它必须订阅传入的 Multi

When the method returns void, it must subscribe to the incoming Multi:

@OnTextMessage
public void stream(Multi<ChatMessage> incoming) {
    incoming.subscribe().with(item -> log(item));
}

Skipping reply

当方法旨在生成一条写到客户端的消息时,它可以发出 null。发出 null 表示不对客户端发送任何消息,从而允许在需要时跳过响应。

When a method is intended to produce a message written to the client, it can emit null. Emitting null signifies no response to be sent to the client, allowing for skipping a response when needed.

JsonObject and JsonArray

Vert.x JsonObjectJsonArray 实例绕过序列化和反序列化机制。消息以文本消息的形式发送。

Vert.x JsonObject and JsonArray instances bypass the serialization and deserialization mechanisms. Messages are sent as text messages.

OnOpen and OnClose methods

还可以在客户端连接或断开连接时通知 WebSocket 端点。

The WebSocket endpoint can also be notified when a client connects or disconnects.

这是通过使用 @OnOpen@OnClose 来注释方法完成的:

This is done by annotating a method with @OnOpen or @OnClose:

@OnOpen(broadcast = true)
public ChatMessage onOpen() {
    return new ChatMessage(MessageType.USER_JOINED, connection.pathParam("username"), null);
}

@Inject WebSocketConnection connection;

@OnClose
public void onClose() {
    ChatMessage departure = new ChatMessage(MessageType.USER_LEFT, connection.pathParam("username"), null);
    connection.broadcast().sendTextAndAwait(departure);
}

在客户端连接时触发 @OnOpen,而在客户端断开连接时调用 @OnClose

@OnOpen is triggered upon client connection, while @OnClose is invoked upon disconnection.

这些方法可以访问 session-scoped WebSocketConnection bean。

These methods have access to the session-scoped WebSocketConnection bean.

Parameters

使用 @OnOpen@OnClose 注释的方法可以接受以下参数:

Methods annotated with @OnOpen and @OnClose may accept the following parameters:

  • WebSocketConnection/WebSocketClientConnection

  • HandshakeRequest

  • String parameters annotated with @PathParam

注释为 @OnClose 的端点方法也可能接受 io.quarkus.websockets.next.CloseReason 参数,此参数可能表示关闭连接的原因。

An endpoint method annotated with @OnClose may also accept the io.quarkus.websockets.next.CloseReason parameter that may indicate a reason for closing a connection.

Supported return types

@OnOpen@OnClose 方法支持不同的返回类型。

@OnOpen and @OnClose methods support different returned types.

对于 @OnOpen 方法,与 @On[Text|Binary]Message 相同的规则适用。因此,注释为 @OnOpen 的方法可以在连接后立即向客户端发送消息。@OnOpen 方法支持的返回类型包括:

For @OnOpen methods, the same rules as @On[Text|Binary]Message apply. Thus, a method annotated with @OnOpen can send messages to the client immediately after connecting. The supported return types for @OnOpen methods are:

  • void: Indicates a blocking method where no explicit message is sent back to the connected client.

  • Uni<Void>: Denotes a non-blocking method where the completion of the returned Uni signifies the end of processing. No message is sent back to the client.

  • An object of type X: Represents a blocking method where the returned object is serialized and sent back to the client.

  • Uni<X>: Specifies a non-blocking method where the item emitted by the non-null Uni is sent to the client.

  • Multi<X>: Indicates a non-blocking method where the items emitted by the non-null Multi are sequentially sent to the client until completion or cancellation.

  • Kotlin suspend function returning Unit: Denotes a non-blocking method where no explicit message is sent back to the client.

  • Kotlin suspend function returning X: Specifies a non-blocking method where the returned item is sent to the client.

发送到客户端的项目为 serialized ,但 Stringio.vertx.core.json.JsonObjectio.vertx.core.json.JsonArrayio.vertx.core.buffer.Bufferbyte[] 类型除外。对于 Multi,Quarkus 订阅返回的 Multi 并将其写入 WebSocket 中发送它们。StringJsonObjectJsonArray 作为文本消息发送。Buffers 和字节数组作为二进制消息发送。

Items sent to the client are serialization except for the String, io.vertx.core.json.JsonObject, io.vertx.core.json.JsonArray, io.vertx.core.buffer.Buffer, and byte[] types. In the case of Multi, Quarkus subscribes to the returned Multi and writes the items to the WebSocket as they are emitted. String, JsonObject and JsonArray are sent as text messages. Buffers and byte arrays are sent as binary messages.

对于 @OnClose 方法,支持的返回类型包括:

For @OnClose methods, the supported return types include:

  • void: The method is considered blocking.

  • Uni<Void>: The method is considered non-blocking.

  • Kotlin suspend function returning Unit: The method is considered non-blocking.

在服务器端点上声明的 @OnClose 方法不能通过返回对象将项目发送给已连接的客户端。它们只能通过使用 WebSocketConnection 对象将消息发送给其他客户端。

@OnClose methods declared on a server endpoint cannot send items to the connected client by returning objects. They can only send messages to the other clients by using the WebSocketConnection object.

Error handling

当发生错误时,也可以通知 WebSocket 端点。当端点回调抛出运行时错误,或发生转换错误,或返回的 io.smallrye.mutiny.Uni/io.smallrye.mutiny.Multi 接收失败时,调用注释为 @io.quarkus.websockets.next.OnError 的 WebSocket 端点方法。

WebSocket endpoints can also be notified when an error occurs. A WebSocket endpoint method annotated with @io.quarkus.websockets.next.OnError is invoked when an endpoint callback throws a runtime error, or when a conversion errors occurs, or when a returned io.smallrye.mutiny.Uni/io.smallrye.mutiny.Multi receives a failure.

该方法必须接受正好一个 error 参数,即从 java.lang.Throwable 分配的参数。该方法还可以接受以下参数:

The method must accept exactly one error parameter, i.e. a parameter that is assignable from java.lang.Throwable. The method may also accept the following parameters:

  • WebSocketConnection/WebSocketClientConnection

  • HandshakeRequest

  • String parameters annotated with @PathParam

一个端点可以声明多个注释为 @io.quarkus.websockets.next.OnError 的方法。但是,每个方法必须声明一个不同的错误参数。选择声明实际异常的最具体超类型的方法。

An endpoint may declare multiple methods annotated with @io.quarkus.websockets.next.OnError. However, each method must declare a different error parameter. The method that declares a most-specific supertype of the actual exception is selected.

@io.quarkus.websockets.next.OnError 注释还可以用于声明全局错误处理程序,即未在 WebSocket 端点上声明的方法。此类方法可能不接受 @PathParam 参数。在端点上声明的错误处理程序优先于全局错误处理程序。

The @io.quarkus.websockets.next.OnError annotation can be also used to declare a global error handler, i.e. a method that is not declared on a WebSocket endpoint. Such a method may not accept @PathParam paremeters. Error handlers declared on an endpoint take precedence over the global error handlers.

当错误发生且无错误处理程序可以处理失败时,Quarkus 将采用由 `quarkus.websockets-next.server.unhandled-failure-strategy`指定的方法。默认情况下,连接已关闭。另外,可以记录一条错误消息或不执行任何操作。

When an error occurs but no error handler can handle the failure, Quarkus uses the strategy specified by quarkus.websockets-next.server.unhandled-failure-strategy. By default, the connection is closed. Alternatively, an error message can be logged or no operation performed.

Serialization and deserialization

WebSocket Next 扩展支持消息的自动序列化和反序列化。

The WebSocket Next extension supports automatic serialization and deserialization of messages.

类型为 StringJsonObjectJsonArrayBuffer、和 `byte[]`的对象会被按原样发送,并绕过序列化和反序列化。当未提供任何编码器时,序列化和反序列化会自动将消息从 JSON 转换过来,或者将消息转换为 JSON。

Objects of type String, JsonObject, JsonArray, Buffer, and byte[] are sent as-is and bypass the serialization and deserialization. When no codec is provided, the serialization and deserialization convert the message from/to JSON automatically.

当需要自定义序列化和反序列化时,你可以提供一个自定义编码器。

When you need to customize the serialization and deserialization, you can provide a custom codec.

Custom codec

要实现一个自定义编码器,你必须提供一个实现 CDI Bean 的:

To implement a custom codec, you must provide a CDI bean implementing:

  • io.quarkus.websockets.next.BinaryMessageCodec for binary messages

  • io.quarkus.websockets.next.TextMessageCodec for text messages

以下示例展示了如何为 `Item`类实现一个自定义编码器:

The following example shows how to implement a custom codec for a Item class:

@Singleton
public class ItemBinaryMessageCodec implements BinaryMessageCodec<Item> {

    @Override
    public boolean supports(Type type) {
        // Allows selecting the right codec for the right type
        return type.equals(Item.class);
    }

    @Override
    public Buffer encode(Item value) {
        // Serialization
        return Buffer.buffer(value.toString());
    }

    @Override
    public Item decode(Type type, Buffer value) {
        // Deserialization
        return new Item(value.toString());
    }
}

`OnTextMessage`和 `OnBinaryMessage`方法还能明确指定应该使用哪个编码器:

OnTextMessage and OnBinaryMessage methods can also specify which codec should be used explicitly:

@OnTextMessage(codec = MyInputCodec.class) (1)
Item find(Item item) {
        //....
}
  1. Specify the codec to use for both the deserialization and serialization of the message

当序列化和反序列化必须使用不同编码器时,可以分别为序列化和反序列化指定要使用的编码器:

When the serialization and deserialization must use a different codec, you can specify the codec to use for the serialization and deserialization separately:

@OnTextMessage(
        codec = MyInputCodec.class, (1)
        outputCodec = MyOutputCodec.class (2)
Item find(Item item) {
        //....
}
  1. Specify the codec to use for the deserialization of the incoming message

  2. Specify the codec to use for the serialization of the outgoing message

Ping/pong messages

ping message可以用作保持连接或验证远程终端。 pong message作为 ping 消息的响应发送,它必须具有相同的有效负载。

A ping message may serve as a keepalive or to verify the remote endpoint. A pong message is sent in response to a ping message and it must have an identical payload.

服务器/客户端终端自动响应从客户端/服务器发送的 ping 消息。换句话说,在终端上声明 `@OnPingMessage`回调是没有必要的。

Server/client endpoints automatically respond to a ping message sent from the client/server. In other words, there is no need for @OnPingMessage callback declared on an endpoint.

服务器可以向已连接的客户端发送 ping 消息。WebSocketConnection/WebSocketClientConnection`声明用于发送 ping 消息的方法;有一个非阻塞变量 `sendPing(Buffer)`和一个阻塞变量 `sendPingAndAwait(Buffer)。默认情况下,ping 消息不会自动发送。然而,配置属性 `quarkus.websockets-next.server.auto-ping-interval`和 `quarkus.websockets-next.client.auto-ping-interval`可以用于设置时间间隔,服务器/客户端会在此时间间隔后向已连接的客户端/服务器发送 ping 消息。

The server can send ping messages to a connected client. WebSocketConnection/WebSocketClientConnection declare methods to send ping messages; there is a non-blocking variant: sendPing(Buffer) and a blocking variant: sendPingAndAwait(Buffer). By default, the ping messages are not sent automatically. However, the configuration properties quarkus.websockets-next.server.auto-ping-interval and quarkus.websockets-next.client.auto-ping-interval can be used to set the interval after which, the server/client sends a ping message to a connected client/server automatically.

quarkus.websockets-next.server.auto-ping-interval=2 1
1 Sends a ping message from the server to a connected client every 2 seconds.

@OnPongMessage`注释用于定义一个回调,它使用从客户端/服务器发送的 pong 消息。一个终端必须声明最多一个用 `@OnPongMessage`注释的方法。回调方法必须返回 `void`或 `Uni<Void>(或返回 `Unit`的 Kotlin `suspend`函数),并且它必须接受一个类型为 `Buffer`的单个参数。

The @OnPongMessage annotation is used to define a callback that consumes pong messages sent from the client/server. An endpoint must declare at most one method annotated with @OnPongMessage. The callback method must return either void or Uni<Void> (or be a Kotlin suspend function returning Unit), and it must accept a single parameter of type Buffer.

@OnPongMessage
void pong(Buffer data) {
    // ....
}

服务器/客户端还可以发送非请求的 pong 消息,这些消息可以用作单向心跳。有一个非阻塞变量: WebSocketConnection#sendPong(Buffer),还有一个阻塞变量: WebSocketConnection#sendPongAndAwait(Buffer)

The server/client can also send unsolicited pong messages that may serve as a unidirectional heartbeat. There is a non-blocking variant: WebSocketConnection#sendPong(Buffer) and also a blocking variant: WebSocketConnection#sendPongAndAwait(Buffer).

Inbound processing mode

WebSocket 终端可以使用 @WebSocket#inboundProcessingMode()`和 `@WebSocketClient.inboundProcessingMode()`分别定义用于处理特定连接的接收事件所用的模式。接收事件可以代表一条消息(文本、二进制、pong)、打开连接和关闭连接。默认情况下,事件按串行处理,并且会确保排序。这意味着,如果一个终端接收事件 `A`和 `B(按这个特定顺序),那么事件 B`的回调将在事件 `A`的回调完成以后被调用。然而,在某些情况下,最好并发处理事件,即不保证排序,但也无并发限制。对于这种情况,应该使用 `InboundProcessingMode#CONCURRENT

WebSocket endpoints can define the mode used to process incoming events for a specific connection using the @WebSocket#inboundProcessingMode(), and @WebSocketClient.inboundProcessingMode() respectively. An incoming event can represent a message (text, binary, pong), opening connection and closing connection. By default, events are processed serially and ordering is guaranteed. This means that if an endpoint receives events A and B (in this particular order) then callback for event B will be invoked after the callback for event A completed. However, in some situations it is preferable to process events concurrently, i.e. with no ordering guarantees but also with no concurrency limits. For this cases, the InboundProcessingMode#CONCURRENT should be used.

Server API

HTTP server configuration

此扩展程序会重新使用 _main_HTTP 服务器。

This extension reuses the main HTTP server.

因此,WebSocket 服务器的配置是在 quarkus.http. 配置部分完成的。

Thus, the configuration of the WebSocket server is done in the quarkus.http. configuration section.

应用程序内配置的 WebSocket 路径与由 quarkus.http.root 定义的根路径(默认值为 /)连接在一起。此连接确保 WebSocket 端点正确地放置在应用程序的 URL 结构中。

WebSocket paths configured within the application are concatenated with the root path defined by quarkus.http.root (which defaults to /). This concatenation ensures that WebSocket endpoints are appropriately positioned within the application’s URL structure.

有关更多详细信息,请参阅 HTTP guide

Refer to the HTTP guide for more details.

Sub-websockets endpoints

@WebSocket 端点可封装静态嵌套类,这些类还用 @WebSocket 注释,并表示 sub-websockets。这些子 WebSocket 的最终路径会连接外层类和嵌套类的路径。最终路径是根据 HTTP URL 规则进行标准化的。

A @WebSocket endpoint can encapsulate static nested classes, which are also annotated with @WebSocket and represent sub-websockets. The resulting path of these sub-websockets concatenates the path from the enclosing class and the nested class. The resulting path is normalized, following the HTTP URL rules.

子 WebSocket 继承对 @WebSocket 注释中声明的路径参数的访问权限,该注释用于外层类和嵌套类。在以下示例中,外层类内的 consumePrimary 方法可访问 version 参数。同时,嵌套类内的 consumeNested 方法可访问 versionid 参数:

Sub-websockets inherit access to the path parameters declared in the @WebSocket annotation of both the enclosing and nested classes. The consumePrimary method within the enclosing class can access the version parameter in the following example. Meanwhile, the consumeNested method within the nested class can access both version and id parameters:

@WebSocket(path = "/ws/v{version}")
public class MyPrimaryWebSocket {

    @OnTextMessage
    void consumePrimary(String s)    { ... }

    @WebSocket(path = "/products/{id}")
    public static class MyNestedWebSocket {

      @OnTextMessage
      void consumeNested(String s)    { ... }

    }
}

WebSocket connection

io.quarkus.websockets.next.WebSocketConnection 对象表示 WebSocket 连接。Quarkus 提供了一个 @SessionScoped CDI Bean,该 Bean 实施此接口,可以在 WebSocket 端点中注入,并用于与已连接的客户端进行交互。

The io.quarkus.websockets.next.WebSocketConnection object represents the WebSocket connection. Quarkus provides a @SessionScoped CDI bean that implements this interface and can be injected in a WebSocket endpoint and used to interact with the connected client.

使用 @OnOpen@OnTextMessage@OnBinaryMessage@OnClose 进行注释的方法可以访问注入的 WebSocketConnection 对象:

Methods annotated with @OnOpen, @OnTextMessage, @OnBinaryMessage, and @OnClose can access the injected WebSocketConnection object:

@Inject WebSocketConnection connection;

请注意,在这些方法之外,WebSocketConnection 对象不可用。但是,可以 list all open connections

Note that outside of these methods, the WebSocketConnection object is not available. However, it is possible to list-open-connections.

该连接可用于向客户端发送消息、访问路径参数、向所有已连接的客户端广播消息,等等。

The connection can be used to send messages to the client, access the path parameters, broadcast messages to all connected clients, etc.

// Send a message:
connection.sendTextAndAwait("Hello!");

// Broadcast messages:
connection.broadcast().sendTextAndAwait(departure);

// Access path parameters:
String param = connection.pathParam("foo");

WebSocketConnection 提供发送消息的阻塞和非阻塞方法变体:

The WebSocketConnection provides both a blocking and a non-blocking method variants to send messages:

  • sendTextAndAwait(String message): Sends a text message to the client and waits for the message to be sent. It’s blocking and should only be called from an executor thread.

  • sendText(String message): Sends a text message to the client. It returns a Uni. It’s non-blocking, but you must subscribe to it.

List open connections

还可以列出所有打开的连接。Quarkus 提供了一个类型为 io.quarkus.websockets.next.OpenConnections 的 CDI Bean,该 Bean 声明了方便访问连接的方法。

It is also possible to list all open connections. Quarkus provides a CDI bean of type io.quarkus.websockets.next.OpenConnections that declares convenient methods to access the connections.

import io.quarkus.logging.Log;
import io.quarkus.websockets.next.OpenConnections;

class MyBean {

  @Inject
  OpenConnections connections;

  void logAllOpenConnections() {
     Log.infof("Open connections: %s", connections.listAll()); 1
  }
}
1 OpenConnections#listAll() returns an immutable snapshot of all open connections at the given time.

还有其他一些方便的方法。例如,OpenConnections#findByEndpointId(String) 可以让您轻松地找到某个特定端点的连接。

There are also other convenient methods. For example, OpenConnections#findByEndpointId(String) makes it easy to find connections for a specific endpoint.

CDI events

当打开一个新连接时,Quarkus 会使用限定符 @io.quarkus.websockets.next.Open 异步触发类型为 io.quarkus.websockets.next.WebSocketConnection 的 CDI 事件。此外,当一个连接关闭时,会使用限定符 @io.quarkus.websockets.next.Closed 异步触发类型为 WebSocketConnection 的 CDI 事件。

Quarkus fires a CDI event of type io.quarkus.websockets.next.WebSocketConnection with qualifier @io.quarkus.websockets.next.Open asynchronously when a new connection is opened. Moreover, a CDI event of type WebSocketConnection with qualifier @io.quarkus.websockets.next.Closed is fired asynchronously when a connection is closed.

import jakarta.enterprise.event.ObservesAsync;
import io.quarkus.websockets.next.Open;
import io.quarkus.websockets.next.WebSocketConnection;

class MyBean {

  void connectionOpened(@ObservesAsync @Open WebSocketConnection connection) { 1
     // This observer method is called when a connection is opened...
  }
}
1 An asynchronous observer method is executed using the default blocking executor service.

Security

可以使用安全注释来保护 WebSocket 端点回调方法,例如 io.quarkus.security.Authenticatedjakarta.annotation.security.RolesAllowed 以及 Supported security annotations 文档中列出的其他注释。

WebSocket endpoint callback methods can be secured with security annotations such as io.quarkus.security.Authenticated, jakarta.annotation.security.RolesAllowed and other annotations listed in the Supported security annotations documentation.

例如:

For example:

package io.quarkus.websockets.next.test.security;

import jakarta.annotation.security.RolesAllowed;
import jakarta.inject.Inject;

import io.quarkus.security.ForbiddenException;
import io.quarkus.security.identity.SecurityIdentity;
import io.quarkus.websockets.next.OnError;
import io.quarkus.websockets.next.OnOpen;
import io.quarkus.websockets.next.OnTextMessage;
import io.quarkus.websockets.next.WebSocket;

@WebSocket(path = "/end")
public class Endpoint {

    @Inject
    SecurityIdentity currentIdentity;

    @OnOpen
    String open() {
        return "ready";
    }

    @RolesAllowed("admin")
    @OnTextMessage
    String echo(String message) { 1
        return message;
    }

    @OnError
    String error(ForbiddenException t) { 2
        return "forbidden:" + currentIdentity.getPrincipal().getName();
    }
}
1 The echo callback method can only be invoked if the current security identity has an admin role.
2 The error handler is invoked in case of the authorization failure.

`SecurityIdentity`最初是在安全 HTTP 升级期间创建的,并与 websocket 连接关联。

SecurityIdentity is initially created during a secure HTTP upgrade and associated with the websocket connection.

当使用 OpenId Connect 扩展且令牌过期时,Quarkus 将自动关闭连接。

When OpenID Connect extension is used and token expires, Quarkus automatically closes connection.

Secure HTTP upgrade

当标准安全注释放在端点类中或定义了 HTTP 安全策略时,HTTP 升级将被保护。保护 HTTP 升级的优点是处理更少,授权可以及早执行,并且只执行一次。您应始终更喜欢 HTTP 升级安全性,除非像上面的示例中一样,您需要对错误执行操作。

An HTTP upgrade is secured when standard security annotation is placed on an endpoint class or an HTTP Security policy is defined. The advantage of securing HTTP upgrade is less processing, the authorization is performed early and only once. You should always prefer HTTP upgrade security unless, like in th example above, you need to perform action on error.

Use standard security annotation to secure an HTTP upgrade
package io.quarkus.websockets.next.test.security;

import io.quarkus.security.Authenticated;
import jakarta.inject.Inject;

import io.quarkus.security.identity.SecurityIdentity;
import io.quarkus.websockets.next.OnOpen;
import io.quarkus.websockets.next.OnTextMessage;
import io.quarkus.websockets.next.WebSocket;

@Authenticated 1
@WebSocket(path = "/end")
public class Endpoint {

    @Inject
    SecurityIdentity currentIdentity;

    @OnOpen
    String open() {
        return "ready";
    }

    @OnTextMessage
    String echo(String message) {
        return message;
    }
}
1 Initial HTTP handshake ends with the 401 status for anonymous users. You can also redirect the handshake request on authorization failure with the quarkus.websockets-next.server.security.auth-failure-redirect-url configuration property.

仅当在 `@WebSocket`注释旁边的端点类上声明安全注释时,HTTP 升级才受到保护。在端点 bean 上放置一个安全注释不会保护 bean 方法,只会保护 HTTP 升级。您必须始终验证端点是否按预期受保护。

HTTP upgrade is only secured when a security annotation is declared on an endpoint class next to the @WebSocket annotation. Placing a security annotation on an endpoint bean will not secure bean methods, only the HTTP upgrade. You must always verify that your endpoint is secured as intended.

Use HTTP Security policy to secure an HTTP upgrade
quarkus.http.auth.permission.http-upgrade.paths=/end
quarkus.http.auth.permission.http-upgrade.policy=authenticated

Inspect and/or reject HTTP upgrade

要检查 HTTP 升级,您必须提供实现 `io.quarkus.websockets.next.HttpUpgradeCheck`接口的 CDI bean。Quarkus 在应该升级为 WebSocket 连接的每个 HTTP 请求上调用 `HttpUpgradeCheck#perform`方法。在此方法中,您可以执行任何业务逻辑和/或拒绝 HTTP 升级。

To inspect an HTTP upgrade, you must provide a CDI bean implementing the io.quarkus.websockets.next.HttpUpgradeCheck interface. Quarkus calls the HttpUpgradeCheck#perform method on every HTTP request that should be upgraded to a WebSocket connection. Inside this method, you can perform any business logic and/or reject the HTTP upgrade.

Example HttpUpgradeCheck
package io.quarkus.websockets.next.test;

import io.quarkus.websockets.next.HttpUpgradeCheck;
import io.smallrye.mutiny.Uni;
import jakarta.enterprise.context.ApplicationScoped;

@ApplicationScoped 1
public class ExampleHttpUpgradeCheck implements HttpUpgradeCheck {

    @Override
    public Uni<CheckResult> perform(HttpUpgradeContext ctx) {
        if (rejectUpgrade(ctx)) {
            return CheckResult.rejectUpgrade(400); 2
        }
        return CheckResult.permitUpgrade();
    }

    private boolean rejectUpgrade(HttpUpgradeContext ctx) {
        var headers = ctx.httpRequest().headers();
        // implement your business logic in here
    }
}
1 The CDI beans implementing HttpUpgradeCheck interface can be either @ApplicationScoped, @Singleton or @Dependent beans, but never the @RequestScoped beans.
2 Reject the HTTP upgrade. Initial HTTP handshake ends with the 400 Bad Request response status code.

您可以使用 `HttpUpgradeCheck#appliesTo`方法为应用 `HttpUpgradeCheck`的 WebSocket 端点进行选择。

You can choose WebSocket endpoints to which the HttpUpgradeCheck is applied with the HttpUpgradeCheck#appliesTo method.

TLS

由于此扩展重复使用 _main_HTTP 服务器,因此所有相关的服务器配置都适用。有关更多详细信息,请参阅 HTTP guide

As a direct consequence of the fact this extension reuses the main HTTP server, all the relevant server configurations apply. See Refer to the HTTP guide for more details.

Client API

Client connectors

`io.quarkus.websockets.next.WebSocketConnector<CLIENT>`用于为客户端端点配置和创建新连接。提供了一个实现此接口的 CDI bean,并可以将其注入其他 bean 中。实际类型参数用于确定客户端端点。该类型在构建期间得到验证 - 如果它不表示客户端端点,则构建失败。

The io.quarkus.websockets.next.WebSocketConnector<CLIENT> is used to configure and create new connections for client endpoints. A CDI bean that implements this interface is provided and can be injected in other beans. The actual type argument is used to determine the client endpoint. The type is validated during build - if it does not represent a client endpoint the build fails.

让我们考虑以下客户端端点:

Let’s consider the following client endpoint:

Client endpoint
@WebSocketClient(path = "/endpoint/{name}")
public class ClientEndpoint {

    @OnTextMessage
    void onMessage(@PathParam String name, String message, WebSocketClientConnection connection) {
        // ...
    }
}

此客户端端点的连接器使用如下:

The connector for this client endpoint is used as follows:

Connector
@Singleton
public class MyBean {

    @ConfigProperty(name = "enpoint.uri")
    URI myUri;

    @Inject
    WebSocketConnector<ClientEndpoint> connector; 1

    void openAndSendMessage() {
        WebSocketClientConnection connection = connector
            .baseUri(uri) 2
            .pathParam("name", "Roxanne") 3
            .connectAndAwait();
        connection.sendTextAndAwait("Hi!"); 4
    }
}
1 Inject the connector for ClientEndpoint.
2 If the base URI is not supplied we attempt to obtain the value from the config. The key consists of the client id and the .base-uri suffix.
3 Set the path param value. Throws IllegalArgumentException if the client endpoint path does not contain a parameter with the given name.
4 Use the connection to send messages, if needed.

如果应用程序尝试为丢失的端点注入连接器,则会抛出错误。

If an application attempts to inject a connector for a missing endpoint, an error is thrown.

Basic connector

如果应用程序开发者不需要客户端端点和连接器的组合,则可以使用 basic connector 。基本连接器是一种创建连接以及使用/发送消息的简单方法,无需定义客户端端点。

In the case where the application developer does not need the combination of the client endpoint and the connector, a basic connector can be used. The basic connector is a simple way to create a connection and consume/send messages without defining a client endpoint.

Basic connector
@Singleton
public class MyBean {

    @Inject
    BasicWebSocketConnector connector; 1

    void openAndConsume() {
        WebSocketClientConnection connection = connector
            .baseUri(uri) 2
            .path("/ws") 3
            .executionModel(ExecutionModel.NON_BLOCKING) 4
            .onTextMessage((c, m) -> { 5
               // ...
            })
            .connectAndAwait();
    }
}
1 Inject the connector.
2 The base URI must be always set.
3 The additional path that should be appended to the base URI.
4 Set the execution model for callback handlers. By default, the callback may block the current thread. However in this case, the callback is executed on the event loop and may not block the current thread.
5 The lambda will be called for every text message sent from the server.

基本连接器更接近底层 API,仅供高级用户使用。然而,与其他底层 WebSocket 客户端不同,它仍然是一个 CDI bean,可以在其他 bean 中注入。它还提供了一种配置回调执行模型的方法,确保与其他 Quarkus 组件的最佳集成。

The basic connector is closer to a low-level API and is reserved for advanced users. However, unlike others low-level WebSocket clients, it is still a CDI bean and can be injected in other beans. It also provides a way to configure the execution model of the callbacks, ensuring optimal integration with the rest of Quarkus.

WebSocket client connection

io.quarkus.websockets.next.WebSocketClientConnection 对象表示 WebSocket 连接。Quarkus 提供了一个实现此接口的 @SessionScoped CDI bean,该 bean 可以在 WebSocketClient 端点中注入并用于与已连接的服务器进行交互。

The io.quarkus.websockets.next.WebSocketClientConnection object represents the WebSocket connection. Quarkus provides a @SessionScoped CDI bean that implements this interface and can be injected in a WebSocketClient endpoint and used to interact with the connected server.

注释有 @OnOpen@OnTextMessage@OnBinaryMessage`和 `@OnClose 的方法可以访问注入的 WebSocketClientConnection 对象:

Methods annotated with @OnOpen, @OnTextMessage, @OnBinaryMessage, and @OnClose can access the injected WebSocketClientConnection object:

@Inject WebSocketClientConnection connection;

请注意,在这些方法之外,WebSocketClientConnection 对象是不可用的。然而,可以 list all open client connections

Note that outside of these methods, the WebSocketClientConnection object is not available. However, it is possible to list-open-client-connections.

该连接可用于向客户端发送消息、访问路径参数等。

The connection can be used to send messages to the client, access the path parameters, etc.

// Send a message:
connection.sendTextAndAwait("Hello!");

// Broadcast messages:
connection.broadcast().sendTextAndAwait(departure);

// Access path parameters:
String param = connection.pathParam("foo");

WebSocketClientConnection 提供了阻塞和非阻塞方法变体来发送消息:

The WebSocketClientConnection provides both a blocking and a non-blocking method variants to send messages:

  • sendTextAndAwait(String message): Sends a text message to the client and waits for the message to be sent. It’s blocking and should only be called from an executor thread.

  • sendText(String message): Sends a text message to the client. It returns a Uni. It’s non-blocking, but you must subscribe to it.

List open client connections

还可以列出所有打开的连接。Quarkus 提供了一个 io.quarkus.websockets.next.OpenClientConnections 类型的 CDI bean,该 bean 声明了访问连接的便捷方法。

It is also possible to list all open connections. Quarkus provides a CDI bean of type io.quarkus.websockets.next.OpenClientConnections that declares convenient methods to access the connections.

import io.quarkus.logging.Log;
import io.quarkus.websockets.next.OpenClientConnections;

class MyBean {

  @Inject
  OpenClientConnections connections;

  void logAllOpenClinetConnections() {
     Log.infof("Open client connections: %s", connections.listAll()); 1
  }
}
1 OpenClientConnections#listAll() returns an immutable snapshot of all open connections at the given time.

还有其他便捷方法。例如,OpenClientConnections#findByClientId(String) 可以轻松找到特定端点的连接。

There are also other convenient methods. For example, OpenClientConnections#findByClientId(String) makes it easy to find connections for a specific endpoint.

CDI events

Quarkus 在打开新连接时以限定符 @io.quarkus.websockets.next.Open 异步触发类型为 io.quarkus.websockets.next.WebSocketClientConnection 的 CDI 事件。此外,在连接关闭时以限定符 @io.quarkus.websockets.next.Closed 异步触发类型为 WebSocketClientConnection 的 CDI 事件。

Quarkus fires a CDI event of type io.quarkus.websockets.next.WebSocketClientConnection with qualifier @io.quarkus.websockets.next.Open asynchronously when a new connection is opened. Moreover, a CDI event of type WebSocketClientConnection with qualifier @io.quarkus.websockets.next.Closed is fired asynchronously when a connection is closed.

import jakarta.enterprise.event.ObservesAsync;
import io.quarkus.websockets.next.Open;
import io.quarkus.websockets.next.WebSocketClientConnection;

class MyBean {

  void connectionOpened(@ObservesAsync @Open WebSocketClientConnection connection) { 1
     // This observer method is called when a connection is opened...
  }
}
1 An asynchronous observer method is executed using the default blocking executor service.

Configuring SSL/TLS

要建立一个 TLS 连接,您需要使用 TLS registry 配置一个 named 配置:

To establish a TLS connection, you need to configure a named configuration using the TLS registry:

quarkus.tls.my-ws-client.trust-store.p12.path=server-truststore.p12
quarkus.tls.my-ws-client.trust-store.p12.password=secret

quarkus.websockets-next.client.tls-configuration-name=my-ws-client # Reference the named configuration

使用 WebSocket 客户端时,需使用 named 配置来避免与其他 TLS 配置发生冲突。客户端将不会使用默认的 TLS 配置。

When using the WebSocket client, using a named configuration is required to avoid conflicts with other TLS configurations. The client will not use the default TLS configuration.

当您配置一个 named TLS 配置时,默认情况下会启用 TLS。

When you configure a named TLS configuration, TLS is enabled by default.

Traffic logging

Quarkus 可以记录收发消息以进行调试。要启用服务器的流量日志记录,将 quarkus.websockets-next.server.traffic-logging.enabled 配置属性设置为 true。要启用客户端的流量日志记录,将 quarkus.websockets-next.client.traffic-logging.enabled 配置属性设置为 true。文本消息的载荷也会被记录。然而,记录字符的数量是有限的。默认限制为 100,但是您可以分别使用 quarkus.websockets-next.server.traffic-logging.text-payload-limitquarkus.websockets-next.client.traffic-logging.text-payload-limit 配置属性更改此限制。

Quarkus can log the messages sent and received for debugging purposes. To enable traffic logging for the server, set the quarkus.websockets-next.server.traffic-logging.enabled configuration property to true. To enable traffic logging for the client, set the quarkus.websockets-next.client.traffic-logging.enabled configuration property to true. The payload of text messages is logged as well. However, the number of logged characters is limited. The default limit is 100, but you can change this limit with the quarkus.websockets-next.server.traffic-logging.text-payload-limit and quarkus.websockets-next.client.traffic-logging.text-payload-limit configuration property, respectively.

如果已为记录器 io.quarkus.websockets.next.traffic`启用了 `DEBUG 级别,才会记录消息。

The messages are only logged if the DEBUG level is enabled for the logger io.quarkus.websockets.next.traffic.

Example server configuration
quarkus.websockets-next.server.traffic-logging.enabled=true 1
quarkus.websockets-next.server.traffic-logging.text-payload-limit=50 2

quarkus.log.category."io.quarkus.websockets.next.traffic".level=DEBUG 3
1 Enables traffic logging.
2 Set the number of characters of a text message payload which will be logged.
3 Enable DEBUG level is for the logger io.quarkus.websockets.next.traffic.

Configuration reference

Unresolved directive in websockets-next-reference.adoc - include::{generated-dir}/config/quarkus-websockets-next.adoc[]