将 WebSockets 与 Undertow 一起使用
本指南将解释 Quarkus 应用程序如何在 Undertow 基础的 Quarkus 应用程序中,或者在依赖 Jakarta WebSocket 的情况下,利用 WebSockets 创建交互式 Web 应用程序。
如果您不使用 Undertow 或 Jakarta WebSocket,建议使用更现代的 WebSockets Next 扩展。 |
由于这是标准的 WebSocket 应用程序,我们将创建一个简单的聊天应用程序。
先决条件
要完成本指南,您需要
-
大约 15 分钟
-
一个 IDE
-
已安装 JDK 17+ 并正确配置了
JAVA_HOME
-
Apache Maven 3.9.9
-
如果您想使用它,可以选择 Quarkus CLI
-
如果您想构建本机可执行文件(或者如果您使用本机容器构建,则为 Docker),可以选择安装 Mandrel 或 GraalVM 并进行适当的配置
解决方案
我们建议您按照以下部分的说明逐步创建应用程序。但是,您可以直接跳到完成的示例。
克隆 Git 仓库:git clone https://github.com/quarkusio/quarkus-quickstarts.git
,或下载 存档。
解决方案位于 websockets-quickstart
目录。
创建 Maven 项目
首先,我们需要一个新项目。使用以下命令创建一个新项目
对于 Windows 用户
-
如果使用 cmd,(不要使用反斜杠
\
并将所有内容放在同一行上) -
如果使用 Powershell,请将
-D
参数用双引号括起来,例如"-DprojectArtifactId=websockets-quickstart"
此命令会生成项目(不包含任何类)并导入 websockets
扩展。
如果您已经配置了 Quarkus 项目,可以通过在项目根目录运行以下命令将 websockets
扩展添加到项目中
quarkus extension add websockets
./mvnw quarkus:add-extension -Dextensions='websockets'
./gradlew addExtension --extensions='websockets'
这会将以下内容添加到您的构建文件中
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-websockets</artifactId>
</dependency>
implementation("io.quarkus:quarkus-websockets")
如果您只想使用 WebSocket 客户端,则应包含 quarkus-websockets-client 。 |
处理 WebSockets
我们的应用程序包含一个处理 WebSockets 的类。在 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 | 存储当前打开的 WebSockets |
一个简洁的 Web 前端
所有聊天应用程序都需要一个漂亮的 UI,嗯,这个可能不是最漂亮的,但能完成工作。Quarkus 会自动服务 META-INF/resources
目录中的静态资源。创建 src/main/resources/META-INF/resources
目录并将此 index.html 文件复制进去。
运行应用程序
现在,让我们看看我们的应用程序的运行情况。使用以下命令运行它
quarkus dev
./mvnw quarkus:dev
./gradlew --console=plain quarkusDev
然后打开 2 个浏览器窗口访问 https://:8080/
-
在顶部的文本区域输入一个名字(使用 2 个不同的名字)。
-
点击连接
-
发送和接收消息
与往常一样,可以使用以下命令打包应用程序
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
您也可以使用此处详细介绍的方法来测试您的 WebSocket 应用程序 here。
WebSocket 客户端
Quarkus 还包含一个 WebSocket 客户端。您可以调用 ContainerProvider.getWebSocketContainer().connectToServer
来创建 WebSocket 连接。默认情况下,quarkus-websockets
artifact 同时包含客户端和服务器支持。但是,如果您只想使用客户端,可以改用 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);
}
}
}
更多 WebSocket 信息
Quarkus WebSocket 实现是 Jakarta Websockets 的实现。