Sunday 21 August 2016

AKKA (Actor Messaging) -Part 2

Now lets discuss the messaging part of Actors:



Broadly these are explained in the following six steps when a message is passed to the actor:
  1. Employee creates something called an ActorSystem
  2. It uses the ActorSystem to create something called as ActorRef. The message(MSG) is sent to the ActorRef (a proxy to HR Actor)
  3. Actor ref passes the message along to a Message Dispatcher
  4. The Dispatcher enqueues the message in the target Actor’s MailBox.
  5. The Dispatcher then puts the Mailbox on a Thread (more on that in the next section).
  6. The MailBox dequeues a message and eventually delegates that to the actual HR Actor’s receive method.
Let’s look at each step in detail now. You can come back and revisit these five steps once we are done.

The EmployeeActor Program.

Lets consider this Employee Actor as the main program and lets cal it . EmployeeActorApp.
As we understand from the picture, the Employee Actor,
  1. Creates an ActorSystem
  2. Uses the ActorSystem to create a proxy to the HRActor (ActorRef)
  3. Sends the Message to the proxy.
Let’s focus on these three points alone now.

Creating an Actor System

ActorSystem is the entry point into the ActorWorld. ActorSystems are through which you could create and stop Actors. Or even shutdown the entire Actor environment.

On the other end of the spectrum, Actors are hierarchical and the ActorSystem is also similar to the java.lang.Object or scala.Any for all Actors - meaning, it is the root for all Actors. When you create an Actor using the ActorSystem’s actorOf method, you create an Actor just below the ActorSystem.

The code for initializing the ActorSystem looks like.
val system=ActorSystem("HrMessageingSystem")  
The HrMessageingSystem is simply a name you give to your ActorSystem.

Creating a Proxy for HR Actor

val hrActorRef:ActorRef = actorSystem.actorOf(Props[HRActor])
The actorOf is the Actor creation method in ActorSystem. But, as you can see, it doesn’t return a HrActor which we need. It returns something of type ActorRef.

The ActorRef acts as a Proxy for the actual Actors. The clients do not talk directly with the Actor. This is Actor Model’s way of avoiding direct access to any custom/private methods or variables in the HrActor or any Actor for that sake.

To repeat, you send messages only to the ActorRef and it eventually reaches your actual Actor. You can never talk to your Actor directly.

Send a Message to the Proxy

Now that we have an actor system and a reference to the actor, we would like to send requests to the HR actor reference. We send the message to an actor using the ! also called Tell.
//send a message to the HR Actor
 hrActorRef ! Message.
The Tell is also called as fire-forget. There is no acknowledgement returned from a Tell.
When the message is sent to an actor, the actor’s receive method will receive the message and processes it further. receive is a partial function and has the following signature:
def receive: PartialFunction[Any, Unit]
The return type receive suggests it is Unit and therefore this function is side effecting.

Here is the sample program for what we have discussed till now:
object EmployeeActorApp extends App{
 //Initialize the ActorSystem
  val actorSystem=ActorSystem("HrMessageingSystem")

 //construct the HR Actor Ref
  val hrActorRef=actorSystem.actorOf(Props[HrActor])

 //send a message to the HR Actor
  hrActorRef!Message

 //Let's wait for a couple of seconds before we shut down the    system
  Thread.sleep (2000) 

 //Shut down the ActorSystem.
  actorSystem.shutdown()

}  
You’ll have to shutdown the ActorSystem or otherwise, the JVM keeps running forever. And I am making the main thread sleep for a little while just to give the HrActor to finish off its task.

The Message

We just told a Message to the ActorRef but we didn’t see the message class at all!
object Message{
  case class Message()
  case class Message(someString:String)
}
As we know, the Message is for the requests that come to the HrActor. The Actor would respond back with a MessageResponse.

Dispatcher & A Mailbox

The ActorRef delegates the message handling functionality to the Dispatcher. Under the hood, while we created the ActorSystem and the ActorRef, a Dispatcher and a MailBox was created. Let’s see what they are about.

MailBox

Ever Actor has one MailBox . As Per our analogy, every HR has one mailbox too. The HR has to check the mailbox and process the message. In Actor world, it’s the other way round - the mailbox, when it gets a chance uses the Actor to accomplish its work.

Also the mailbox has a queue to store and process the messages in a FIFO fashion - a little different from our regular inbox where the most latest is the one at the top.

Dispatcher

Dispatcher does some really cool stuff. From the looks of it, the Dispatcher just gets the message from the ActorRef and passes it on to the MailBox. But there’s one amazing thing happening behind the scenes :

The Dispatcher wraps an ExecutorService (ForkJoinPool or ThreadPoolExecutor). It executes the MailBox against this ExecutorService.

Check out this code snippet from the Dispatcher:
protected[akka] override def registerForExecution(mbox: Mailbox, ...): Boolean = {  
    ...
    try {
        executorService execute mbox
    ...
}

HR Actor

The MailBox, when it gets its run method fired, dequeues a message from the message queue and passes it to the Actor for processing.

The method that eventually gets called when you tell a message to an ActorRef is the receive method of the target Actor.

The HrActor is a rudimentary class which has a List of quotes and obviously the receive method which handles the messages.

HRActor.scala :
class HRActor extends Actor {
def receive  = {
    case s: String if(s.equalsIgnoreCase(“SICK”)=> println("Sick Leave Applied”)
    case s: String if(s.equalsIgnoreCase(“PTO”) => println("PTO applied “)
    }
 }
The HRActor’s receive method pattern matches for the Messages passed - the Message (actually, it is a good practice to pattern match the default case but there’s an interesting story to tell there)

All that the receive method does is to,
  1. Pattern match for Message
  2. Check the message
  3. Check the type of the Message
  4. Process the message based on the type of message.

No comments:

Post a Comment