Monday, December 24, 2012

Basics of Actors in akka/scala part 2


Hot swap of akka's actors behaviour

Akka actors' receive method accepts a partial function from Any to Unit(i.e. PartialFunction[Any, Unit]) and this function can be dynamically changed at runtime. Shown below is a simple example of that:

scala> class MyActor extends Actor {
     |   def receive = {
     |     case s: String => println("Current behaviour : "+ s)
     |     case pf: PartialFunction[Any, Unit] => context.become(pf)
     |   }
     | }


scala> val pf:PartialFunction[Any, Unit] = {
     |   case s => println("Behaving differently : "+ s)
     | }
pf: PartialFunction[Any,Unit] = <function1>



scala> val system =ActorSystem("system")
system: akka.actor.ActorSystem = akka://system

scala> val ma = system.actorOf(Props[MyActor])
ma: akka.actor.ActorRef = Actor[akka://system/user/$a]

scala> ma ! "Testing"
Current behaviour : Testing

scala> ma ! pf

scala> ma ! "Testing again"
Behaving differently : Testing again

Complementing `become`, the context also provides an `unbecome` which reverts the actor's behavior to the previous one in the hotswap stack.

Router Actor

A router is just an actor that routes incoming messages to other actors. Akka has a default set of routers for various use cases. Shown below is a router which routes messages to routees in a round robin fashion.

scala> class Routee extends Actor {
     |   def receive = {
     |     case x => println(self.path + " : received : "+ x)
     |   }
     | } 
defined class Routee

scala> val routee1 = system.actorOf(Props[Routee],name="routee1")
routee1: akka.actor.ActorRef = Actor[akka://system/user/routee1]

scala> val routee3 = system.actorOf(Props[Routee],name="route3")
routee3: akka.actor.ActorRef = Actor[akka://system/user/route3]

scala> val routee2 = system.actorOf(Props[Routee],name="route2")
routee2: akka.actor.ActorRef = Actor[akka://system/user/route2]

scala> import akka.routing.RoundRobinRouter
import akka.routing.RoundRobinRouter

scala> val routees = Vector[ActorRef](routee1, routee2, routee3)
routees: scala.collection.immutable.Vector[akka.actor.ActorRef] = Vector(Actor[akka://system/user/routee1], Actor[akka://system/user/route2], Actor[akka://system/user/route3])

scala> val router = system.actorOf(Props().withRouter(RoundRobinRouter(routees = routees)))
router: akka.actor.ActorRef = Actor[akka://system/user/$b]

scala> router ! "testing"

scala> akka://system/user/routee1 : received : testing


scala> router ! "testing"
akka://system/user/route2 : received : testing

scala> router ! "testing"
akka://system/user/route3 : received : testing


Remote actors:

As mentioned in the previous post, akka actors shipped with scala does not support remoting by default. We need to add addtional jars from the akka distribution. Following are the jars that need to be in the class path:

  • akka-remote_2.10.0-RC5-2.1.0-RC6.jar
  • netty-3.5.8.Final.jar
  • protobuf-java-2.4.1.jar

The first step is to create a conf file called `application.conf` with following content:

 akka {
   actor {
     provider = "akka.remote.RemoteActorRefProvider"
   }
   remote {
     transport = "akka.remote.netty.NettyRemoteTransport"
     netty {
       hostname = "127.0.0.1"
       port = 2552
     }
   }
 }

This file should be available in the classpath. We launch  two scala consoles, from two terminals, making a copy of the `application.conf` and changing port number to 2553 in one of them.


class RemoteActor extends Actor {
 def receive = {
   case msg => println("Received : "+ msg)
               sender ! "Got : "+ msg
 }
}

usr@ubuntu:~/akka/remoting$ scala -cp ./:$AKKA_LIB/akka-remote_2.10.0-RC5-2.1.0-RC6.jar:$AKKA_LIB/netty-3.5.8.Final.jar:$AKKA_LIB/protobuf-java-2.4.1.jar
Welcome to Scala version 2.10.0-RC5 (Java HotSpot(TM) Server VM, Java 1.7.0).
Type in expressions to have them evaluated.
Type :help for more information.

scala> import akka.actor._
import akka.actor._

scala> class RemoteActor extends Actor {
     |  def receive = {
     |    case msg => println("Received : "+ msg)
     |                sender ! "Got : "+ msg
     |  }
     | }
defined class RemoteActor

scala> val remoteSystem = ActorSystem("remotesystem")
remoteSystem: akka.actor.ActorSystem = akka://remotesystem


scala> val remoteActor = remoteSystem.actorOf(Props[RemoteActor],name="remoteactor")
remoteActor: akka.actor.ActorRef = Actor[akka://remotesystem/user/remoteactor]

scala> remoteActor ! "local msg"
Received : local msg

We send a message the actor locally - it receives the message and prints it but reply does not get printed because the sender(i.e. REPL) is not an actor - so the reply goes to the dead letter actor.

Now we go to the second scala console create an actor and look up the actor created before and send a message to it.

scala> class MyActor extends Actor {
     |    def receive = {
     |      case x: String => println("Received : "+ x)
     |      case (a: ActorRef, x: String) => a ! x
     |    }
     | }
defined class MyActor

scala> val system = ActorSystem("system")
system: akka.actor.ActorSystem = akka://system

val ma = system.actorOf(Props[MyActor],name="myactor")
ma: akka.actor.ActorRef = Actor[akka://system/user/myactor]


scala> val remoteActor = system.actorFor("akka://remotesystem@127.0.0.1:2553/user/remoteactor")
remoteActor: akka.actor.ActorRef = Actor[akka://remotesystem@127.0.0.1:2553/user/remoteactor]

scala> ma ! (remoteActor, "testing")

scala> [INFO] [12/24/2012 14:31:21.155] [system-akka.actor.default-dispatcher-5] [NettyRemoteTransport(akka://system@127.0.0.1:2552)] RemoteClientStarted@akka://remotesystem@127.0.0.1:2553
Received : Got : testing


On the remote actor side we see:

scala> [INFO] [12/24/2012 14:29:43.906] [remotesystem-15] [NettyRemoteTransport(akka://remotesystem@127.0.0.1:2553)] RemoteClientStarted@akka://system@127.0.0.1:2552
Received : testing

Conclusion: In these two parts posts we explored basics of scala/akka actors. Hopefully, anyone looking for where to begin about akka actors may have got some footing to explore the rest of akka.

1 comment:

  1. Great Posts , exactly the kind of Introduction to Akka I was looking for. Thanks a bunch.

    ReplyDelete