多节点测试

注:本节未经校验,如有问题欢迎提issue

多节点测试概念

当我们谈论Akka多节点测试时,我们指的是多个actor系统在不同Jvm上运行协同测试的过程。多节点测试套件由三个主要部分组成。

  • 测试导体。协调和控制测试的节点。
  • 多节点规格。启动TestConductor的方便的包装器,并允许所有节点连接到它。
  • SbtMultiJvm插件。启动可能在多个机器上的多个Jvm的测试。

测试导线" class="reference-link">测试导线

多节点测试的基础是 TestConductor。它是插入到网络栈的一个Akka扩展,它用来协调参与测试的节点,并提供多种功能,包括:

  • 节点地址查找: 找出另一个测试节点的完整路径 (不需要在测试节点之间共享配置)
  • 节点屏障协调: 在指定屏障等待其他节点。
  • 网络故障注入: 流量限制、丢失的数据包、拔出节点并重新插入。

这是测试指挥示意性概述。

.. image:: ../images/akka-remote-testconductor.png

测试导体服务器负责协调屏障,并向对他们采取行动的测试导体客户端发送命令,例如限制从/到另一个客户端的网络流量。有关可能操作的详细信息见 akka.remote.testconductor.Conductor API 文档。

多节点规范" class="reference-link">多节点规范

多节点规范由两部分组成。负责对常见配置,并且枚举和命名测试节点的MultiNodeConfigMultiNodeSpec中包含许多方便的函数使测试节点彼此交互。有关可能的操作的详细信息见 akka.remote.testkit.MultiNodeSpec API 文档。

MultiNodeSpec 的启动设置是通过java 系统属性配置的,你需要对要运行测试的节点的所有Jvm设定的。这些可以轻松地通过在 JVM 命令行中使用-Dproperty=value设置。

以下是是可用的属性:

  • multinode.max-nodes

    一个测试可以有的节点的最大数目。

  • multinode.host

    此节点的主机名或 IP 。必须能够使用 InetAddress.getByName解析。

  • multinode.port

    此节点端口号。默认值为 0,此时将使用一个随机端口。

  • multinode.server-host

    主机名或服务器节点 IP 。必须能够使用 InetAddress.getByName解析。

  • multinode.server-port

    服务器节点的端口号。默认值为 4711。

  • multinode.index

    为测试定义的角色序列中本节点的索引。索引 0 是特别指定为服务器的机器。所有的故障注入和节流必须在这个节点进行。

SbtMultiJvm 插件" class="reference-link"> SbtMultiJvm 插件

SbtMultiJvm 插件已更新以支持运行多节点测试,通过自动生成相关的 multinode.* 属性。这意味着你可以轻松地在单台计算机上运行多节点测试,而不用任何特殊配置,只要像运行正常多jvm测试一样运行即可。这些测试无需任何更改就可以分布运行在多台机器上,只需使用插件的多节点附加。

多节点特定附加

该插件也有大量的新的multi-node-* sbt 任务和支持多台机器上运行测试的设置。必要的测试类和依赖项通过SbtAssembly被打包入一个 jar 文件分发给其他机器,其命名格式为<projectName>_<scalaVersion>-<projectVersion>-multi-jvm-assembly.jar

注意

要能分发并在多个机器上启动测试,需假定系统主机和目标系统是类 POSIX 系统,并支持 sshrsync

以下都是可用的 sbt 多节点的设置:

  • multiNodeHosts

    用于运行测试的主机,以user@host:java的形式,其中主机是唯一必须的部分。将覆盖文件中的设置。

  • multiNodeHostsFileName

    用于在运行测试的主机读取的文件。与上述格式相同,每行一个。默认为项目基目录中的multi-node-test.hosts

  • multiNodeTargetDirName

    目标系统上的目录名称,用于复制 jar 文件。默认为ssh 用户基目录的multi-node-test,用于 rsync jar 文件。

  • multiNodeJavaName

    目标机器上的默认 Java 可执行文件的名称。默认值为 java

这里是如何定义主机的一些示例:

  • localhost

    本地主机上的当前用户,使用缺省 java。

  • user1@host1

    host1主机上的用户 user1,使用缺省 java。

  • user2@host2:/usr/lib/jvm/java-7-openjdk-amd64/bin/java

    host2主机上的用户 user2,使用 java 7。

  • host3:/usr/lib/jvm/java-6-openjdk-amd64/bin/java

    host3主机上的当前用户 user2,使用 java 6。

运行多节点测试

若要在多节点模式下从sbt中运行所有的多节点测试 (即分发 jar 文件并开启远程测试),使用multi-node-test任务:

  1. multi-node-test

若要在多 jvm 模式中运行所有测试(即在本地计算机上的所有 Jvm),执行:

  1. multi-jvm:test

