From ecc949441a5e6439435c0b5ad135a0655e122ba0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Bone=CC=81r?= Date: Sat, 14 Apr 2012 20:06:03 +0200 Subject: [PATCH] Initial commit of 'akka-cluster' CLI tool working together with the new Cluster JMX API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Jonas Bonér --- .../src/main/scala/akka/cluster/Cluster.scala | 66 +++++- .../scala/akka/cluster/ClusterMBeanSpec.scala | 61 ++++++ akka-kernel/src/main/dist/bin/akka-cluster | 205 ++++++++++++++++++ .../dist/lib/cmdline-jmxclient-0.10.3.jar | Bin 0 -> 20124 bytes 4 files changed, 329 insertions(+), 3 deletions(-) create mode 100644 akka-cluster/src/test/scala/akka/cluster/ClusterMBeanSpec.scala create mode 100755 akka-kernel/src/main/dist/bin/akka-cluster create mode 100644 akka-kernel/src/main/dist/lib/cmdline-jmxclient-0.10.3.jar diff --git a/akka-cluster/src/main/scala/akka/cluster/Cluster.scala b/akka-cluster/src/main/scala/akka/cluster/Cluster.scala index 7944157103..69c2facf14 100644 --- a/akka-cluster/src/main/scala/akka/cluster/Cluster.scala +++ b/akka-cluster/src/main/scala/akka/cluster/Cluster.scala @@ -20,6 +20,9 @@ import java.util.concurrent.TimeUnit._ import java.util.concurrent.TimeoutException import java.security.SecureRandom +import java.lang.management.ManagementFactory +import javax.management._ + import scala.collection.immutable.{ Map, SortedSet } import scala.annotation.tailrec @@ -308,6 +311,27 @@ object Cluster extends ExtensionId[Cluster] with ExtensionIdProvider { override def createExtension(system: ExtendedActorSystem): Cluster = new Cluster(system) } +/** + * Interface for the cluster JMX MBean. + */ +trait ClusterNodeMBean { + def getMemberStatus: String + def getClusterStatus: String + + def isLeader: Boolean + def isSingleton: Boolean + def isConvergence: Boolean + def isAvailable: Boolean + + def ping(): String + def join(address: String) + def leave(address: String) + def down(address: String) + def remove(address: String) + + def shutdown() +} + /** * This module is responsible for Gossiping cluster information. The abstraction maintains the list of live * and dead members. Periodically i.e. every 1 second this module chooses a random member and initiates a round @@ -328,7 +352,7 @@ object Cluster extends ExtensionId[Cluster] with ExtensionIdProvider { * if (Cluster(system).isLeader) { ... } * }}} */ -class Cluster(system: ExtendedActorSystem) extends Extension { +class Cluster(system: ExtendedActorSystem) extends Extension { clusterNode ⇒ /** * Represents the state for this Cluster. Implemented using optimistic lockless concurrency. @@ -370,7 +394,10 @@ class Cluster(system: ExtendedActorSystem) extends Extension { private val log = Logging(system, "Node") private val random = SecureRandom.getInstance("SHA1PRNG") - log.info("Cluster Node [{}] - is JOINING cluster...", remoteAddress) + private val mBeanServer = ManagementFactory.getPlatformMBeanServer + private val clusterMBeanName = new ObjectName("akka:type=Cluster") + + log.info("Cluster Node [{}] - is starting up...", remoteAddress) // create superisor for daemons under path "/system/cluster" private val clusterDaemons = { @@ -409,12 +436,16 @@ class Cluster(system: ExtendedActorSystem) extends Extension { leaderActions() } - log.info("Cluster Node [{}] - has JOINED cluster successfully", remoteAddress) + createMBean() + + log.info("Cluster Node [{}] - has started up successfully", remoteAddress) // ====================================================== // ===================== PUBLIC API ===================== // ====================================================== + def ping = "pong" + def self: Member = latestGossip.members .find(_.address == remoteAddress) .getOrElse(throw new IllegalStateException("Can't find 'this' Member in the cluster membership ring")) @@ -464,6 +495,7 @@ class Cluster(system: ExtendedActorSystem) extends Extension { failureDetectorReaperCanceller.cancel() leaderActionsCanceller.cancel() system.stop(clusterDaemons) + mBeanServer.unregisterMBean(clusterMBeanName) } } @@ -1005,4 +1037,32 @@ class Cluster(system: ExtendedActorSystem) extends Extension { private def selectRandomNode(addresses: Iterable[Address]): Address = addresses.toSeq(random nextInt addresses.size) private def isSingletonCluster(currentState: State): Boolean = currentState.latestGossip.members.size == 1 + + /** + * Creates the cluster JMX MBean and registers it in the MBean server. + */ + private def createMBean() = { + val mbean = new StandardMBean(classOf[ClusterNodeMBean]) with ClusterNodeMBean { + def getMemberStatus: String = clusterNode.status.toString + + // FIXME clean up: Gossip(overview = GossipOverview(seen = [], unreachable = []), members = [Member(address = akka://system0@localhost:5550, status = Joining)], meta = [], version = VectorClock(Node(df2691d6cc6779dc2555316f557b5fa4) -> 00000136b164746d)) + def getClusterStatus: String = clusterNode.latestGossip.toString + + def isLeader: Boolean = clusterNode.isLeader + def isSingleton: Boolean = clusterNode.isSingletonCluster + def isConvergence: Boolean = clusterNode.convergence.isDefined + def isAvailable: Boolean = clusterNode.isAvailable + + def ping(): String = clusterNode.ping + + // FIXME return error message if failure + def join(address: String) = clusterNode.join(AddressFromURIString(address)) + def leave(address: String) = clusterNode.leave(AddressFromURIString(address)) + def down(address: String) = clusterNode.down(AddressFromURIString(address)) + def remove(address: String) = clusterNode.remove(AddressFromURIString(address)) + def shutdown() = clusterNode.shutdown() + } + log.info("Cluster Node [{}] - registering cluster JMX MBean [{}]", remoteAddress, clusterMBeanName) + mBeanServer.registerMBean(mbean, clusterMBeanName) + } } diff --git a/akka-cluster/src/test/scala/akka/cluster/ClusterMBeanSpec.scala b/akka-cluster/src/test/scala/akka/cluster/ClusterMBeanSpec.scala new file mode 100644 index 0000000000..9e9e89347e --- /dev/null +++ b/akka-cluster/src/test/scala/akka/cluster/ClusterMBeanSpec.scala @@ -0,0 +1,61 @@ +/** + * Copyright (C) 2009-2011 Typesafe Inc. + */ +package akka.cluster + +import java.net.InetSocketAddress + +import akka.testkit._ +import akka.dispatch._ +import akka.actor._ +import akka.remote._ +import akka.util.duration._ + +import com.typesafe.config._ + +/** + * ***************************************************** + * + * Add these options to the sbt script: + * java \ + * -Dcom.sun.management.jmxremote.port=9999 \ + * -Dcom.sun.management.jmxremote.ssl=false \ + * -Dcom.sun.management.jmxremote.authenticate=false \ + * -Dcom.sun.management.jmxremote.password.file= + * + * Use the jmx/cmdline-jmxclient to invoke queries and operations: + * java -jar jmx/cmdline-jmxclient-0.10.3.jar - localhost:9999 akka:type=Cluster join=akka://system0@localhost:5550 + * + * ***************************************************** + */ + +class ClusterMBeanSpec extends ClusterSpec("akka.loglevel = DEBUG") with ImplicitSender { + + var node0: Cluster = _ + var system0: ActorSystemImpl = _ + + try { + "A cluster node" must { + System.setProperty("com.sun.management.jmxremote.port", "9999") + system0 = ActorSystem("system0", ConfigFactory + .parseString("akka.remote.netty.port=5550") + .withFallback(system.settings.config)) + .asInstanceOf[ActorSystemImpl] + val remote0 = system0.provider.asInstanceOf[RemoteActorRefProvider] + node0 = Cluster(system0) + + "be able to communicate over JMX through its ClusterMBean" in { //taggedAs LongRunningTest in { + Thread.sleep(120.seconds.dilated.toMillis) + } + } + } catch { + case e: Exception ⇒ + e.printStackTrace + fail(e.toString) + } + + override def atTermination() { + if (node0 ne null) node0.shutdown() + if (system0 ne null) system0.shutdown() + } +} diff --git a/akka-kernel/src/main/dist/bin/akka-cluster b/akka-kernel/src/main/dist/bin/akka-cluster new file mode 100755 index 0000000000..a99d84de88 --- /dev/null +++ b/akka-kernel/src/main/dist/bin/akka-cluster @@ -0,0 +1,205 @@ +#!/bin/bash + +# FIXME allow passing in a hostname:port to override default +# FIXME leader should return the current leader - not boolean + +DEFAULT_HOST="localhost:9999" +SCRIPT=`basename $0` +JMX_CLIENT="java -jar $AKKA_HOME/jmx/cmdline-jmxclient-0.10.3.jar - $DEFAULT_HOST akka:type=Cluster" + +# Check the first argument for instructions +case "$1" in + join) + if [ $# -ne 2 ]; then + echo "Usage: $SCRIPT join " + exit 1 + fi + + # Make sure the local node IS running + REPLY=$($JMX_CLIENT ping 2>&1 >/dev/null) # redirect STDERR to STDOUT before capturing it + if [[ "$REPLY" != *pong* ]]; then + echo "Akka cluster node is not running" + exit 1 + fi + + shift + $JMX_CLIENT join=$@ + ;; + + leave) + if [ $# -ne 1 ]; then + echo "Usage: $SCRIPT leave" + exit 1 + fi + + # Make sure the local node IS running + REPLY=$($JMX_CLIENT ping 2>&1 >/dev/null) # redirect STDERR to STDOUT before capturing it + if [[ "$REPLY" != *pong* ]]; then + echo "Akka cluster node is not running" + exit 1 + fi + + shift + $JMX_CLIENT leave=$@ + ;; + + remove) + if [ $# -ne 2 ]; then + echo "Usage: $SCRIPT remove " + exit 1 + fi + + # Make sure the local node IS running + REPLY=$($JMX_CLIENT ping 2>&1 >/dev/null) # redirect STDERR to STDOUT before capturing it + if [[ "$REPLY" != *pong* ]]; then + echo "Akka cluster node is not running" + exit 1 + fi + + shift + $JMX_CLIENT remove=$@ + ;; + + down) + if [ $# -ne 2 ]; then + echo "Usage: $SCRIPT down " + exit 1 + fi + + # Make sure the local node IS running + REPLY=$($JMX_CLIENT ping 2>&1 >/dev/null) # redirect STDERR to STDOUT before capturing it + if [[ "$REPLY" != *pong* ]]; then + echo "Akka cluster node is not running" + exit 1 + fi + + shift + $JMX_CLIENT down=$@ + ;; + + member-status) + if [ $# -ne 1 ]; then + echo "Usage: $SCRIPT member-status" + exit 1 + fi + + # Make sure the local node IS running + REPLY=$($JMX_CLIENT ping 2>&1 >/dev/null) # redirect STDERR to STDOUT before capturing it + if [[ "$REPLY" != *pong* ]]; then + echo "Akka cluster node is not running" + exit 1 + fi + + shift + $JMX_CLIENT MemberStatus + ;; + + cluster-status) + if [ $# -ne 1 ]; then + echo "Usage: $SCRIPT cluster-status" + exit 1 + fi + + # Make sure the local node IS running + REPLY=$($JMX_CLIENT ping 2>&1 >/dev/null) # redirect STDERR to STDOUT before capturing it + if [[ "$REPLY" != *pong* ]]; then + echo "Akka cluster node is not running" + exit 1 + fi + + shift + $JMX_CLIENT ClusterStatus + ;; + + ping) + if [ $# -ne 1 ]; then + echo "Usage: $SCRIPT ping" + exit 1 + fi + + # Make sure the local node IS running + REPLY=$($JMX_CLIENT ping 2>&1 >/dev/null) # redirect STDERR to STDOUT before capturing it + if [[ "$REPLY" != *pong* ]]; then + echo "Akka cluster node is not running" + exit 1 + fi + + shift + $JMX_CLIENT ping + ;; + + leader) + if [ $# -ne 1 ]; then + echo "Usage: $SCRIPT leader" + exit 1 + fi + + # Make sure the local node IS running + REPLY=$($JMX_CLIENT ping 2>&1 >/dev/null) # redirect STDERR to STDOUT before capturing it + if [[ "$REPLY" != *pong* ]]; then + echo "Akka cluster node is not running" + exit 1 + fi + + shift + $JMX_CLIENT Leader + ;; + + is-singleton) + if [ $# -ne 1 ]; then + echo "Usage: $SCRIPT is-singleton" + exit 1 + fi + + # Make sure the local node IS running + REPLY=$($JMX_CLIENT ping 2>&1 >/dev/null) # redirect STDERR to STDOUT before capturing it + if [[ "$REPLY" != *pong* ]]; then + echo "Akka cluster node is not running" + exit 1 + fi + + shift + $JMX_CLIENT Singleton + ;; + + has-convergence) + if [ $# -ne 1 ]; then + echo "Usage: $SCRIPT is-convergence" + exit 1 + fi + + # Make sure the local node IS running + REPLY=$($JMX_CLIENT ping 2>&1 >/dev/null) # redirect STDERR to STDOUT before capturing it + if [[ "$REPLY" != *pong* ]]; then + echo "Akka cluster node is not running" + exit 1 + fi + + shift + $JMX_CLIENT Convergence + ;; + + is-available) + if [ $# -ne 1 ]; then + echo "Usage: $SCRIPT is-available" + exit 1 + fi + + # Make sure the local node IS running + REPLY=$($JMX_CLIENT ping 2>&1 >/dev/null) # redirect STDERR to STDOUT before capturing it + if [[ "$REPLY" != *pong* ]]; then + echo "Akka cluster node is not running" + exit 1 + fi + + shift + $JMX_CLIENT Available + ;; + + *) + echo "Usage: $SCRIPT { ping | join | leave | remove | down" + echo " member-status | cluster-status | leader" + echo " is-singleton | has-convergence | is-available }" + exit 1 + ;; +esac diff --git a/akka-kernel/src/main/dist/lib/cmdline-jmxclient-0.10.3.jar b/akka-kernel/src/main/dist/lib/cmdline-jmxclient-0.10.3.jar new file mode 100644 index 0000000000000000000000000000000000000000..a3454cfe78eea0d6eba0081663cd85ce7d55c5e0 GIT binary patch literal 20124 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 b$|0@K3-D%T1KG#Pz{!xp#K55B1L6Syu7VWE literal 0 HcmV?d00001