Using WebSockets
本指南解释了如何让你的 Quarkus 应用程序利用 WebSockets 创建交互式 Web 应用程序。因为它是一个 canonical WebSocket 应用程序,所以我们要创建一个简单的聊天应用程序。
Solution
我们建议你按照后续章节中的说明,逐步创建应用程序。但是,你可以直接跳到已完成的示例。
克隆 Git 存储库: git clone {quickstarts-clone-url}
,或下载 {quickstarts-archive-url}[存档]。
解决方案位于 websockets-quickstart
directory。
Creating the Maven project
首先,我们需要一个新项目。使用以下命令创建一个新项目:
Unresolved directive in websockets.adoc - include::{includes}/devtools/create-app.adoc[]
此命令生成项目(没有任何类),并导入 websockets
扩展。
如果你已配置你的 Quarkus 项目,则可以通过在项目基本目录中运行以下命令向你的项目添加 websockets
扩展:
Unresolved directive in websockets.adoc - include::{includes}/devtools/extension-add.adoc[]
这会将以下内容添加到构建文件中:
<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 <1> 存储当前打开的 WebSocket
A slick web frontend
所有聊天应用程序都需要 nice UI,此示例可能不太美观,但能完成工作。Quarkus 自动为 META-INF/resources
目录中包含的静态资源提供服务。创建 src/main/resources/META-INF/resources
目录并将此 index.html 文件复制到其中。
Run the application
现在,让我们看看应用程序在实际应用中的情况。使用以下命令运行它:
Unresolved directive in websockets.adoc - include::{includes}/devtools/dev.adoc[]
然后在 2 个浏览器窗口中打开 [role="bare"][role="bare"]http://localhost:8080/:
-
在顶部文本区域中输入一个名称(使用 2 个不同的名称)。
-
Click on connect
-
Send and receive messages
和往常一样,可以使用以下命令打包应用程序:
Unresolved directive in websockets.adoc - include::{includes}/devtools/build.adoc[]
并使用 `java -jar target/quarkus-app/quarkus-run.jar`执行。
您还可以使用以下命令构建本机可执行文件:
Unresolved directive in websockets.adoc - include::{includes}/devtools/build-native.adoc[]
你还可以使用 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 的实现。