diff --git a/akka-docs/cluster/cluster-usage.rst b/akka-docs/cluster/cluster-usage.rst new file mode 100644 index 0000000000..b258b8eb8e --- /dev/null +++ b/akka-docs/cluster/cluster-usage.rst @@ -0,0 +1,84 @@ + +.. _cluster_usage: + +######### + Cluster +######### + +.. note:: *This document describes how to use the features implemented so far of the +new clustering coming in Akka Coltrane and is not available in the latest stable release. +The API might change before it is released. + +For introduction to the Akka Cluster concepts please see + +Preparing your ActorSystem for Clustering +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +The Akka cluster is a separate jar file. Make sure that you have the following dependency in your project:: + + "com.typesafe.akka" % "akka-cluster" % "2.1-SNAPSHOT" + +It can be difficult to find the correct versions and repositories at the moment. The following sbt 0.11.3 build +file illustrates what to use with Scala 2.10.0-M6 and Akka 2.1-SNAPSHOT + + import sbt._ + import sbt.Keys._ + + object ProjectBuild extends Build { + + lazy val root = Project( + id = "root", + base = file("."), + settings = Project.defaultSettings ++ Seq( + name := "Akka Cluster Example", + organization := "org.test", + version := "0.1-SNAPSHOT", + scalaVersion := "2.10.0-M6", + + resolvers += "Sonatype Releases Repo" at "https://oss.sonatype.org/content/repositories/releases/", + resolvers += "Sonatype Snapshot Repo" at "https://oss.sonatype.org/content/repositories/snapshots/", + resolvers += "Typesafe Releases" at "http://repo.typesafe.com/typesafe/releases", + resolvers += "Typesafe Snapshots" at "http://repo.typesafe.com/typesafe/snapshots/", + + + libraryDependencies ++= Seq( + "com.typesafe.akka" % "akka-cluster" % "2.1-20120816-000904", + "com.typesafe.akka" % "akka-testkit" % "2.1-20120816-000904" % "test", + "junit" % "junit" % "4.5" % "test", + "org.scalatest" %% "scalatest" % "1.9-2.10.0-M6-B2" % "test") + ) + ) + } + +Pick a timestamped Akka version from ``_. + +To enable cluster capabilities in your Akka project you should, at a minimum, add the :ref:`remoting-scala` +settings and the ``cluster seed-nodes`` to your ``application.conf`` file: + +.. literalinclude:: ../../akka-samples/akka-sample-remote/src/main/resources/common.conf + :language: none + +The seed nodes are configured contact points for inital join of the cluster. +When a new node is started started it sends a message to all seed nodes and +then sends join command to the one that answers first. + +A Simple Cluster Example +^^^^^^^^^^^^^^^^^^^^^^^^ + + + + +Configuration +^^^^^^^^^^^^^ + +There are lots of more properties that are related to clustering in Akka. We refer to the following +reference file for more information: + + +.. literalinclude:: ../../akka-cluster/src/main/resources/reference.conf + :language: none + + + + + diff --git a/akka-docs/cluster/index.rst b/akka-docs/cluster/index.rst index 35c4b2250a..dac3a558d9 100644 --- a/akka-docs/cluster/index.rst +++ b/akka-docs/cluster/index.rst @@ -5,3 +5,4 @@ Cluster :maxdepth: 2 cluster + cluster-usage diff --git a/akka-samples/akka-sample-cluster/README.rst b/akka-samples/akka-sample-cluster/README.rst new file mode 100644 index 0000000000..5f9a9fb6c6 --- /dev/null +++ b/akka-samples/akka-sample-cluster/README.rst @@ -0,0 +1,143 @@ +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 two implementations of the sample; one in Scala and one in Java. +The explanation below is for Scala, but everything is similar in Java except that the class names begin with a ``J``, +e.g. ``JCalcApp`` instead of ``CalcApp``, and that the Java classes reside in another package structure. + +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 + +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 actor system and thereafter the +CreationApplication will send messages to it. + +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 +------- + +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 `_ +* `SBT `_ diff --git a/akka-samples/akka-sample-cluster/src/main/resources/application.conf b/akka-samples/akka-sample-cluster/src/main/resources/application.conf new file mode 100644 index 0000000000..779b3825cd --- /dev/null +++ b/akka-samples/akka-sample-cluster/src/main/resources/application.conf @@ -0,0 +1,18 @@ +akka { + actor { + provider = "akka.remote.RemoteActorRefProvider" + } + remote { + transport = "akka.remote.netty.NettyRemoteTransport" + netty { + hostname = "127.0.0.1" + port = 0 + } + } + + extensions = ["akka.cluster.Cluster$"] + + cluster { + seed-nodes = ["akka://ClusterSystem@127.0.0.1:2551", "akka://ClusterSystem@127.0.0.1:2552"] + } +} \ No newline at end of file diff --git a/akka-samples/akka-sample-cluster/src/main/scala/sample/cluster/ClusterApp.scala b/akka-samples/akka-sample-cluster/src/main/scala/sample/cluster/ClusterApp.scala new file mode 100644 index 0000000000..e61af1db6a --- /dev/null +++ b/akka-samples/akka-sample-cluster/src/main/scala/sample/cluster/ClusterApp.scala @@ -0,0 +1,28 @@ +package sample.cluster + +import akka.actor._ +import akka.cluster.Cluster +import akka.cluster.ClusterEvent._ + +object ClusterApp { + + def main(args: Array[String]): Unit = { + + if (args.nonEmpty) System.setProperty("akka.remote.netty.port", args(0)) + + // Create an Akka system + val system = ActorSystem("ClusterSystem") + val clusterListener = system.actorOf(Props(new Actor { + def receive = { + case state: CurrentClusterState ⇒ + println("Current members: " + state.members) + case MembersChanged(members) ⇒ + println("Current members: " + members) + + } + })) + + Cluster(system).subscribe(clusterListener, classOf[MembersChanged]) + } + +} \ No newline at end of file diff --git a/project/AkkaBuild.scala b/project/AkkaBuild.scala index b7cfe35c52..8d62f30923 100644 --- a/project/AkkaBuild.scala +++ b/project/AkkaBuild.scala @@ -282,7 +282,7 @@ object AkkaBuild extends Build { id = "akka-samples", base = file("akka-samples"), settings = parentSettings, - aggregate = Seq(camelSample, fsmSample, helloSample, helloKernelSample, remoteSample) + aggregate = Seq(camelSample, fsmSample, helloSample, helloKernelSample, remoteSample, clusterSample) ) lazy val camelSample = Project( @@ -322,6 +322,13 @@ object AkkaBuild extends Build { settings = defaultSettings ) + lazy val clusterSample = Project( + id = "akka-sample-cluster", + base = file("akka-samples/akka-sample-cluster"), + dependencies = Seq(cluster), + settings = defaultSettings + ) + lazy val tutorials = Project( id = "akka-tutorials", base = file("akka-tutorials"),