First part of usage doc for cluster, see #1916
* Sample app, akka-sample-cluster * Preparing Your Project for Clustering * A Simple Cluster Example * Automatic vs. Manual Joining * Automatic vs. Manual Downing * Configuration
This commit is contained in:
parent
f6079de381
commit
a237058b64
4 changed files with 208 additions and 7 deletions
196
akka-docs/cluster/cluster-usage.rst
Normal file
196
akka-docs/cluster/cluster-usage.rst
Normal file
|
|
@ -0,0 +1,196 @@
|
||||||
|
|
||||||
|
.. _cluster_usage:
|
||||||
|
|
||||||
|
###############
|
||||||
|
Cluster Usage
|
||||||
|
###############
|
||||||
|
|
||||||
|
.. 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 :ref:`cluster`.
|
||||||
|
|
||||||
|
Preparing Your Project 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"
|
||||||
|
|
||||||
|
Instead of 2.1-SNAPSHOT you should pick a timestamped Akka version from `<http://repo.typesafe.com/typesafe/snapshots/com/typesafe/akka/akka-cluster/>`_.
|
||||||
|
|
||||||
|
The following sbt build file illustrates what to use with Scala 2.10.0-M7 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-M7",
|
||||||
|
|
||||||
|
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-20120822-000942")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Pick a timestamped Akka version from `<http://repo.typesafe.com/typesafe/snapshots/com/typesafe/akka/akka-cluster/>`_.
|
||||||
|
|
||||||
|
|
||||||
|
A Simple Cluster Example
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
The following small program together with its configuration starts an ``ActorSystem``
|
||||||
|
with the Cluster extension enabled. It joins the cluster and logs some membership events.
|
||||||
|
|
||||||
|
Try it out:
|
||||||
|
|
||||||
|
1. Add the following ``application.conf`` in your project, place it in ``src/main/resources``:
|
||||||
|
|
||||||
|
|
||||||
|
.. literalinclude:: ../../akka-samples/akka-sample-cluster/src/main/resources/application.conf
|
||||||
|
:language: none
|
||||||
|
|
||||||
|
To enable cluster capabilities in your Akka project you should, at a minimum, add the :ref:`remoting-scala`
|
||||||
|
settings and the ``akka.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 initial, automatic, join of the cluster.
|
||||||
|
|
||||||
|
Note that if you are going to starting the nodes on different machines you need to specify the
|
||||||
|
ip-addresses or host names of the machines in ``application.conf`` instead of ``127.0.0.1``
|
||||||
|
|
||||||
|
2. Add the following main program to your project, place it in ``src/main/scala``:
|
||||||
|
|
||||||
|
.. literalinclude:: ../../akka-samples/akka-sample-cluster/src/main/scala/sample/cluster/ClusterApp.scala
|
||||||
|
:language: scala
|
||||||
|
|
||||||
|
|
||||||
|
3. Start the first seed node. Open a sbt session in one terminal window and run::
|
||||||
|
|
||||||
|
run-main sample.cluster.ClusterApp 2551
|
||||||
|
|
||||||
|
2551 corresponds to the port of the first seed-nodes element in the configuration.
|
||||||
|
In the log output you see that the cluster node has been started and changed status to Up.
|
||||||
|
|
||||||
|
4. Start the second seed node. Open a sbt session in another terminal window and run::
|
||||||
|
|
||||||
|
run-main sample.cluster.ClusterApp 2552
|
||||||
|
|
||||||
|
|
||||||
|
2552 corresponds to the port of the first seed-nodes element in the configuration.
|
||||||
|
In the log output you see that the cluster node has been started and joins the other seed node
|
||||||
|
and becomes a member of the cluster. It's status changed to Up.
|
||||||
|
|
||||||
|
Switch over to the first terminal window and see in the log output that the member joined.
|
||||||
|
|
||||||
|
5. Start another node. Open a sbt session in yet another terminal window and run::
|
||||||
|
|
||||||
|
run-main sample.cluster.ClusterApp
|
||||||
|
|
||||||
|
Now you don't need to specify the port number, and it will use a random available port.
|
||||||
|
It joins one of the configured seed nodes. Look at the log output in the different terminal
|
||||||
|
windows.
|
||||||
|
|
||||||
|
Start even more nodes in the same way, if you like.
|
||||||
|
|
||||||
|
6. Shut down one of the nodes by pressing 'ctrl-c' in one of the terminal windows.
|
||||||
|
The other nodes will detect the failure after a while, which you can see in the log
|
||||||
|
output in the other terminals.
|
||||||
|
|
||||||
|
Look at the source code of the program again. What it does is to create an actor
|
||||||
|
and register it as subscriber of certain cluster events. It gets notified with
|
||||||
|
an snapshot event, 'CurrentClusterState' that holds full state information of
|
||||||
|
the cluster. After that it receives events for changes that happen in the cluster.
|
||||||
|
|
||||||
|
Automatic vs. Manual Joining
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
You may decide if joining to the cluster should be done automatically or manually.
|
||||||
|
By default it is automatic and you need to define the seed nodes in configuration
|
||||||
|
so that a new node has an initial contact point. When a new node is started it
|
||||||
|
sends a message to all seed nodes and then sends join command to the one that
|
||||||
|
answers first. If no one of the seed nodes replied (might not be started yet)
|
||||||
|
it retries this procedure until successful or shutdown.
|
||||||
|
|
||||||
|
There is one thing to be aware of regarding the seed node configured as the
|
||||||
|
first element in the ``seed-nodes`` configuration list.
|
||||||
|
The seed nodes can be started in any order and it is not necessary to have all
|
||||||
|
seed nodes running, but the first seed node must be started when initially
|
||||||
|
starting a cluster, otherwise the other seed-nodes will not become initialized
|
||||||
|
and no other node can join the cluster. Once more than two seed nodes have been
|
||||||
|
started it is no problem to shut down the first seed node. If it goes down it
|
||||||
|
must be manually joined to the cluster again.
|
||||||
|
Automatic joining of the first seed node is not possible, it would only join
|
||||||
|
itself. It is only the first seed node that has this restriction.
|
||||||
|
|
||||||
|
You can disable automatic joining with configuration:
|
||||||
|
|
||||||
|
akka.cluster.auto-join = off
|
||||||
|
|
||||||
|
Then you need to join manually, using JMX or the provided script.
|
||||||
|
You can join to any node in the cluster. It doesn't have to be configured as
|
||||||
|
seed node. If you are not using auto-join there is no need to configure
|
||||||
|
seed nodes at all.
|
||||||
|
|
||||||
|
Joining can also be performed programatically with ``Cluster(system).join``.
|
||||||
|
|
||||||
|
|
||||||
|
Automatic vs. Manual Downing
|
||||||
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
When a member is considered by the failure detector to be unreachable the
|
||||||
|
leader is not allowed to perform its duties, such as changing status of
|
||||||
|
new joining members to Up. The status of the unreachable member must be
|
||||||
|
changed to Down. This can be performed automatically or manually. By
|
||||||
|
default it must be done manually, using using JMX or the provided script.
|
||||||
|
|
||||||
|
It can also be performed programatically with ``Cluster(system).down``.
|
||||||
|
|
||||||
|
You can enable automatic downing with configuration:
|
||||||
|
|
||||||
|
akka.cluster.auto-down = on
|
||||||
|
|
||||||
|
Be aware of that using auto-down implies that two separate clusters will
|
||||||
|
automatically be formed in case of network partition. That might be
|
||||||
|
desired by some applications but not by others.
|
||||||
|
|
||||||
|
Configuration
|
||||||
|
^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
There are several configuration properties for the cluster. We refer to the following
|
||||||
|
reference file for more information:
|
||||||
|
|
||||||
|
|
||||||
|
.. literalinclude:: ../../akka-cluster/src/main/resources/reference.conf
|
||||||
|
:language: none
|
||||||
|
|
||||||
|
It is recommended that you change the ``tick-duration`` to 33 ms or less
|
||||||
|
of the default scheduler when using cluster, if you don't need to have it
|
||||||
|
configured to a longer duration for other reasons. If you don't do this
|
||||||
|
a dedicated scheduler will be used for periodic tasks of the cluster, which
|
||||||
|
introduce the extra overhead of another thread.
|
||||||
|
|
||||||
|
::
|
||||||
|
|
||||||
|
# shorter tick-duration of default scheduler when using cluster
|
||||||
|
akka.scheduler.tick-duration.tick-duration = 33ms
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -5,3 +5,4 @@ Cluster
|
||||||
:maxdepth: 2
|
:maxdepth: 2
|
||||||
|
|
||||||
cluster
|
cluster
|
||||||
|
cluster-usage
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,8 @@ akka {
|
||||||
extensions = ["akka.cluster.Cluster$"]
|
extensions = ["akka.cluster.Cluster$"]
|
||||||
|
|
||||||
cluster {
|
cluster {
|
||||||
seed-nodes = ["akka://ClusterSystem@127.0.0.1:2551", "akka://ClusterSystem@127.0.0.1:2552"]
|
seed-nodes = [
|
||||||
|
"akka://ClusterSystem@127.0.0.1:2551",
|
||||||
|
"akka://ClusterSystem@127.0.0.1:2552"]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -8,24 +8,26 @@ object ClusterApp {
|
||||||
|
|
||||||
def main(args: Array[String]): Unit = {
|
def main(args: Array[String]): Unit = {
|
||||||
|
|
||||||
|
// Override the configuration of the port
|
||||||
|
// when specified as program argument
|
||||||
if (args.nonEmpty) System.setProperty("akka.remote.netty.port", args(0))
|
if (args.nonEmpty) System.setProperty("akka.remote.netty.port", args(0))
|
||||||
|
|
||||||
// Create an Akka system
|
// Create an Akka system
|
||||||
val system = ActorSystem("ClusterSystem")
|
val system = ActorSystem("ClusterSystem")
|
||||||
val clusterListener = system.actorOf(Props(new Actor {
|
val clusterListener = system.actorOf(Props(new Actor with ActorLogging {
|
||||||
def receive = {
|
def receive = {
|
||||||
case state: CurrentClusterState ⇒
|
case state: CurrentClusterState ⇒
|
||||||
println("Current members: " + state.members)
|
log.info("Current members: {}", state.members)
|
||||||
case MemberJoined(member) ⇒
|
case MemberJoined(member) ⇒
|
||||||
println("Member joined: " + member)
|
log.info("Member joined: {}", member)
|
||||||
case MemberUp(member) ⇒
|
case MemberUp(member) ⇒
|
||||||
println("Member is Up: " + member)
|
log.info("Member is Up: {}", member)
|
||||||
case MemberUnreachable(member) ⇒
|
case MemberUnreachable(member) ⇒
|
||||||
println("Member detected as unreachable: " + member)
|
log.info("Member detected as unreachable: {}", member)
|
||||||
case _ ⇒ // ignore
|
case _ ⇒ // ignore
|
||||||
|
|
||||||
}
|
}
|
||||||
}))
|
}), name = "clusterListener")
|
||||||
|
|
||||||
Cluster(system).subscribe(clusterListener, classOf[ClusterDomainEvent])
|
Cluster(system).subscribe(clusterListener, classOf[ClusterDomainEvent])
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue