多节点测试

依赖

为了使用多节点测试(Multi Node Testing),你需要将以下依赖添加到你的项目中:

  1. <!-- Maven -->
  2. <dependency>
  3. <groupId>com.typesafe.akka</groupId>
  4. <artifactId>akka-multi-node-testkit_2.12</artifactId>
  5. <version>2.5.22</version>
  6. </dependency>
  7. <!-- Gradle -->
  8. dependencies {
  9. compile group: 'com.typesafe.akka', name: 'akka-multi-node-testkit_2.12', version: '2.5.22'
  10. }
  11. <!-- sbt -->
  12. libraryDependencies += "com.typesafe.akka" %% "akka-multi-node-testkit" % "2.5.22"

示例项目

你可以查看「多节点示例」项目,以了解实践中的情况。

多节点测试概念

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

  • Test Conductor,它协调和控制被测节点。
  • Multi Node Spec,它是一个方便的包装器,用于启动TestConductor并让所有节点连接到它。
  • SbtMultiJvm 插件,它可以在多个 JVM 中(可能在多台机器上)启动测试。

Test Conductor

多节点测试的基础是TestConductor。它是一个插入网络堆栈的 Akka 扩展,用于协调参与测试的节点,并提供以下几个功能:

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

下面是TestConductor的示意图概述:

test-conductor-sc

TestConductor服务器负责协调屏障并向TestConductor客户端发送命令,例如限制与其他客户端之间的网络流量。有关可能操作的更多信息,请参阅akka.remote.testconductor.Conductor API文档。

Multi Node Spec

Multi Node Spec由两部分组成。MultiNodeConfig负责公共配置、枚举和命名测试节点;MultiNodeSpec包含许多方便函数,用于使测试节点相互作用。有关可能操作的更多信息,请参阅akka.remote.testkit.MultiNodeSpec API文档。

MultiNodeSpec的设置是通过 Java 系统属性来配置的,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 插件

SbtMultiJvm插件已经更新,可以通过自动生成相关的multinode.*属性来运行多节点测试。这意味着你可以在一台机器上运行多节点测试,而无需任何特殊配置,方法是将它们作为普通的多 JVM 测试运行。然后,通过使用插件中的多节点添加,可以在多台计算机上运行这些测试,而无需进行任何更改。

多节点特定添加

该插件还具有许多新的multi-node-* sbt 任务和设置,以支持在多台计算机上运行测试。将必要的测试类和依赖项打包,以便通过「SbtAssembly」分发到一个jar文件中,该文件的格式为<projectName>_<scalaVersion>-<projectVersion>-multi-jvm-assembly.jar

  • 注释:为了能够在多台机器上分发和启动测试,假设主机和目标系统都是具有sshrsync可用性的类似 POSIX 的系统。

这些是可用的sbt多节点设置:

  • multiNodeHosts,提供一个用于运行测试的主机序列,在表单user@host:java中,host是唯一需要的部分。将覆盖文件中的设置。
  • multiNodeHostsFileName,用于在主机中读取以用于运行测试的文件。每行一个,格式同上。默认为基本项目目录中的multi-node-test.hosts
  • multiNodeTargetDirName,为目标计算机上的目录命名,在其中复制jar文件。默认为ssh用户的基本目录中用于rsync jar 文件的multi-node-test
  • multiNodeJavaName,目标机器上的默认 Java 可执行文件的名称,默认为java

下面是一些如何定义主机的示例:

  • localhost,使用默认java在本地主机上定位当前用户。
  • user1@host1,用户user1在主机host1使用默认java
  • user2@host2:/usr/lib/jvm/java-7-openjdk-amd64/bin/java,用户user2在主机host2上使用java 7
  • host3:/usr/lib/jvm/java-6-openjdk-amd64/bin/java,使用java 6在主机host3上的当前用户。

运行多节点测试

