Using WebSockets
本指南解释了如何让你的 Quarkus 应用程序利用 WebSockets 创建交互式 Web 应用程序。因为它是一个 canonical WebSocket 应用程序,所以我们要创建一个简单的聊天应用程序。
Prerequisites
如要完成本指南,您需要:
-
Roughly 15 minutes
-
An IDE
-
安装了 JDK 17+,已正确配置
JAVA_HOME
-
Apache Maven ${proposed-maven-version}
-
如果你想使用 Quarkus CLI, 则可以选择使用
-
如果你想构建一个本机可执行文件(或如果你使用本机容器构建,则使用 Docker),则可以选择安装 Mandrel 或 GraalVM 以及 configured appropriately
Solution
我们建议你按照后续章节中的说明,逐步创建应用程序。但是,你可以直接跳到已完成的示例。
克隆 Git 存储库: git clone $${quickstarts-base-url}.git
,或下载 $${quickstarts-base-url}/archive/main.zip[存档]。
解决方案位于 websockets-quickstart
directory。
Creating the Maven project
首先,我们需要一个新项目。使用以下命令创建一个新项目:
quarkus create app {create-app-group-id}:{create-app-artifact-id} \
--no-code
cd {create-app-artifact-id}
要创建一个 Gradle 项目,添加 --gradle
或 --gradle-kotlin-dsl
选项。
有关如何安装和使用 Quarkus CLI 的详细信息,请参见 Quarkus CLI 指南。
mvn {quarkus-platform-groupid}:quarkus-maven-plugin:{quarkus-version}:create \
-DprojectGroupId={create-app-group-id} \
-DprojectArtifactId={create-app-artifact-id} \
-DnoCode
cd {create-app-artifact-id}
要创建一个 Gradle 项目,添加 -DbuildTool=gradle
或 -DbuildTool=gradle-kotlin-dsl
选项。
适用于 Windows 用户:
-
如果使用 cmd,(不要使用反斜杠
\
,并将所有内容放在同一行上) -
如果使用 Powershell,将
-D
参数用双引号引起来,例如"-DprojectArtifactId={create-app-artifact-id}"
此命令生成项目(没有任何类),并导入 websockets
扩展。
如果你已配置你的 Quarkus 项目,则可以通过在项目基本目录中运行以下命令向你的项目添加 websockets
扩展:
quarkus extension add {add-extension-extensions}
./mvnw quarkus:add-extension -Dextensions='{add-extension-extensions}'
./gradlew addExtension --extensions='{add-extension-extensions}'
这会将以下内容添加到构建文件中:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-websockets</artifactId>
</dependency>
implementation("io.quarkus:quarkus-websockets")
如果你只想使用 WebSocket 客户端,你应该用 |
Handling web sockets
我们的应用程序含有一个处理 WebSocket 的类。在 src/main/java
目录中创建 org.acme.websockets.ChatSocket
类。复制以下内容到创建的文件中:
package org.acme.websockets;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.websocket.OnClose;
import jakarta.websocket.OnError;
import jakarta.websocket.OnMessage;
import jakarta.websocket.OnOpen;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;
import jakarta.websocket.Session;
@ServerEndpoint("/chat/{username}") (1)
@ApplicationScoped
public class ChatSocket {
Map<String, Session> sessions = new ConcurrentHashMap<>(); (2)
@OnOpen
public void onOpen(Session session, @PathParam("username") String username) {
broadcast("User " + username + " joined");
sessions.put(username, session);
}
@OnClose
public void onClose(Session session, @PathParam("username") String username) {
sessions.remove(username);
broadcast("User " + username + " left");
}
@OnError
public void onError(Session session, @PathParam("username") String username, Throwable throwable) {
sessions.remove(username);
broadcast("User " + username + " left on error: " + throwable);
}
@OnMessage
public void onMessage(String message, @PathParam("username") String username) {
broadcast(">> " + username + ": " + message);
}
private void broadcast(String message) {
sessions.values().forEach(s -> {
s.getAsyncRemote().sendObject(message, result -> {
if (result.getException() != null) {
System.out.println("Unable to send message: " + result.getException());
}
});
});
}
}
1 | 配置 WebSocket URL |
2 | 存储当前打开的 WebSocket |
A slick web frontend
所有聊天应用程序都需要 nice UI,此示例可能不太美观,但能完成工作。Quarkus 自动为 META-INF/resources
目录中包含的静态资源提供服务。创建 src/main/resources/META-INF/resources
目录并将此 index.html 文件复制到其中。
Run the application
现在,让我们看看应用程序在实际应用中的情况。使用以下命令运行它:
quarkus dev
./mvnw quarkus:dev
./gradlew --console=plain quarkusDev
然后在 2 个浏览器窗口中打开 [role="bare"][role="bare"]http://localhost:8080/:
-
在顶部文本区域中输入一个名称(使用 2 个不同的名称)。
-
Click on connect
-
Send and receive messages
和往常一样,可以使用以下命令打包应用程序:
quarkus build
./mvnw install
./gradlew build
并使用 `java -jar target/quarkus-app/quarkus-run.jar`执行。
您还可以使用以下命令构建本机可执行文件:
quarkus build --native
./mvnw install -Dnative
./gradlew build -Dquarkus.native.enabled=true
你还可以使用 here 中详述的方法测试 WebSocket 应用程序。
WebSocket Clients
Quarkus 还包含一个 WebSocket 客户端。你可以调用 ContainerProvider.getWebSocketContainer().connectToServer
创建 WebSocket 连接。默认情况下,quarkus-websockets
工件同时包括客户端和服务器支持。但是,如果你只想要客户端,你可以使用 quarkus-websockets-client
替代。
当连接到服务器时,你可以传入想要使用的注释客户端终端的类,或 jakarta.websocket.Endpoint
的实例。如果你正在使用注释终端,则可以使用与在服务器上完全相同的注释,除了必须使用 @ClientEndpoint
而不是 @ServerEndpoint
注释外。
以下示例展示了客户端被用来测试上述聊天终端。
package org.acme.websockets;
import java.net.URI;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit;
import jakarta.websocket.ClientEndpoint;
import jakarta.websocket.ContainerProvider;
import jakarta.websocket.OnMessage;
import jakarta.websocket.OnOpen;
import jakarta.websocket.Session;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
import io.quarkus.test.common.http.TestHTTPResource;
import io.quarkus.test.junit.QuarkusTest;
@QuarkusTest
public class ChatTest {
private static final LinkedBlockingDeque<String> MESSAGES = new LinkedBlockingDeque<>();
@TestHTTPResource("/chat/stu")
URI uri;
@Test
public void testWebsocketChat() throws Exception {
try (Session session = ContainerProvider.getWebSocketContainer().connectToServer(Client.class, uri)) {
Assertions.assertEquals("CONNECT", MESSAGES.poll(10, TimeUnit.SECONDS));
Assertions.assertEquals("User stu joined", MESSAGES.poll(10, TimeUnit.SECONDS));
session.getAsyncRemote().sendText("hello world");
Assertions.assertEquals(">> stu: hello world", MESSAGES.poll(10, TimeUnit.SECONDS));
}
}
@ClientEndpoint
public static class Client {
@OnOpen
public void open(Session session) {
MESSAGES.add("CONNECT");
// Send a message to indicate that we are ready,
// as the message handler may not be registered immediately after this callback.
session.getAsyncRemote().sendText("_ready_");
}
@OnMessage
void message(String msg) {
MESSAGES.add(msg);
}
}
}
More WebSocket Information
Quarkus WebSocket 实现是 Jakarta Websockets 的实现。