☰
Aeron 远程绑定测试资源调配指南:用 Fabric 编排跨主机 RemoteEchoTest
2026/9/25 3:04:26 网站建设 项目流程
  • 消息队列
  • 后端
  • 通信

【免费下载链接】aeron

Efficient reliable UDP unicast, UDP multicast, and IPC message transport

项目地址:https://gitcode.com/gh_mirrors/ae/aeron
点击查看免费下载

本指南以 aeron-system-tests/scripts/provisioning/README.md 为核心,结合仓库中fabfile.py、RemoteEchoTest与ProvisioningServerMain等源码,系统讲解 Aeron 如何通过 Fabric v2 在远程服务器上自动部署aeron-alljar、拉起回显(Echo)服务并以 JMX 驱动跨主机 UDP 绑定测试。读完本文,你将掌握fab五段式命令的完整用法、每个任务的底层实现、JMX 控制通道的工作方式,以及排查部署问题的关键点。

一、这套脚本要解决什么问题

Aeron 的消息传输支持 UDP 单播、UDP 组播与 IPC 三种方式。当需要验证 Aeron 在不同操作系统、不同网络栈(Bindings)上的真实收发行为时,最可靠的手段是跑系统级测试——尤其是让两个主机之间通过真实网络完成数据回环的RemoteEchoTest。

