事件总线跨端桥接

Vert.x TCP EventBus 网桥 (vertx-tcp-eventbus-bridge)

Vert.x TCP EventBus bridge 为外部系统提供了直接基于原生 TCP Socket 与 Vert.x 内部事件总线(Event Bus)进行双向通信的轻量级网桥服务。 要在项目中使用该组件,请在构建描述文件中添加以下依赖:

  • Maven(在 pom.xml 中):

<dependency>
  <groupId>io.vertx</groupId>
  <artifactId>vertx-tcp-eventbus-bridge</artifactId>
  <version>5.2.1</version>
</dependency>
  • Gradle(在 build.gradle 文件中):

compile 'io.vertx:vertx-tcp-eventbus-bridge:5.2.1'

TCP EventBus 网桥直接构建在原始 TCP 之上。这意味着任何具备建立 TCP Socket 能力的外部应用(无论何种开发语言或运行环境),均可直接同远程 Vert.x 实例的 Event Bus 展开全双工交互。

相较于基于 HTTP WebSocket 的 SockJS 网桥,TCP 网桥的核心优势在于极度轻量与极低资源消耗:完全去除了 HTTP 握手与 WebSocket 协议栈开销,代之以纯粹的原生 TCP 套接字。这在嵌入式 IoT 设备或受限环境中表现极其出色。

即便对于普通应用,该协议也凭借清晰简单的二进制帧结构,为非 JVM 语言生态提供了一条高效可靠的集成纽带。

通信帧协议规范

协议设计力求极致极简,客户端与服务端之间的双向通信均采用统一的数据帧(Frame)封装。数据帧结构如下:

<长度: 32位无符号大端整数><{
   type: String,
   address: String,
   (replyAddress: String)?,
   headers: JsonObject,
   body: JsonObject
}: JSON对象>

通信内容由一个 JSON 报文构成(压缩或带空白均可)。每个 JSON 报文前必须强制附加一个 32 位大端序(Big-Endian)正整数(4 字节),用以指明后续完整 JSON 报文字符串的准确字节长度。

客户端上送消息支持的 type 类型包括:

  1. send:向指定 address 发送点对点消息

  2. publish:向指定 address 广播发布消息

  3. register:订阅监听发往或广播至某 address 的消息

  4. unregister:取消对某 address 的消息订阅

  5. ping:向网桥发送心跳 ping 探活包

请注意:replyAddress 字段为选填项,仅在 send 类型的消息中生效。携带该字段的消息在服务端完成处理后,将收到一条发往该 replyAddress 地址的响应消息。

网桥服务端回推给客户端的消息支持的 type 包括:

  1. message:在客户端订阅的 address 上收到发送或发布的消息

  2. err:表示处理出错(错误明细包含在 body 中)

  3. pong:对客户端发送的 ping 心跳探针返回响应

项目源码中附带了一个 Node.js 客户端参考实现。该客户端提供了与 SockJS 网桥一致的高层 API 抽象,便于在 TCP 与 SockJS 实现之间灵活平滑切换。

启动网桥示例

以下是创建并启动 TCP 网桥的基础代码:

TcpEventBusBridge bridge = TcpEventBusBridge.create(
    vertx,
    new BridgeOptions()
        .addInboundPermitted(new PermittedOptions().setAddress("in"))
        .addOutboundPermitted(new PermittedOptions().setAddress("out")));

bridge.listen(7000).onComplete(res -> {
  if (res.succeeded()) {
    // 网桥成功监听在 7000 端口
  } else {
    // 监听失败!
  }
});

监听 Unix 域套接字 (Domain Sockets)

在 JDK 16+ 运行时或启用了 Vert.x 原生网络传输 环境下,TCP 网桥服务端还支持直接监听 Unix 域套接字(Domain Socket)文件,实现本机超高性能 IPC 通信:

SocketAddress domainSocketAddress = SocketAddress.domainSocketAddress("/var/tmp/bridge.sock");

bridge.listen(domainSocketAddress).onComplete(res -> {
  if (res.succeeded()) {
    // Unix Domain Socket 监听成功
  } else {
    // 失败!
  }
});