Akka Notes – Actor Messaging – Request and Response – 3
Last time when we saw Actor messaging, we saw how fire-n-forget messages are sent (Meaning, we just send a message to the Actor but don’t expect a response from the Actor).
Technically, we fire messages to Actors for its side-effects ALL THE TIME. It is by design. Other than not responding, the target Actor could ALSO do the following with that message:
- Send a response back to the sender (in our case, the
TeacherActor
would respond with a quote back to theStudentActor
OR - Forward a response back to some other Actor who might be the intended audience which in turn might respond/forward/have a side-effect. Routers and Supervisors are examples of those cases (we’ll look at them very soon).
Request & Response
In this write-up, we’ll be focussing only on Point 1 – the request-response cycle.
The picture conveys what we are trying to achieve this time. For sake of brevity, I didn’t represent the ActorSystem, Dispatcher or Mailboxes in the picture.
- The
DriverApp
sends anInitSignal
message to theStudentActor
. - The
StudentActor
reacts to theInitSignal
message and sends aQuoteRequest
message to theTeacherActor
. - The
TeacherActor
, like we saw in the first discussion, responds with aQuoteResponse
. - The
StudentActor
just logs theQuoteResponse
to the console/logger.
We’ll also cook up a testcase to verify it.
Let’s look at these 4 points in detail now :
1. The DriverApp
sends an InitSignal
message to the StudentActor
By now, you would have guessed what would the DriverApp
do. Just 4 things :
- Initialize the
ActorSystem
- Create the
TeacherActor
- Create the
StudentActor
- The
DriverApp
would then send anInitSignal
to theStudentActor
, so that the StudentActor could start sending the QuoteRequest message to the TeacherActor.
//Initialize the ActorSystem val system = ActorSystem("UniversityMessageSystem")
//create the teacher actor val teacherRef = system.actorOf(Props[TeacherActor], "teacherActor")
//create the Student Actor - pass the teacher actorref as a constructor parameter to StudentActor val studentRef = system.actorOf(Props(new StudentActor(teacherRef)), "studentActor")
You’ll notice that I am passing in the ActorRef
of the TeacherActor
to the constructor of the StudentActor
so that the StudentActor could use the ActorRef for sending messages to the TeacherActor. There are other ways to achieve this (like passing in the Props) but this method would come in handy when we look at Supervisors and Routers in the following write-ups. We’ll also be looking at child actors pretty soon but that wouldn’t semantically be the right approach here – Student creating Teacher doesn’t sound nice. Does it?
Lastly,
//send a message to the Student Actor studentRef ! InitSignal
That’s pretty much the DriverClass. The Thread.sleep
and the ActorSystem.shutdown
are just to wait for a couple of seconds for the message sending to finish before we finally shut down the ActorSystem.
DriverApp.scala
package me.rerun.akkanotes.messaging.requestresponse import akka.actor.ActorSystem import akka.actor.Props import me.rerun.akkanotes.messaging.protocols.StudentProtocol._ import akka.actor.ActorRef object DriverApp extends App { //Initialize the ActorSystem val system = ActorSystem("UniversityMessageSystem") //construct the teacher actor val teacherRef = system.actorOf(Props[TeacherActor], "teacherActor") //construct the Student Actor - pass the teacher actorref as a constructor parameter to StudentActor val studentRef = system.actorOf(Props(new StudentActor(teacherRef)), "studentActor") //send a message to the Student Actor studentRef ! InitSignal //Let's wait for a couple of seconds before we shut down the system Thread.sleep(2000) //Shut down the ActorSystem. system.shutdown() }
2. The StudentActor
reacts to the InitSignal
message and sends a QuoteRequest
message to the TeacherActor
AND
4. The StudentActor
receives the QuoteResponse
from TeacherActor
and just logs to the console/logger
Why did I combine Points 2 and 4? Because it is so simple you’ll hate me if I separate them.
So, Point 2 – the StudentActor receives the InitSignal
message from the DriverApp
and sends QuoteRequest
to the TeacherActor.
def receive = { case InitSignal=> { teacherActorRef!QuoteRequest } ... ...
That’s it !!!
Point 4 – The StudentActor logs the message that it receives from the TeacherActor.
Just, as promised :
case QuoteResponse(quoteString) => { log.info ("Received QuoteResponse from Teacher") log.info(s"Printing from Student Actor $quoteString") }
I am sure you’d agree that it almost looks like pseudocode now.
So, the entire StudentActor
class looks like :
StudentActor.scala
package me.rerun.akkanotes.messaging.requestresponse import akka.actor.Actor import akka.actor.ActorLogging import me.rerun.akkanotes.messaging.protocols.TeacherProtocol._ import me.rerun.akkanotes.messaging.protocols.StudentProtocol._ import akka.actor.Props import akka.actor.ActorRef class StudentActor (teacherActorRef:ActorRef) extends Actor with ActorLogging { def receive = { case InitSignal=> { teacherActorRef!QuoteRequest } case QuoteResponse(quoteString) => { log.info ("Received QuoteResponse from Teacher") log.info(s"Printing from Student Actor $quoteString") } } }
3. The TeacherActor
responds with a QuoteResponse
.
This is the exact same code as we saw in the fire-n-forget write-up.
The TeacherActor receives a QuoteRequest
message and sends QuoteResponse
back.
TeacherActor.scala
package me.rerun.akkanotes.messaging.requestresponse import scala.util.Random import akka.actor.Actor import akka.actor.ActorLogging import akka.actor.actorRef2Scala import me.rerun.akkanotes.messaging.protocols.TeacherProtocol._ class TeacherActor extends Actor with ActorLogging { val quotes = List( "Moderation is for cowards", "Anything worth doing is worth overdoing", "The trouble is you think you have time", "You never gonna know if you never even try") def receive = { case QuoteRequest => { import util.Random //Get a random Quote from the list and construct a response val quoteResponse = QuoteResponse(quotes(Random.nextInt(quotes.size))) //respond back to the Student who is the original sender of QuoteRequest sender ! quoteResponse } } }
Testcases
Now, our testcase would simulate the DriverApp
. Since, the StudentActor just logs the message and we won’t be able to assert on the QuoteResponse itself, we’ll just assert the presence of the log message in the EventStream (just like we talked last time).
So, our testcase looks like :
"A student" must { "log a QuoteResponse eventually when an InitSignal is sent to it" in { import me.rerun.akkanotes.messaging.protocols.StudentProtocol._ val teacherRef = system.actorOf(Props[TeacherActor], "teacherActor") val studentRef = system.actorOf(Props(new StudentActor(teacherRef)), "studentActor") EventFilter.info (start="Printing from Student Actor", occurrences=1).intercept{ studentRef!InitSignal } } }
Code
The entire project could be downloaded from github here.
Next up, we’ll see how to use schedulers in Akka and monitoring your Akka app using Kamon.
Reference: | Akka Notes – Actor Messaging – Request and Response – 3 from our JCG partner Arun Manivannan at the Rerun.me blog. |