aeron-system-tests/scripts/provisioning目录下的脚本正是为此而生:它借助 Python 生态的 Fabric 部署工具 v2(官方文档见 https://docs.fabfile.org),把"构建、上传、远程启动服务、执行测试、清理"整条链路自动化,使开发者只需一条命令即可在远程主机上完成一次完整的绑定测试。

整个流程在 README.md 中被归纳为 5 个步骤:

  1. 构建一份aeron-all-<version>.jar
  2. 将 jar 拷贝到远程服务器
  3. SSH 登录远程服务器,启动一个远程资源调配服务(provisioning service)实例
  4. 运行RemoteEchoTest
  5. 停止该服务

二、环境要求与运行前提

脚本基于Fabric v2(其底层依赖 Invoke),需要先安装:

pip install fabric

同时还需满足以下前提:

  • 必须从项目根目录运行(README 明确说明 "It should be run from the root directory of the project"),因为脚本内部使用./gradlew、version.txt、aeron-all/build/libs/...等相对项目根目录的路径;
  • 本机与远程主机之间已配置好 SSH 免密登录(Fabric 依赖 SSH 连接执行远程命令);
  • 远程主机已安装Java,并在调用deploy时通过--java-home指明 JDK 目录;
  • 远程主机的10000 端口可供本机访问——该端口是部署脚本指定的 JMX/RMI 远程管理端口(详见下文)。

三、命令总览

README 给出的完整用法如下:

$ fab -r aeron-system-tests/scripts/provisioning \ -H <ip address to run the remote service on> stop \ prepare-deploy \ deploy \ --java-home=<location of the java home directory on the remote server> \ test \ --test-host=<optional, ip address of the host running RemoteEchoTest> \ --aeron-dir=<optional, path to an already running media driver>

参数说明:

参数含义是否必填
-r aeron-system-tests/scripts/provisioning指定 Fabric 任务(task)所在目录,即 fabfile.py必填
-H <ip>远程服务器的 IP 地址,测试服务将运行在此主机上必填
stop先清理远程主机上可能残留的旧服务进程(pkill)建议
prepare-deploy本地构建 jar(clean+:aeron-all:jar)必填
deploy --java-home=<路径>上传 jar 并远程启动 provisioning 服务必填
test本地执行RemoteEchoTest必填
--test-host=<ip>(可选)运行测试的主机 IP;默认自动推断可选
--aeron-dir=<路径>(可选)指向一个已运行的 media driver 目录;不填则测试内嵌启动 driver可选

可以看到命令将stop、prepare-deploy、deploy、test四个任务串联,等价于"旧进程清场 → 构建 → 部署 → 测试"的完整流水线。每个任务的实现细节如下。

四、任务逐层拆解(源码级)

fabfile.py基于 Invoke 的@task()装饰器定义了 4 个任务,外加一个辅助的version任务。

4.1 prepare-deploy:本地构建 aeron-all jar

@task() def prepare_deploy(c): local("./gradlew --console=verbose clean", env = {"JAVA_HOME": "/home/mike/opt/jdk/jdk8"}) local("./gradlew --console=verbose :aeron-all:jar", env = {"JAVA_HOME": "/home/mike/opt/jdk/jdk8"})

该任务在本机执行 Gradle Wrapper,先clean再构建:aeron-all:jar子模块,产物位于aeron-all/build/libs/aeron-all-<version>.jar。需要注意两点:

  • 脚本中的JAVA_HOME是作者本机的/home/mike/opt/jdk/jdk8,这是硬编码的开发环境路径,实际使用时应按本机 JDK 位置修改,或改为从环境变量读取;
  • jar 的版本号来自仓库根目录的 version.txt(当前为1.54.0-SNAPSHOT),deploy任务正是读取该文件确定上传文件名。

4.2 version:探测远程 Java 主版本

deploy内部会先调用{java_home}/bin/java -version,再通过parse_version正则从输出首行解析主版本号:

def parse_version(version_string): g = re.search("version \"(.*)\\.(.*)\\..*\"", version_string) ... if (g.group(1) == "1"): version = int(g.group(2)) # 如 1.8.0_xxx → 8 else: version = int(g.group(1)) # 如 17.0.x → 17 return version

该逻辑兼容 Java 8 时代的1.8.x命名法与 Java 9+ 的17.x命名法,解析出的版本号决定是否需要在启动命令中追加 JVM 参数(见下节)。

4.3 deploy:上传 jar 并远程启动服务

@task() def deploy(c, java_home=None, provisioning_host=None): ... command = [ "{}/bin/java".format(java_home), "-Dcom.sun.management.jmxremote", "-Dcom.sun.management.jmxremote.authenticate=false", "-Dcom.sun.management.jmxremote.ssl=false", "-Dcom.sun.management.jmxremote.port=10000", "-Djava.rmi.server.hostname={}".format(provisioning_host), "-cp ./provisioning/aeron-all-1.38.0-SNAPSHOT.jar", "io.aeron.samples.echo.ProvisioningServerMain", "< /dev/null > ./provisioning/log 2>&1", "&" ] if (8 < java_version): command.insert(1, "--add-opens java.base/jdk.internal.misc=ALL-UNNAMED") ... c.run("rm -rf provisioning") c.run("mkdir -p provisioning") c.put("aeron-all/build/libs/aeron-all-{}.jar".format(lines[0]), "provisioning/.") c.run(" ".join(command)) c.run("sleep 2 ; pgrep -f io.aeron.samples.echo.ProvisioningServerMain")

deploy的完整动作链为:

  1. 解析远程 Java 版本,若主版本 > 8(Java 9 及以上),在命令首部插入--add-opens java.base/jdk.internal.misc=ALL-UNNAMED——这是 Aeron 使用的 Agrona 底层库访问 JDK 内部 API 所需;
  2. 在远程主机的家目录下rm -rf provisioning并重建该目录;
  3. 将本机aeron-all/build/libs/aeron-all-<version>.jar(版本取自 version.txt)上传到远程provisioning/.;
  4. 用nohup风格的写法(< /dev/null > ./provisioning/log 2>&1 &)在后台启动主类io.aeron.samples.echo.ProvisioningServerMain,日志重定向到./provisioning/log;
  5. sleep 2后通过pgrep -f校验进程确实存活,若进程未拉起则该任务报错退出。

JMX 远程管理参数是整套远程控制机制的关键,含义如下:

JVM 参数作用
-Dcom.sun.management.jmxremote启用 JMX 远程管理
-Dcom.sun.management.jmxremote.authenticate=false关闭认证(测试场景)
-Dcom.sun.management.jmxremote.ssl=false关闭 SSL(测试场景)
-Dcom.sun.management.jmxremote.port=10000暴露 RMI 注册端口 10000
-Djava.rmi.server.hostname=<ip>指定 RMI 服务对外通告的主机名/IP,防止多网卡下地址推断错误

⚠️版本一致性提示:从当前仓库源码看,deploy中上传的 jar 文件名取自version.txt,但启动命令里-cp的 jar 名硬编码为aeron-all-1.38.0-SNAPSHOT.jar(fabfile.py 第 50 行)。若version.txt版本与硬编码值不一致,-cp会指向不存在的文件导致服务启动失败,实际使用时需保持两者同步。

4.4 test:在本地运行 RemoteEchoTest

@task() def test(c, provisioning_host=None, test_host=None, aeron_dir=None): ... command = [ "./gradlew", "-Daeron.test.system.binding.remote.host={}".format(provisioning_host), "--console=verbose", ":aeron-system-test:test", "--tests", "'*RemoteEchoTest'", ] if (not test_host is None): command.insert(1, "-Daeron.test.system.binding.local.host={}".format(test_host)) if (not aeron_dir is None): command.insert(1, "-Daeron.test.system.aeron.dir={}".format(aeron_dir)) local(" ".join(command))

测试任务在本机通过 Gradle 执行:aeron-system-test:test,只运行匹配*RemoteEchoTest的用例,并通过三个系统属性向测试传递运行参数:

系统属性默认行为作用
aeron.test.system.binding.remote.host必填(-H指定)远程 provisioning 服务所在主机
aeron.test.system.binding.local.host不传则由测试根据远程 IP 自动选择本机网卡本机参与通信的 IP
aeron.test.system.aeron.dir不传则测试内嵌启动 MediaDriver复用已运行的 media driver 目录

需要说明的是,Gradle 侧测试模块的完整任务名是:aeron-system-tests:test(见 settings.gradle 中aeron-system-tests模块声明),fabfile 中的:aeron-system-test:test与实际模块名存在拼写差异,实际运行时建议核对本仓库的 Gradle 模块名。

4.5 stop:清理远程服务

@task() def stop(c): c.run("pkill -f io.aeron.samples.echo.ProvisioningServerMain", warn=True)

用pkill -f精确匹配进程命令行中包含主类名io.aeron.samples.echo.ProvisioningServerMain的进程,warn=True表示即使没有匹配到进程(如首次运行)也不视为失败。该任务通常在deploy之前执行,确保不会与上一次遗留的服务进程冲突。

五、远程服务端:ProvisioningServerMain 剖析

远程主机上启动的ProvisioningServerMain位于 aeron-samples/src/main/java/io/aeron/samples/echo/ProvisioningServerMain.java,它同时扮演三个角色:

  • 内嵌 MediaDriver:若未设置aeron.dir系统属性,则调用MediaDriver.launchEmbedded()在远程主机内嵌启动驱动,并把 driver 目录传给 Aeron 客户端;
  • Aeron 客户端:Aeron.connect(context)连接本地 driver;
  • JMX 服务端:通过StandardMBean(provisioning, ProvisioningMBean.class)将 Provisioning 注册到平台 MBean Server,ObjectName 为io.aeron:type=Provisioning,name=testing(常量见 ProvisioningConstants.java)。

服务主体是一个 Agrona Agent(roleName为"EchoProvisioningServer"),其doWork()每次轮询做两件事:

  1. pollProvisioningQueue():消费ManyToOneConcurrentArrayQueue<ProvisioningMessage>中的控制消息(CreateEchoPair/RemoveAllEchoPairs);
  2. pollEchoPairs():对每个已创建的EchoPair调用poll(),完成"收到请求消息 → 原样回写"的回环转发。

createEchoPair(correlationId, subChannel, subStreamId, pubChannel, pubStreamId)是 JMX 暴露的核心操作:它会为请求通道创建Subscription、为响应通道创建ConcurrentPublication,并将二者封装为EchoPair,同时为每个 pair 注册EchoMonitorMBean(ObjectName 形如io.aeron:type=EchoPair,name=<correlationId>),用于上报getFragmentCount()等监控指标。

六、客户端:RemoteEchoTest 如何与远程服务协作

测试类位于 aeron-system-tests/src/test/java/io/aeron/RemoteEchoTest.java,标注了@BindingsTest(该注解定义于 aeron-test-support/src/main/java/io/aeron/test/BindingsTest.java),说明它属于专门验证网络绑定行为的系统测试。

beforeAll阶段的连接策略体现了整个机制的优雅之处:

  • 若设置了aeron.test.system.binding.remote.host(即远程部署模式),测试通过如下 JMX URL 连接远程服务:
    service:jmx:rmi:///jndi/rmi://<remoteHost>:10000/jmxrmi

    并通过JMX.newMBeanProxy获得ProvisioningMBean与EchoMonitorMBean的本地代理;

  • 若未设置远程主机(纯本地开发场景),则直接在当前 JVM 内ProvisioningServerMain.launch(...)并连接平台 MBean Server,测试仍可完整运行——这为不依赖远程环境的日常验证提供了便利。

测试包含两个用例:

  1. shouldHandleSingleUnicastEchoPair(10 秒超时):构建一对 UDP 单播通道,请求端点为remoteHost:24324、响应端点为localHost:24325,stream id 分别为 1001/1002,通过 JMX 调用createEchoPair(1, ...)创建回环对,随后本机向远程发布随机数据并订阅回传数据,最终校验EchoMonitorMBean.getFragmentCount() > 0且收发内容完全一致;
  2. shouldHandleTenUnicastEchoPairs(20 秒超时):循环创建 10 对 echo pair(请求端口 24300–24309、响应端口 24400–24409),并发跑完同样的随机数据回环校验,用于验证多通道并发下的可靠性。

测试使用 1 MiB 随机源数据(SOURCE_DATA_LENGTH = 1024 * 1024),通过Publication.offer按随机长度分片发送、Subscription.poll收取并比对,且所有通道都显式设置了rejoin(false)、linger(0)、termLength(1 << 16),保证通道生命周期可控、资源可回收。

七、与 CMake 测试体系的关联

除 Fabric 脚本外,这套系统测试也可由 CMake 驱动运行。在 aeron-system-tests/CMakeLists.txt 中,当开启AERON_SYSTEM_TESTS时,会注册名为java_system_tests_c_media_driver的测试目标,它通过 Gradle Wrapper 执行:aeron-system-tests:cleanTest与:aeron-system-tests:test,并通过-Daeron.test.system.aeronmd.path传入 C 语言版 Media Driver 的可执行文件路径,验证 Java 客户端与 C driver 的组合。这也解释了为何test任务中的--aeron-dir参数同样支持指向"已运行的 media driver"。

八、实操注意事项

  1. JAVA_HOME 硬编码:prepare_deploy中JAVA_HOME写死为作者本机路径,实际使用请改为本机 JDK 8 及以上版本路径;
  2. 远程 JDK 必须 >= 8:deploy会解析远程 Java 版本;Java 9+ 会自动追加--add-opens,Java 8 则无需;
  3. jar 版本一致性:version.txt中的版本号决定上传文件名,而-cp中的 jar 名在 fabfile 中是硬编码的,两者不一致会导致远程启动失败;
  4. 防火墙/安全组:确保本机能访问远程 10000 端口(JMX/RMI 注册),否则RemoteEchoTest的JMXConnectorFactory.connect会连接超时;
  5. SSH 免密:Fabric 通过 SSH 执行远程命令,需提前配置密钥认证;
  6. 复用外部 driver:若远程已运行独立的 MediaDriver,可先设置aeron.dir再启动ProvisioningServerMain(其launch逻辑只在未设置该属性时才内嵌启动 driver);
  7. 端口占用:测试用例使用 24300–24325 区间的固定端口,若与其他进程冲突需调整通道配置。

九、相关文件索引

  • README(本文依据)
  • Fabric 任务实现
  • RemoteEchoTest 测试类
  • 远程服务主类
  • Provisioning 核心逻辑
  • JMX 常量定义
  • 版本文件
  • 系统测试的 CMake 集成

这套脚本的价值在于把"跨主机绑定验证"从手工操作(手动构建、scp、ssh 启动、跑测试、kill 进程)压缩为一条fab命令,同时通过 JMX 控制通道把远程服务的生命周期与本地测试的执行过程解耦——无论是 CI 流水线中多主机绑定矩阵验证,还是本地临时搭建跨机回环环境,都能直接复用。

  • 消息队列
  • 后端
  • 通信

【免费下载链接】aeron

Efficient reliable UDP unicast, UDP multicast, and IPC message transport

项目地址:https://gitcode.com/gh_mirrors/ae/aeron
点击查看免费下载

相关推荐

上一篇:SDRPlusPlus配置文件加密工具:命令行界面与密钥管理
下一篇:CrossDesk安全机制全面剖析:保障远程连接的数据传输安全

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询