From 06f9466aea8033b09161f10c6999cbfd8b701752 Mon Sep 17 00:00:00 2001 From: Henrik Engstrom Date: Thu, 22 Dec 2011 15:12:41 +0100 Subject: [PATCH 1/6] A sample application showcasing some of the remoting capabilities of Akka 2.0. See #1560 --- akka-samples/akka-sample-remote/README | 138 ++++++++++++++++++ .../src/main/resources/application.conf | 35 +++++ .../src/main/resources/common.conf | 14 ++ .../calculator/CalculatorApplication.scala | 38 +++++ .../calculator/CreationApplication.scala | 49 +++++++ .../remote/calculator/LookupApplication.scala | 49 +++++++ .../sample/remote/calculator/MathOp.scala | 37 +++++ project/AkkaBuild.scala | 9 +- 8 files changed, 368 insertions(+), 1 deletion(-) create mode 100644 akka-samples/akka-sample-remote/README create mode 100644 akka-samples/akka-sample-remote/src/main/resources/application.conf create mode 100644 akka-samples/akka-sample-remote/src/main/resources/common.conf create mode 100644 akka-samples/akka-sample-remote/src/main/scala/sample/remote/calculator/CalculatorApplication.scala create mode 100644 akka-samples/akka-sample-remote/src/main/scala/sample/remote/calculator/CreationApplication.scala create mode 100644 akka-samples/akka-sample-remote/src/main/scala/sample/remote/calculator/LookupApplication.scala create mode 100644 akka-samples/akka-sample-remote/src/main/scala/sample/remote/calculator/MathOp.scala diff --git a/akka-samples/akka-sample-remote/README b/akka-samples/akka-sample-remote/README new file mode 100644 index 0000000000..d82565dbe6 --- /dev/null +++ b/akka-samples/akka-sample-remote/README @@ -0,0 +1,138 @@ +REMOTE CALCULATOR +================= + +Requirements +------------ + +To build and run remote calculator you need [Simple Build Tool][sbt] (sbt). + +The Sample Explained +-------------------- + +In order to showcase the remote capabilities of Akka 2.0 we thought a remote calculator could do the trick. + +There are three actor systems (AS) used in the sample: +* CalculatorApplication : the AS performing the number crunching +* LookupApplication : illustrates how to look up an actor on a remote node and and how communicate with that actor +* CreationApplication : illustrates how to create an actor on a remote node and how to communicate with that actor + +The CalculatorApplication contains an actor, SimpleCalculatorActor, which can handle simple math operations such as +addition and subtraction. This actor is looked up and used from the LookupApplication. + +The CreationApplication wants to use more "advanced" mathematical operations, such as multiplication and division, +but as the CalculatorApplication does not have any actor that can perform those type of calculations the +CreationApplication has to remote deploy an actor that can (which in our case is AdvancedCalculatorActor). +So this actor is deployed, over the network, onto the CalculatorApplication AS and thereafter the CreationApplication +will send messages to it. + +It is important to point out that as the AS run on different ports it is possible to run all three in parallel. +See the next section for more information of how to run the sample application. + +Running +------- + +In order to run all three actor systems you have to start SBT in three different terminal windows. + +We start off by running the CalculatorApplication: + +First type 'sbt' to start SBT interactively, the run 'update' and 'run': +> cd $AKKA_HOME + +> sbt + +> project akka-sample-remote + +> run + +Select to run "sample.remote.calculator.CalcApp" which in the case below is number 3: + + Multiple main classes detected, select one to run: + + [1] sample.remote.calculator.LookupApp + [2] sample.remote.calculator.CreationApp + [3] sample.remote.calculator.CalcApp + + Enter number: 3 + +You should see something similar to this:: + + [info] Running sample.remote.calculator.CalcApp + [INFO] [12/22/2011 14:21:51.631] [run-main] [ActorSystem] REMOTE: RemoteServerStarted@akka://CalculatorApplication@127.0.0.1:2552 + [INFO] [12/22/2011 14:21:51.632] [run-main] [Remote] Starting remote server on [akka://CalculatorApplication@127.0.0.1:2552] + Started Calculator Application - waiting for messages + [INFO] [12/22/2011 14:22:39.894] [New I/O server worker #1-1] [ActorSystem] REMOTE: RemoteClientStarted@akka://127.0.0.1:2553 + +Open up a new terminal window and run SBT once more: + +> sbt + +> project akka-sample-remote + +> run + +Select to run "sample.remote.calculator.LookupApp" which in the case below is number 1: + + Multiple main classes detected, select one to run: + + [1] sample.remote.calculator.LookupApp + [2] sample.remote.calculator.CreationApp + [3] sample.remote.calculator.CalcApp + + Enter number: 1 + +Now you should see something like this: + + [info] Running sample.remote.calculator.LookupApp + [INFO] [12/22/2011 14:54:38.630] [run-main] [ActorSystem] REMOTE: RemoteServerStarted@akka://LookupApplication@127.0.0.1:2553 + [INFO] [12/22/2011 14:54:38.632] [run-main] [Remote] Starting remote server on [akka://LookupApplication@127.0.0.1:2553] + Started Lookup Application + [INFO] [12/22/2011 14:54:38.801] [default-dispatcher-21] [ActorSystem] REMOTE: RemoteClientStarted@akka://127.0.0.1:2552 + Sub result: 4 - 30 = -26 + Add result: 17 + 1 = 18 + Add result: 37 + 43 = 80 + Add result: 68 + 66 = 134 + +Congrats! You have now successfully looked up a remote actor and communicated with it. +The next step is to have an actor deployed on a remote note. +Once more you should open a new terminal window and run SBT: + +> sbt + +> project akka-sample-remote + +> run + +Select to run "sample.remote.calculator.CreationApp" which in the case below is number 2: + + Multiple main classes detected, select one to run: + + [1] sample.remote.calculator.LookupApp + [2] sample.remote.calculator.CreationApp + [3] sample.remote.calculator.CalcApp + + Enter number: 2 + +Now you should see something like this: + + [info] Running sample.remote.calculator.CreationApp + [INFO] [12/22/2011 14:57:02.150] [run-main] [ActorSystem] REMOTE: RemoteServerStarted@akka://RemoteCreation@127.0.0.1:2554 + [INFO] [12/22/2011 14:57:02.151] [run-main] [Remote] Starting remote server on [akka://RemoteCreation@127.0.0.1:2554] + [INFO] [12/22/2011 14:57:02.267] [default-dispatcher-21] [ActorSystem] REMOTE: RemoteClientStarted@akka://127.0.0.1:2552 + Started Creation Application + Mul result: 14 * 17 = 238 + Div result: 3764 / 80 = 47.00 + Mul result: 16 * 5 = 80 + Mul result: 1 * 18 = 18 + Mul result: 8 * 13 = 104 + +That's it! + +Notice +------ + +The sample application is just that, i.e. a sample. Parts of it are not the way you would do a "real" application. +Some improvements are to remove all hard coded addresses from the code as they reduce the flexibility of how and +where the application can be run. We leave this to the astute reader to refine the sample into a real-world app. + +[akka]: http://akka.io +[sbt]: http://code.google.com/p/simple-build-tool/ diff --git a/akka-samples/akka-sample-remote/src/main/resources/application.conf b/akka-samples/akka-sample-remote/src/main/resources/application.conf new file mode 100644 index 0000000000..c056dd4dfb --- /dev/null +++ b/akka-samples/akka-sample-remote/src/main/resources/application.conf @@ -0,0 +1,35 @@ +calculator { + include "common" + + akka { + remote.server.port = 2552 + cluster.nodename = "n1" + } +} + + +remotelookup { + include "common" + + akka { + remote.server.port = 2553 + cluster.nodename = "n2" + } +} + +remotecreation { + include "common" + + akka { + actor { + deployment { /advancedCalculator { + remote = "akka://CalculatorApplication@127.0.0.1:2552" + } + } + } + + remote.server.port = 2554 + cluster.nodename = "n3" + } +} + diff --git a/akka-samples/akka-sample-remote/src/main/resources/common.conf b/akka-samples/akka-sample-remote/src/main/resources/common.conf new file mode 100644 index 0000000000..79009c0aea --- /dev/null +++ b/akka-samples/akka-sample-remote/src/main/resources/common.conf @@ -0,0 +1,14 @@ +akka { + version = "2.0-SNAPSHOT" + + actor { + provider = "akka.remote.RemoteActorRefProvider" + } + + remote { + transport = "akka.remote.netty.NettyRemoteSupport" + server { + hostname = "127.0.0.1" + } + } +} diff --git a/akka-samples/akka-sample-remote/src/main/scala/sample/remote/calculator/CalculatorApplication.scala b/akka-samples/akka-sample-remote/src/main/scala/sample/remote/calculator/CalculatorApplication.scala new file mode 100644 index 0000000000..78b41bcb3a --- /dev/null +++ b/akka-samples/akka-sample-remote/src/main/scala/sample/remote/calculator/CalculatorApplication.scala @@ -0,0 +1,38 @@ +/** + * Copyright (C) 2009-2011 Typesafe Inc. + */ +package sample.remote.calculator + +import akka.kernel.Bootable +import akka.actor.{ Props, Actor, ActorSystem } +import com.typesafe.config.ConfigFactory + +class SimpleCalculatorActor extends Actor { + def receive = { + case Add(n1, n2) ⇒ + println("Calculating %d + %d".format(n1, n2)) + sender ! AddResult(n1, n2, n1 + n2) + case Subtract(n1, n2) ⇒ + println("Calculating %d - %d".format(n1, n2)) + sender ! SubtractResult(n1, n2, n1 - n2) + } +} + +class CalculatorApplication extends Bootable { + val system = ActorSystem("CalculatorApplication", ConfigFactory.load.getConfig("calculator")) + val actor = system.actorOf(Props[SimpleCalculatorActor], "simpleCalculator") + + def startup() { + } + + def shutdown() { + system.shutdown() + } +} + +object CalcApp { + def main(args: Array[String]) { + new CalculatorApplication + println("Started Calculator Application - waiting for messages") + } +} diff --git a/akka-samples/akka-sample-remote/src/main/scala/sample/remote/calculator/CreationApplication.scala b/akka-samples/akka-sample-remote/src/main/scala/sample/remote/calculator/CreationApplication.scala new file mode 100644 index 0000000000..e97f4f9dec --- /dev/null +++ b/akka-samples/akka-sample-remote/src/main/scala/sample/remote/calculator/CreationApplication.scala @@ -0,0 +1,49 @@ +/** + * Copyright (C) 2009-2011 Typesafe Inc. + */ +package sample.remote.calculator + +import akka.kernel.Bootable +import com.typesafe.config.ConfigFactory +import scala.util.Random +import akka.actor._ + +class CreationApplication extends Bootable { + val system = ActorSystem("RemoteCreation", ConfigFactory.load.getConfig("remotecreation")) + val localActor = system.actorOf(Props[CreationActor], "creationActor") + val remoteActor = system.actorOf(Props[AdvancedCalculatorActor], "advancedCalculator") + + def doSomething(op: MathOp) = { + localActor ! Tuple2(remoteActor, op) + } + + def startup() { + } + + def shutdown() { + system.shutdown() + } +} + +class CreationActor extends Actor { + def receive = { + case (actor: ActorRef, op: MathOp) ⇒ actor ! op + case result: MathResult ⇒ result match { + case MultiplicationResult(n1, n2, r) ⇒ println("Mul result: %d * %d = %d".format(n1, n2, r)) + case DivisionResult(n1, n2, r) ⇒ println("Div result: %.0f / %d = %.2f".format(n1, n2, r)) + } + } +} + +object CreationApp { + def main(args: Array[String]) { + val app = new CreationApplication + println("Started Creation Application") + while (true) { + if (Random.nextInt(100) % 2 == 0) app.doSomething(Multiply(Random.nextInt(20), Random.nextInt(20))) + else app.doSomething(Divide(Random.nextInt(10000), (Random.nextInt(99) + 1))) + + Thread.sleep(200) + } + } +} diff --git a/akka-samples/akka-sample-remote/src/main/scala/sample/remote/calculator/LookupApplication.scala b/akka-samples/akka-sample-remote/src/main/scala/sample/remote/calculator/LookupApplication.scala new file mode 100644 index 0000000000..e4b6ccc825 --- /dev/null +++ b/akka-samples/akka-sample-remote/src/main/scala/sample/remote/calculator/LookupApplication.scala @@ -0,0 +1,49 @@ +/** + * Copyright (C) 2009-2011 Typesafe Inc. + */ +package sample.remote.calculator + +import akka.kernel.Bootable +import com.typesafe.config.ConfigFactory +import scala.util.Random +import akka.actor.{ ActorRef, Props, Actor, ActorSystem } + +class LookupApplication extends Bootable { + val system = ActorSystem("LookupApplication", ConfigFactory.load.getConfig("remotelookup")) + val actor = system.actorOf(Props[LookupActor], "lookupActor") + val remoteActor = system.actorFor("akka://CalculatorApplication@127.0.0.1:2552/user/simpleCalculator") + + def doSomething(op: MathOp) = { + actor ! Tuple2(remoteActor, op) + } + + def startup() { + } + + def shutdown() { + system.shutdown() + } +} + +class LookupActor extends Actor { + def receive = { + case (actor: ActorRef, op: MathOp) ⇒ actor ! op + case result: MathResult ⇒ result match { + case AddResult(n1, n2, r) ⇒ println("Add result: %d + %d = %d".format(n1, n2, r)) + case SubtractResult(n1, n2, r) ⇒ println("Sub result: %d - %d = %d".format(n1, n2, r)) + } + } +} + +object LookupApp { + def main(args: Array[String]) { + val app = new LookupApplication + println("Started Lookup Application") + while (true) { + if (Random.nextInt(100) % 2 == 0) app.doSomething(Add(Random.nextInt(100), Random.nextInt(100))) + else app.doSomething(Subtract(Random.nextInt(100), Random.nextInt(100))) + + Thread.sleep(200) + } + } +} diff --git a/akka-samples/akka-sample-remote/src/main/scala/sample/remote/calculator/MathOp.scala b/akka-samples/akka-sample-remote/src/main/scala/sample/remote/calculator/MathOp.scala new file mode 100644 index 0000000000..5dde9625a9 --- /dev/null +++ b/akka-samples/akka-sample-remote/src/main/scala/sample/remote/calculator/MathOp.scala @@ -0,0 +1,37 @@ +/** + * Copyright (C) 2009-2011 Typesafe Inc. + */ +package sample.remote.calculator + +import akka.actor.Actor + +trait MathOp + +case class Add(nbr1: Int, nbr2: Int) extends MathOp + +case class Subtract(nbr1: Int, nbr2: Int) extends MathOp + +case class Multiply(nbr1: Int, nbr2: Int) extends MathOp + +case class Divide(nbr1: Int, nbr2: Int) extends MathOp + +trait MathResult + +case class AddResult(nbr: Int, nbr2: Int, result: Int) extends MathResult + +case class SubtractResult(nbr1: Int, nbr2: Int, result: Int) extends MathResult + +case class MultiplicationResult(nbr1: Int, nbr2: Int, result: Int) extends MathResult + +case class DivisionResult(nbr1: Double, nbr2: Int, result: Double) extends MathResult + +class AdvancedCalculatorActor extends Actor { + def receive = { + case Multiply(n1, n2) ⇒ + println("Calculating %d * %d".format(n1, n2)) + sender ! MultiplicationResult(n1, n2, n1 * n2) + case Divide(n1, n2) ⇒ + println("Calculating %d / %d".format(n1, n2)) + sender ! DivisionResult(n1, n2, n1 / n2) + } +} \ No newline at end of file diff --git a/project/AkkaBuild.scala b/project/AkkaBuild.scala index 0138db65c5..0845cf2c60 100644 --- a/project/AkkaBuild.scala +++ b/project/AkkaBuild.scala @@ -224,7 +224,7 @@ object AkkaBuild extends Build { id = "akka-samples", base = file("akka-samples"), settings = parentSettings, - aggregate = Seq(fsmSample, helloSample, helloKernelSample) + aggregate = Seq(fsmSample, helloSample, helloKernelSample, remoteSample) ) lazy val fsmSample = Project( @@ -248,6 +248,13 @@ object AkkaBuild extends Build { settings = defaultSettings ) + lazy val remoteSample = Project( + id = "akka-sample-remote", + base = file("akka-samples/akka-sample-remote"), + dependencies = Seq(actor, remote, kernel), + settings = defaultSettings + ) + lazy val tutorials = Project( id = "akka-tutorials", base = file("akka-tutorials"), From 7ade7e817919d0a63f676bf8fe261c19e72046b6 Mon Sep 17 00:00:00 2001 From: Henrik Engstrom Date: Thu, 22 Dec 2011 16:15:59 +0100 Subject: [PATCH 2/6] Minor fixes to the sample. See #1560 --- akka-samples/akka-sample-remote/README | 4 ++-- .../scala/sample/remote/calculator/CreationApplication.scala | 2 +- .../scala/sample/remote/calculator/LookupApplication.scala | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/akka-samples/akka-sample-remote/README b/akka-samples/akka-sample-remote/README index d82565dbe6..98a46bd343 100644 --- a/akka-samples/akka-sample-remote/README +++ b/akka-samples/akka-sample-remote/README @@ -11,8 +11,8 @@ The Sample Explained In order to showcase the remote capabilities of Akka 2.0 we thought a remote calculator could do the trick. -There are three actor systems (AS) used in the sample: -* CalculatorApplication : the AS performing the number crunching +There are three actor systems used in the sample: +* CalculatorApplication : the actor system performing the number crunching * LookupApplication : illustrates how to look up an actor on a remote node and and how communicate with that actor * CreationApplication : illustrates how to create an actor on a remote node and how to communicate with that actor diff --git a/akka-samples/akka-sample-remote/src/main/scala/sample/remote/calculator/CreationApplication.scala b/akka-samples/akka-sample-remote/src/main/scala/sample/remote/calculator/CreationApplication.scala index e97f4f9dec..dac7cbe757 100644 --- a/akka-samples/akka-sample-remote/src/main/scala/sample/remote/calculator/CreationApplication.scala +++ b/akka-samples/akka-sample-remote/src/main/scala/sample/remote/calculator/CreationApplication.scala @@ -14,7 +14,7 @@ class CreationApplication extends Bootable { val remoteActor = system.actorOf(Props[AdvancedCalculatorActor], "advancedCalculator") def doSomething(op: MathOp) = { - localActor ! Tuple2(remoteActor, op) + localActor ! (remoteActor, op) } def startup() { diff --git a/akka-samples/akka-sample-remote/src/main/scala/sample/remote/calculator/LookupApplication.scala b/akka-samples/akka-sample-remote/src/main/scala/sample/remote/calculator/LookupApplication.scala index e4b6ccc825..2439a90f7d 100644 --- a/akka-samples/akka-sample-remote/src/main/scala/sample/remote/calculator/LookupApplication.scala +++ b/akka-samples/akka-sample-remote/src/main/scala/sample/remote/calculator/LookupApplication.scala @@ -14,7 +14,7 @@ class LookupApplication extends Bootable { val remoteActor = system.actorFor("akka://CalculatorApplication@127.0.0.1:2552/user/simpleCalculator") def doSomething(op: MathOp) = { - actor ! Tuple2(remoteActor, op) + actor ! (remoteActor, op) } def startup() { From d9d753ac599134a53419e631ee12ad4f93b783d9 Mon Sep 17 00:00:00 2001 From: Henrik Engstrom Date: Thu, 22 Dec 2011 16:21:20 +0100 Subject: [PATCH 3/6] Removed more abbreviations. See #1560 --- akka-samples/akka-sample-remote/README | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/akka-samples/akka-sample-remote/README b/akka-samples/akka-sample-remote/README index 98a46bd343..24f63ccc86 100644 --- a/akka-samples/akka-sample-remote/README +++ b/akka-samples/akka-sample-remote/README @@ -22,10 +22,10 @@ addition and subtraction. This actor is looked up and used from the LookupApplic The CreationApplication wants to use more "advanced" mathematical operations, such as multiplication and division, but as the CalculatorApplication does not have any actor that can perform those type of calculations the CreationApplication has to remote deploy an actor that can (which in our case is AdvancedCalculatorActor). -So this actor is deployed, over the network, onto the CalculatorApplication AS and thereafter the CreationApplication -will send messages to it. +So this actor is deployed, over the network, onto the CalculatorApplication actor system and thereafter the +CreationApplication will send messages to it. -It is important to point out that as the AS run on different ports it is possible to run all three in parallel. +It is important to point out that as the actor system run on different ports it is possible to run all three in parallel. See the next section for more information of how to run the sample application. Running From e160179aa91c54284c301a7bea64f96c681f3610 Mon Sep 17 00:00:00 2001 From: Henrik Engstrom Date: Thu, 22 Dec 2011 16:31:30 +0100 Subject: [PATCH 4/6] Changed file ending to .rst. See #1560 --- akka-samples/akka-sample-remote/{README => README.rst} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename akka-samples/akka-sample-remote/{README => README.rst} (100%) diff --git a/akka-samples/akka-sample-remote/README b/akka-samples/akka-sample-remote/README.rst similarity index 100% rename from akka-samples/akka-sample-remote/README rename to akka-samples/akka-sample-remote/README.rst From d695ef52c0705b13a50560d472cf6b30ba8dcddc Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Fri, 23 Dec 2011 21:01:33 +0100 Subject: [PATCH 5/6] System property for parallelExecution, use -Dakka.parallelExecution=false in sbt launch script to run build/tests sequential --- project/AkkaBuild.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/project/AkkaBuild.scala b/project/AkkaBuild.scala index 0845cf2c60..dd3f978a5f 100644 --- a/project/AkkaBuild.scala +++ b/project/AkkaBuild.scala @@ -25,7 +25,7 @@ object AkkaBuild extends Build { id = "akka", base = file("."), settings = parentSettings ++ Release.settings ++ Unidoc.settings ++ Rstdoc.settings ++ Publish.versionSettings ++ Dist.settings ++ Seq( - parallelExecution in GlobalScope := true, + parallelExecution in GlobalScope := System.getProperty("akka.parallelExecution", "true").toBoolean, Publish.defaultPublishTo in ThisBuild <<= crossTarget / "repository", Unidoc.unidocExclude := Seq(samples.id, tutorials.id), Dist.distExclude := Seq(actorTests.id, akkaSbtPlugin.id, docs.id) @@ -318,7 +318,7 @@ object AkkaBuild extends Build { unmanagedClasspath in Runtime <+= (baseDirectory in LocalProject("akka")) map { base => Attributed.blank(base / "config") }, unmanagedClasspath in Test <+= (baseDirectory in LocalProject("akka")) map { base => Attributed.blank(base / "config") }, - parallelExecution in Test := true, + parallelExecution in Test := System.getProperty("akka.parallelExecution", "true").toBoolean, // for excluding tests by name (or use system property: -Dakka.test.names.exclude=TimingSpec) excludeTestNames := { From 9749afd72e2872a75a4c78c67259e2ad420ca8a6 Mon Sep 17 00:00:00 2001 From: Patrik Nordwall Date: Fri, 23 Dec 2011 21:26:44 +0100 Subject: [PATCH 6/6] Removed global counter, which sometimes caused test failures when running tests in parallel. * Used extension to scope the counter by actor system --- .../trading/domain/Orderbook.scala | 9 +++++--- .../trading/domain/TradeObserver.scala | 22 ++++++++++++++++--- .../TradingLatencyPerformanceSpec.scala | 7 ++++-- .../trading/system/TradingSystem.scala | 10 +++++---- .../TradingThroughputPerformanceSpec.scala | 7 ++++-- .../workbench/BenchResultRepository.scala | 10 ++++----- 6 files changed, 46 insertions(+), 19 deletions(-) diff --git a/akka-actor-tests/src/test/scala/akka/performance/trading/domain/Orderbook.scala b/akka-actor-tests/src/test/scala/akka/performance/trading/domain/Orderbook.scala index b7188b5c7f..d2e152bb4b 100644 --- a/akka-actor-tests/src/test/scala/akka/performance/trading/domain/Orderbook.scala +++ b/akka-actor-tests/src/test/scala/akka/performance/trading/domain/Orderbook.scala @@ -1,5 +1,6 @@ package akka.performance.trading.domain import akka.performance.workbench.BenchmarkConfig +import akka.actor.ActorSystem abstract class Orderbook(val symbol: String) { var bidSide: List[Bid] = Nil @@ -52,10 +53,12 @@ object Orderbook { val useDummyOrderbook = BenchmarkConfig.config.getBoolean("benchmark.useDummyOrderbook") - def apply(symbol: String, standby: Boolean): Orderbook = (useDummyOrderbook, standby) match { + def apply(symbol: String, standby: Boolean, _system: ActorSystem): Orderbook = (useDummyOrderbook, standby) match { case (false, false) ⇒ new Orderbook(symbol) with NopTradeObserver - case (false, true) ⇒ new Orderbook(symbol) with TotalTradeObserver - case (true, _) ⇒ new DummyOrderbook(symbol) with NopTradeObserver + case (false, true) ⇒ new Orderbook(symbol) with TotalTradeObserver { + override def system = _system + } + case (true, _) ⇒ new DummyOrderbook(symbol) with NopTradeObserver } } diff --git a/akka-actor-tests/src/test/scala/akka/performance/trading/domain/TradeObserver.scala b/akka-actor-tests/src/test/scala/akka/performance/trading/domain/TradeObserver.scala index c7de6ddcc0..be867121f5 100644 --- a/akka-actor-tests/src/test/scala/akka/performance/trading/domain/TradeObserver.scala +++ b/akka-actor-tests/src/test/scala/akka/performance/trading/domain/TradeObserver.scala @@ -1,14 +1,21 @@ package akka.performance.trading.domain import java.util.concurrent.atomic.AtomicInteger +import akka.actor.Extension +import akka.actor.ExtensionId +import akka.actor.ExtensionIdProvider +import akka.actor.ActorSystemImpl +import akka.actor.ActorSystem abstract trait TradeObserver { def trade(bid: Bid, ask: Ask) } trait TotalTradeObserver extends TradeObserver { + def system: ActorSystem + private lazy val counter: TotalTradeCounter = TotalTradeCounterExtension(system) override def trade(bid: Bid, ask: Ask) { - TotalTradeCounter.counter.incrementAndGet + counter.increment() } } @@ -17,10 +24,19 @@ trait NopTradeObserver extends TradeObserver { } } -object TotalTradeCounter { - val counter = new AtomicInteger +class TotalTradeCounter extends Extension { + private val counter = new AtomicInteger + def increment() = counter.incrementAndGet() def reset() { counter.set(0) } + def count: Int = counter.get } + +object TotalTradeCounterExtension + extends ExtensionId[TotalTradeCounter] + with ExtensionIdProvider { + override def lookup = TotalTradeCounterExtension + override def createExtension(system: ActorSystemImpl) = new TotalTradeCounter +} \ No newline at end of file diff --git a/akka-actor-tests/src/test/scala/akka/performance/trading/system/TradingLatencyPerformanceSpec.scala b/akka-actor-tests/src/test/scala/akka/performance/trading/system/TradingLatencyPerformanceSpec.scala index 5458186b5a..9ba77e71e8 100644 --- a/akka-actor-tests/src/test/scala/akka/performance/trading/system/TradingLatencyPerformanceSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/performance/trading/system/TradingLatencyPerformanceSpec.scala @@ -15,6 +15,7 @@ import akka.performance.trading.domain.Order import akka.performance.trading.domain.TotalTradeCounter import akka.performance.workbench.PerformanceSpec import akka.performance.trading.domain.Orderbook +import akka.performance.trading.domain.TotalTradeCounterExtension // -server -Xms512M -Xmx1024M -XX:+UseConcMarkSweepGC -Dbenchmark=true -Dbenchmark.repeatFactor=500 -Dbenchmark.useDummyOrderbook=true @org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner]) @@ -25,12 +26,14 @@ class TradingLatencyPerformanceSpec extends PerformanceSpec { var stat: DescriptiveStatistics = _ val random: Random = new Random(0) + def totalTradeCounter: TotalTradeCounter = TotalTradeCounterExtension(system) + override def beforeEach() { super.beforeEach() stat = new SynchronizedDescriptiveStatistics tradingSystem = new AkkaTradingSystem(system) tradingSystem.start() - TotalTradeCounter.reset() + totalTradeCounter.reset() stat = new SynchronizedDescriptiveStatistics } @@ -101,7 +104,7 @@ class TradingLatencyPerformanceSpec extends PerformanceSpec { if (!warmup) { ok must be(true) if (!Orderbook.useDummyOrderbook) { - TotalTradeCounter.counter.get must be(totalNumberOfOrders / 2) + totalTradeCounter.count must be(totalNumberOfOrders / 2) } logMeasurement(numberOfClients, durationNs, stat) } diff --git a/akka-actor-tests/src/test/scala/akka/performance/trading/system/TradingSystem.scala b/akka-actor-tests/src/test/scala/akka/performance/trading/system/TradingSystem.scala index 89f17198fe..7fe2783a9a 100644 --- a/akka-actor-tests/src/test/scala/akka/performance/trading/system/TradingSystem.scala +++ b/akka-actor-tests/src/test/scala/akka/performance/trading/system/TradingSystem.scala @@ -13,9 +13,7 @@ trait TradingSystem { val allOrderbookSymbols: List[String] = OrderbookRepository.allOrderbookSymbols - val orderbooksGroupedByMatchingEngine: List[List[Orderbook]] = - for (groupOfSymbols: List[String] ← OrderbookRepository.orderbookSymbolsGroupedByMatchingEngine) - yield groupOfSymbols map (s ⇒ Orderbook(s, false)) + def orderbooksGroupedByMatchingEngine: List[List[Orderbook]] def useStandByEngines: Boolean = true @@ -47,6 +45,10 @@ class AkkaTradingSystem(val system: ActorSystem) extends TradingSystem { // by default we use default-dispatcher def matchingEngineDispatcher: Option[String] = None + override val orderbooksGroupedByMatchingEngine: List[List[Orderbook]] = + for (groupOfSymbols: List[String] ← OrderbookRepository.orderbookSymbolsGroupedByMatchingEngine) + yield groupOfSymbols map (s ⇒ Orderbook(s, false, system)) + var matchingEngineForOrderbook: Map[String, ActorRef] = Map() override def createMatchingEngines: List[MatchingEngineInfo] = { @@ -55,7 +57,7 @@ class AkkaTradingSystem(val system: ActorSystem) extends TradingSystem { n = i + 1 } yield { val me = createMatchingEngine("ME" + n, orderbooks) - val orderbooksCopy = orderbooks map (o ⇒ Orderbook(o.symbol, true)) + val orderbooksCopy = orderbooks map (o ⇒ Orderbook(o.symbol, true, system)) val standbyOption = if (useStandByEngines) { val meStandby = createMatchingEngine("ME" + n + "s", orderbooksCopy) diff --git a/akka-actor-tests/src/test/scala/akka/performance/trading/system/TradingThroughputPerformanceSpec.scala b/akka-actor-tests/src/test/scala/akka/performance/trading/system/TradingThroughputPerformanceSpec.scala index 2a4503d68d..7092f87666 100644 --- a/akka-actor-tests/src/test/scala/akka/performance/trading/system/TradingThroughputPerformanceSpec.scala +++ b/akka-actor-tests/src/test/scala/akka/performance/trading/system/TradingThroughputPerformanceSpec.scala @@ -15,6 +15,7 @@ import akka.performance.trading.domain.Order import akka.performance.trading.domain.TotalTradeCounter import akka.performance.workbench.PerformanceSpec import akka.performance.trading.domain.Orderbook +import akka.performance.trading.domain.TotalTradeCounterExtension // -server -Xms512M -Xmx1024M -XX:+UseParallelGC -Dbenchmark=true -Dbenchmark.repeatFactor=500 -Dbenchmark.useDummyOrderbook=true @org.junit.runner.RunWith(classOf[org.scalatest.junit.JUnitRunner]) @@ -22,11 +23,13 @@ class TradingThroughputPerformanceSpec extends PerformanceSpec { var tradingSystem: AkkaTradingSystem = _ + def totalTradeCounter: TotalTradeCounter = TotalTradeCounterExtension(system) + override def beforeEach() { super.beforeEach() tradingSystem = new AkkaTradingSystem(system) tradingSystem.start() - TotalTradeCounter.reset() + totalTradeCounter.reset() } override def afterEach() { @@ -98,7 +101,7 @@ class TradingThroughputPerformanceSpec extends PerformanceSpec { if (!warmup) { ok must be(true) if (!Orderbook.useDummyOrderbook) { - TotalTradeCounter.counter.get must be(totalNumberOfOrders / 2) + totalTradeCounter.count must be(totalNumberOfOrders / 2) } logMeasurement(numberOfClients, durationNs, totalNumberOfOrders) } diff --git a/akka-actor-tests/src/test/scala/akka/performance/workbench/BenchResultRepository.scala b/akka-actor-tests/src/test/scala/akka/performance/workbench/BenchResultRepository.scala index 3263d6a57a..e0e6b0e1e2 100644 --- a/akka-actor-tests/src/test/scala/akka/performance/workbench/BenchResultRepository.scala +++ b/akka-actor-tests/src/test/scala/akka/performance/workbench/BenchResultRepository.scala @@ -50,25 +50,25 @@ class FileBenchResultRepository extends BenchResultRepository { case class Key(name: String, load: Int) - def add(stats: Stats) { + def add(stats: Stats): Unit = synchronized { val values = statsByName.getOrElseUpdate(stats.name, IndexedSeq.empty) statsByName(stats.name) = values :+ stats save(stats) } - def get(name: String): Seq[Stats] = { + def get(name: String): Seq[Stats] = synchronized { statsByName.getOrElse(name, IndexedSeq.empty) } - def get(name: String, load: Int): Option[Stats] = { + def get(name: String, load: Int): Option[Stats] = synchronized { get(name).find(_.load == load) } - def isBaseline(stats: Stats): Boolean = { + def isBaseline(stats: Stats): Boolean = synchronized { baselineStats.get(Key(stats.name, stats.load)) == Some(stats) } - def getWithHistorical(name: String, load: Int): Seq[Stats] = { + def getWithHistorical(name: String, load: Int): Seq[Stats] = synchronized { val key = Key(name, load) val historical = historicalStats.getOrElse(key, IndexedSeq.empty) val baseline = baselineStats.get(key)