要在sbt内部以多节点模式(即分发jar文件并远程启动测试)运行所有多节点测试,请使用multiNodeTest任务:

  1. multiNodeTest

要在多 JVM 模式(即本地计算机上的所有 JVM)下运行所有的 JVM,请执行以下操作:

  1. multi-jvm:test

要运行单个测试,请使用multiNodeTestOnly任务:

  1. multiNodeTestOnly your.MultiNodeTest

要在多 JVM 模式下运行单个测试,请执行以下操作:

  1. multi-jvm:testOnly your.MultiNodeTest

可以列出多个测试名称以运行多个特定测试。sbt中的制表符使完成测试名称变得容易。

多节点测试示例

首先,我们需要一些脚手架(scaffolding)来将MultiNodeSpec与你最喜欢的测试框架连接起来。让我们定义一个使用ScalaTest启动和停止MultiNodeSpec的特征STMultiNodeSpec

  1. package akka.remote.testkit
  2. import scala.language.implicitConversions
  3. import org.scalatest.{ BeforeAndAfterAll, WordSpecLike }
  4. import org.scalatest.Matchers
  5. /**
  6. * Hooks up MultiNodeSpec with ScalaTest
  7. */
  8. trait STMultiNodeSpec extends MultiNodeSpecCallbacks with WordSpecLike with Matchers with BeforeAndAfterAll {
  9. self: MultiNodeSpec =>
  10. override def beforeAll() = multiNodeSpecBeforeAll()
  11. override def afterAll() = multiNodeSpecAfterAll()
  12. // Might not be needed anymore if we find a nice way to tag all logging from a node
  13. override implicit def convertToWordSpecStringWrapper(s: String): WordSpecStringWrapper =
  14. new WordSpecStringWrapper(s"$s (on node '${self.myself.name}', $getClass)")
  15. }

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

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

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

  1. package akka.remote.sample
  2. import akka.actor.{ Actor, Props }
  3. import akka.remote.testkit.MultiNodeSpec
  4. import akka.testkit.ImplicitSender
  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) with STMultiNodeSpec with ImplicitSender {
  15. import MultiNodeSample._
  16. import MultiNodeSampleConfig._
  17. def initialParticipants = roles.size
  18. "A MultiNodeSample" must {
  19. "wait for all nodes to enter a barrier" in {
  20. enterBarrier("startup")
  21. }
  22. "send to and receive from a remote node" in {
  23. runOn(node1) {
  24. enterBarrier("deployed")
  25. val ponger = system.actorSelection(node(node2) / "user" / "ponger")
  26. ponger ! "ping"
  27. import scala.concurrent.duration._
  28. expectMsg(10.seconds, "pong")
  29. }
  30. runOn(node2) {
  31. system.actorOf(Props[Ponger], "ponger")
  32. enterBarrier("deployed")
  33. }
  34. enterBarrier("finished")
  35. }
  36. }
  37. }

自己运行这个示例的最简单方法是下载准备好的「Akka Multi-Node Testing Sample with Scala」以及教程,此示例的源代码可以在「Akka Samples Repository」中找到。

要记住的事情

在编写多节点测试时,需要记住一些事情,否则你的测试可能会以令人惊讶的方式运行。

  • 不要关闭第一个节点。第一个节点是控制器,如果它关闭,测试将中断。
  • 要使用blackholepassThroughthrottle,你必须通过在MultiNodeConfig中指定testTransport(on = true)来激活故障注入器和节流器传输适配器。
  • 节流、关闭和其他故障注入只能从第一个节点完成,这也是控制器。
  • 关闭节点后,不要使用node(address)请求节点的地址。在关闭节点之前获取地址。
  • 不要使用地址查找、屏障入口等来自主测试线程以外的其他线程的MultiNodeSpec方法。这也意味着你不应该在 Actor、futurescheduled的任务中使用它们。

配置

多节点测试模块有几个配置属性,请参考「配置」。


英文原文链接Multi Node Testing.