若要运行单个测试使用multi-node-test-only任务:

  1. multi-node-test-only your.MultiNodeTest

若要在多 jvm 模式下运行单个测试,执行:

  1. multi-jvm:test-only your.MultiNodeTest

可以列出多个测试名称来运行多个特定的测试。sbt的 tab 键提示可以帮你很容易地写出测试的名称。

为你的项目准备多节点测试

多节点测试套件是一个单独的 jar 文件。请确保你的项目中包含以下依存关系:

  1. "com.typesafe.akka" %% "akka-multi-node-testkit" % "2.3.6"

如果你正在使用最新的每夜构建,你应该从“http://repo.typesafe.com/typesafe/snapshots/com/typesafe/akka/akka-multi-node-testkit_2.10/”选择一个带时间戳的Akka版本。我们建议不要使用``SNAPSHOT``以获得稳定的构建。

多节点测试示例

首先,我们需要一些脚手架来讲MultiNodeSpec与你最喜爱的测试框架钩起来。允许定义一个STMultiNodeSpec特质使用 ScalaTest 来启动和停止 MultiNodeSpec

  1. package sample.multinode
  2. import org.scalatest.{ BeforeAndAfterAll, WordSpecLike }
  3. import org.scalatest.Matchers
  4. import akka.remote.testkit.MultiNodeSpecCallbacks
  5. /**
  6. * Hooks up MultiNodeSpec with ScalaTest
  7. */
  8. trait STMultiNodeSpec extends MultiNodeSpecCallbacks
  9. with WordSpecLike with Matchers with BeforeAndAfterAll {
  10. override def beforeAll() = multiNodeSpecBeforeAll()
  11. override def afterAll() = multiNodeSpecAfterAll()
  12. }

然后我们需要定义一个配置。让我们利用两个节点"node1"node2",并称之为 MultiNodeSampleConfig

  1. package sample.multinode
  2. import akka.remote.testkit.MultiNodeConfig
  3. object MultiNodeSampleConfig extends MultiNodeConfig {
  4. val node1 = role("node1")
  5. val node2 = role("node2")
  6. }

然后最终到节点测试代码。启动两个节点,并演示了一个屏障,和一个远程actor消息发送和接收。

  1. package sample.multinode
  2. import akka.remote.testkit.MultiNodeSpec
  3. import akka.testkit.ImplicitSender
  4. import akka.actor.{ Props, Actor }
  5. class MultiNodeSampleSpecMultiJvmNode1 extends MultiNodeSample
  6. class MultiNodeSampleSpecMultiJvmNode2 extends MultiNodeSample
  7. object MultiNodeSample {
  8. class Ponger extends Actor {
  9. def receive = {
  10. case "ping" => sender() ! "pong"
  11. }
  12. }
  13. }
  14. class MultiNodeSample extends MultiNodeSpec(MultiNodeSampleConfig)
  15. with STMultiNodeSpec with ImplicitSender {
  16. import MultiNodeSampleConfig._
  17. import MultiNodeSample._
  18. def initialParticipants = roles.size
  19. "A MultiNodeSample" must {
  20. "wait for all nodes to enter a barrier" in {
  21. enterBarrier("startup")
  22. }
  23. "send to and receive from a remote node" in {
  24. runOn(node1) {
  25. enterBarrier("deployed")
  26. val ponger = system.actorSelection(node(node2) / "user" / "ponger")
  27. ponger ! "ping"
  28. expectMsg("pong")
  29. }
  30. runOn(node2) {
  31. system.actorOf(Props[Ponger], "ponger")
  32. enterBarrier("deployed")
  33. }
  34. enterBarrier("finished")
  35. }
  36. }
  37. }

运行此示例最简单的方法是下载Typesafe Activator并打开名为Akka Multi-Node Testing Sample with Scala的教程。

需要记住的事情

当写多节点测试时有几件事情要牢记在心,否则你的测试行为可能会出现意想不到的行为。

  • 不要关闭第一个节点。第一个节点是控制器,而如果它关闭则你的测试将中断。
  • 要能够使用blackhole, passThroughthrottle,你必须激活故障注入器和节流传输适配器,通过在你的 MultiNodeConfig中指定testTransport(on = true)
  • 限流,关闭和其他失败注解只可以从第一个节点,又即控制器发出。
  • 不要再借点关闭后使用 node(address) 询问节点地址。要在关闭节点之前抓取其地址。
  • 不要在主线程之外的其他线程使用 MultiNodeSpec 方法,比如地址查找,屏障进入等。这也意味着你不应该在actor内部或一个计划的任务中使用他们。

配置

多节点测试模块的几个配置属性,请参考配置一节。