鸿蒙系统应用开发js(使用Cettia构建实时Web应用程序第1部分)
鸿蒙系统应用开发js(使用Cettia构建实时Web应用程序第1部分)它以事件驱动的方式处理暂时断开和永久断开。它简化了一套套接字处理,有助于极大地改善多设备用户体验。即使提供代理,防火墙,防病毒软件或任意平台即服务(PaaS),它也提供简单的全双工连接。它旨在不在服务器之间共享数据,并且可以轻松进行水平缩放。它提供了一个事件系统来对发生在服务器端和客户端的事件进行分类,并且可以实时交换它们。
在本教程中,我们将看看使用Cettia创建实时导向Web应用程序所需的功能,并构建Cettia入门工具包。
2011年,我开始了Cettia的前任(一个用于HTTP流的jQuery插件,我曾用它演示servlet 3.0的异步Servlet和IE 6)。从那时起,WebSocket和Asynchronous IO已经被广泛使用,并且开发变得更容易并在客户端和服务器环境中维护实时Web应用程序。但与此同时,功能性和非功能性要求已经变得更加复杂和难以满足,并且越来越难以估计和控制伴随的技术债务。
Cettia是最初为解决这些挑战而开始的项目的结果,并且是一个创建实时Web应用程序而不妥协的框架:
-
它旨在无缝地在Java虚拟机(JVM)上的任何I / O框架上运行。
-
即使提供代理,防火墙,防病毒软件或任意平台即服务(PaaS),它也提供简单的全双工连接。
-
它旨在不在服务器之间共享数据,并且可以轻松进行水平缩放。
-
它提供了一个事件系统来对发生在服务器端和客户端的事件进行分类,并且可以实时交换它们。
-
它简化了一套套接字处理,有助于极大地改善多设备用户体验。
-
它以事件驱动的方式处理暂时断开和永久断开。
在本教程中,我们将看看使用Cettia创建实时导向Web应用程序所需的功能,并构建Cettia入门工具包。入门工具包的源代码可在https://github.com/cettia/cettia-starter-kit获得。
服务器 - 用于与套接字交互的接口。它提供了一个事件来初始化新接受的套接字,并提供finder方法找到插座符合指定条件并执行给定的套接字行动。
套接字 - 构建在传输层顶部的功能丰富的接口。它提供的事件系统允许您定义自己的事件,而不考虑事件数据的类型,并实时在Cettia客户端和Cettia服务器之间交换它们。
传输 - 表示全双工消息信道的接口。它带有基于消息成帧的二进制和文本有效载荷,双向交换消息,并确保没有消息丢失和没有空闲连接。与服务器和套接字不同,除非要调整默认传输行为或引入全新传输,否则不需要知道传输。
让我们在上面的I / O框架不可知层上设置Cettia服务器,并打开一个套接字作为烟雾测试。添加以下导入:
import io.cettia.DefaultServer;
import io.cettia.Server;
import io.cettia.ServerSocket;
import io.cettia.transport.http.HttpTransportServer;
import io.cettia.transport.websocket.WebSocketTransportServer;
现在,用以下Cettia部分替换上面的Asity部分:
// Cettia part
Server server = new DefaultServer();
HttpTransportServer httpTransportServer = new HttpTransportServer().ontransport(server);
WebSocketTransportServer wsTransportServer = new WebSocketTransportServer().ontransport(server);
// The socket handler
server.onsocket((ServerSocket socket) -> System.out.println(socket));
如的实施方案Action<ServerHttpExchange>和TransportServer,HttpTransportServer消耗HTTP请求-响应交换,并产生流式传输和长轮询传输,并且如的实施方案Action<ServerWebSocket>和TransportServer,WebSocketTransportServer消耗的WebSocket资源,并产生一个网页套接字运输。这些产生的传输被传入Server并用于创建和维护ServerSockets。
当然,WebSocket传输已经足够了,但如果涉及代理,防火墙,防病毒软件或任意平台即服务(PaaS),很难确定单独使用WebSocket是否有效。这就是为什么我们建议您在各种环境中HttpTransportServer一起安装,WebSocketTransportServer以更广泛地覆盖全双工信息通道。
ServerSocket由创建者Server传递给注册的套接字处理程序server.onsocket(socket -> {}),并且此处理程序是您应该初始化套接字的位置。由于接受传输和套接字的成本很高,因此您应该在Cettia之外提前验证请求(如果需要),并在将它们传递给Cettia之前过滤掉不合格的请求。例如,假设使用Apache Shiro,它看起来像这样:
server.onsocket(socket -> {
Subject currentUser = SecurityUtils.getSubject();
if (currentUser.hasRole("admin")) {
// ...
}
});
在客户端,您可以打开指向Cettia服务器的URI的套接字cettia.open(uri)。在索引页面的控制台中运行以下片段:
var socket = cettia.open("http://127.0.0.1:8080/cettia");
如果所有设置都正确,您应该能够在服务器端看到套接字日志,并且可以在客户端通过开发人员工具的网络面板看到发生的情况。如果由于某种原因WebSocket传输在客户端或服务器中都不可用,则Cettia客户端会自动回退到基于HTTP的传输。注释掉Java WebSocket API部分并再次打开一个套接字,或者在Internet Explorer 9中打开索引页。在任何情况下,您都会看到打开一个套接字
套接字生命周期
套接字始终处于特定状态,如打开或关闭,并且其状态会根据底层传输的状态而不断变化。Cettia定义了客户端套接字和服务器套接字的状态转换图,并提供了各种内置事件,这些事件允许在发生状态转换时对套接字进行细粒度处理。如果您充分利用这些图表和内置事件,则可以轻松处理事件驱动的有状态套接字,而无需自行管理其状态。
将以下代码添加到中的套接字处理程序中CettiaConfigListener。它在发生状态转换时记录套接字的状态。
Action<Void> logState = v -> System.out.println(socket " " socket.state());
socket.onopen(logState).onclose(logState).ondelete(logState);
以下是服务器套接字的状态转换图:
-
在收到传输时,服务器将创建一个具有 NULL状态的套接字并将其传递给套接字处理程序。
-
如果它没有执行握手,它将转换到一个CLOSED状态并触发一个close事件。
-
如果它成功执行握手,它将转换为OPENED状态并触发open事件。只有在这种状态下通信才是可能的。
-
如果由于某种原因连接断开,它将转换为CLOSED状态并触发close事件。
-
如果连接通过客户端重新连接恢复,它将转换为OPENED状态并触发open事件。
-
自从CLOSED状态过去一分钟后,它转换到最终状态并触发delete事件。这种状态下的套接字不应该被使用。
正如你所看到的,如果发生4的状态转换,它应该转换为5或6。你可能想重新发送客户端在没有前者连接的情况下无法接收的事件,并采取行动通知错过事件的用户,例如后者的推送通知。我们将在稍后详细讨论如何做到这一点。
在客户端,从用户体验角度告知用户线路上发生了什么非常重要。打开套接字并添加事件处理程序以在发生状态转换时记录套接字的状态:
var socket = cettia.open("http://127.0.0.1:8080/cettia");
var logState = arg => console.log(socket.state() arg);
socket.on("connecting" logState).on("open" logState).on("close" logState).on("waiting" logState);
以下是客户端套接字的状态转换图:
-
如果一个套接字由cettia.open一个连接创建并启动一个连接,它将转换为一个connecting状态并触发一个connecting事件。
-
如果所有传输都无法及时连接,它将转换为closed状态并触发close事件。
-
如果其中一个传输成功建立连接,则它将转换为opened状态并触发open事件。只有在这种状态下通信才是可能的。
-
如果连接由于某种原因断开连接,它将转换为closed状态并触发close事件。
-
如果计划重新连接,它将转换到一个waiting状态,并waiting以重新连接延迟和总重新连接尝试触发一个事件。
-
如果套接字在重新连接延迟结束后开始连接,它将转换到connecting状态并触发connecting事件。
-
如果套接字被socket.close方法关闭,它将转换到最终状态。这种状态下的套接字不应该被使用。
如果连接没有问题,套接字将有一个3-4-5-6的状态转换周期。如果没有,它将有一个2-5-6的状态转换周期。重新启动或关闭服务器以进行4-5-6或2-5-6的状态转换。
发送和接收事件
通过单个通道交换各种类型数据的最常见模式是命令模式; 一个命令对象被序列化并通过线路发送,然后反序列化并在另一端执行。起初,JSON和switch语句应该足以实现这种模式,但如果您必须处理二进制类型的数据,那么它将成为维护和累积技术债务的负担; 实施心跳检查并确保您获得数据的确认。Cettia提供了一个足够灵活的事件系统来满足这些要求。
Cettia客户端和Cettia服务器之间的实时交换单位是由必需的名称属性和可选的数据属性组成的事件。只要名称不与内置事件重复,就可以定义和使用自己的事件。这是echo事件处理程序,其中收到的任何echo事件都会被发回。将它添加到套接字处理程序中:
socket.on("echo" data -> socket.send("echo" data));
在上面的代码中,我们没有操作或验证给定的数据,但使用无类型输入与在服务器中一样不现实。事件数据允许的类型由Cettia在内部使用的JSON处理器Jackson确定。如果事件数据应该是原始类型之一,则可以将其与相应的包装类一起进行强制转换和使用,如果它应该是List或Map之类的对象,并且您更喜欢POJO,则可以将其转换并与其一起使用像杰克逊这样的JSON库。它可能看起来像这样:
socket.on("event" data -> {
Model model = objectMapper.convertValue(data Model.class);
Set<ConstraintViolation<Model>> violations = validator.validate(model);
// ...
});
在客户端,事件数据只是JSON,但有一些例外。以下是测试服务器echo事件处理程序的客户端代码。这个简单的客户端向echo事件发送一个包含任意数据的open事件,并记录echo要接收的事件的数据以返回到控制台。
var socket = cettia.open("http://127.0.0.1:8080/cettia");
socket.once("open" () => socket.send("echo" "Hello world"));
socket.on("echo" data => console.log(data));
当我们决定使用控制台时,您可以键入并运行代码片段,例如: socket.send("echo" {text: "I'm a text" binary: new TextEncoder().encode("I'm a binary")}).send("echo" "It's also chainable")并在飞行中观看结果。在你的控制台上试试它。
如示例所示,事件数据基本上可以是任何数据,只要它是可序列化的,无论数据是二进制还是文本。如果事件数据的属性中的至少一个是byte[]或ByteBuffer在服务器中,Buffer在节点或ArrayBuffer在浏览器中,该事件数据被视为二值和MessagePack格式是用来代替JSON格式。总之,您可以交换包括二进制数据在内的事件数据,而不会造成任何问题。
今天就是这样!明天我们将介绍广播事件,使用特定套接字,断开连接处理以及扩展应用程序。