You are on page 1of 68

AKKAINTRODUCINGACTORS

Anyonewhohasdonemultithreadinginthepastwon'tdenyhowhardandpainfulitisto
manage
multithreadedapplications.Isaid
manage
becauseitstartsoutsimpleanditbecameawholelotoffunonce
youstartseeingperformanceimprovements.However,itacheswhenyouseethatyoudon'thaveaeasier
waytorecoverfromerrorsinyoursubtasksORthosezombiebugsthatyoufindhardtoreproduceOR
whenyourprofilershowsthatyourthreadsarespendingalotoftimeblockingwastefullybeforewritingto
asharedstate.
IprefernottotalkabouthowJavaconcurrencyAPIandtheircollectionsmadeitbetterandeasierbecauseI
amsureifyouarehere,youprobablyneededmorecontroloverthe
subtasks
orsimplybecauseyoudon't
liketowritelocksandsynchronizedblocksandwouldpreferahigherlevelofabstraction.

WHATAREACTORS?
Akka'sActorsfollowthe
ActorModel
(duh!).
TreatActorslikePeople.Peoplewhodon'ttalktoeachotherinperson.Theyjusttalkthroughmails.
Let'sexpandonthatabit.
1.MESSAGING
ConsidertwopersonsAwiseTeacherandStudent.TheStudentsendsamaileverymorningtothe
TeacherandthewiseTeachersendsawisequoteback.
Pointstonote:
1. Thestudentsendsamail.Oncesent,themailcouldn'tbeedited.Talkaboutnaturalimmutability.
2. TheTeachercheckshismailboxwhenhewishestodoso.
3. TheTeacheralsosendsamailback(immutableagain).
4. Thestudentchecksthemailboxathisowntime.
5. Thestudentdoesn'twaitforthereply.(noblocking)
ThatprettymuchsumsupthebasicblockoftheActorModelpassingmessages.


2.CONCURRENCY
Now,imaginethereare3wiseteachersand3studentseverystudentsendsnotestoeveryotherteacher.
Whathappensthen?Nothingchangesactually.Everybodyhastheirownmailbox.Onesubtlepointtonote
hereisthis:
Bydefault,Mailsinthemailboxareread/processedintheordertheyarrived.
Internally,bydefaultitisa
ConcurrentLinkedQueue
.Andsincenobodywaitsforthemailtobepickedup,
itissimplyanonblockingmessage.(Therearea
varietyofbuiltinmailboxes
includingboundedand
prioritybased.Infact,wecouldbuildoneourselftoo)


3.FAILOVER
Imaginethese3teachersarefromthreedifferentdepartmentsHistory,GeographyandPhilosophy.
HistoryteachersreplieswithanoteonanEventinthepast,GeographyteacherssendsanInterestingPlace
andPhilosophyteachers,aquote.Eachstudentsendsmessagetoeachteacherandgetsresponses.The
studentdoesntcarewhichteacherinthedepartmentsendsthereplyback.Whatifoneday,ateacherfalls
sick?Therehastobeatleastoneteacherhandlingthemailsfromthedepartment.Inthiscase,another
teacherinthedepartmentstepsupanddoesthejob.


Pointstonote:
1. TherecouldbeapoolofActorswhodoesdifferentthings.
2. AnActorcoulddosomethingthatcausesanexception.Itwouldn'tbeabletorecoverbyitself.In
whichcaseanewActorcouldbe
created
inplaceoftheoldone.Alternatively,theActorcould
justignorethatoneparticularmessageandproceedwiththerestofthemessages.Thesearecalled
Directivesandwe'lldiscussthemlater.
4.MULTITASKING
Foratwist,let'sassumethateachoftheseteachersalsosendtheexamscorethroughmailtoo,ifthestudent
asksforit.Similarly,antheActorcouldhandlemorethanone
type
ofmessagecomfortably.
5.CHAINING
Whatifthestudentwouldliketogetonlyonefinalconsolidatedtriviamailinsteadofthree?

WecoulddothattoowithActorstoo.Wecouldchaintheteachersasahierarchy.We'llcomebacktothat
laterwhenwetalkaboutSupervisorsandrevisitthesamethoughtwhenwetalkaboutFutures.
AsrequestedbyMohan,let'sjusttrytomaptheanalogycomponentswiththethecomponentsintheActor
Model.

StudentsandtheTeachersbecomesour
Actors
.TheEmailInboxbecomesthe
Mailbox
component.The
requestandtheresponsecan'tbemodified.Theyare
immutable
objects.Finally,the
MessageDispatcher
componentinActormanagesthemailboxandroutesthemessagestotherespective
Mailbox
.
Enoughtalk,let'scookup
somecode....

ACTORMESSAGING1

Fromthe
introductoryfirstpart
oftheAkkaNotes,wesawabird'seyeviewofActorsintheAkkaToolkit.
InthissecondpartoftheAkkaNotes,we'lllookatthemessagingpartofActors.Asfortheexample,we
wouldusethesameStudentTeacherexamplethatwediscussedearlier.
InthisfirstpartofActorMessaging,we'llcreatetheTeacherActorandinsteadoftheStudentActor,we'll
useamainprogramcalled
StudentSimulatorApp
.

REVISITINGSTUDENTTEACHERINDETAIL
Let'sfornowconsiderthemessagesentbytheStudentSimulatorApptotheTeacherActoralone.WhenI
say
StudentSimulatorApp
,Ijustmeananormalmainprogram.

Thepictureconveysthis:
1. Studentcreatessomethingcalledan
ActorSystem
2. ItusestheActorSystemtocreatesomethingcalledas
ActorRef
.The
QuoteRequest
messageis
senttotheActorRef(aproxytoTeacherActor)
3. Actorrefpassesthemessagealongtoa
Dispatcher
4. TheDispatcherenqueuesthemessageinthetargetActor's
MailBox
.
5. TheDispatcherthenputsthe
Mailbox
onaThread(moreonthatinthenextsection).
6. The
MailBox
dequeuesamessageandeventuallydelegatesthattotheactualTeacherActor's
receivemethod.
LikeIsaid,don'tworryaboutit.Let'slookateachstepindetailnow.Youcancomebackandrevisitthese
fivestepsoncewearedone.

THE
STUDENTSIMULATORAPP
PROGRAM
WewouldusethisStudentSimulatorApptobringuptheJVMandinitializetheActorSystem.

Asweunderstandfromthepicture,theStudentSimulatorApp

1. CreatesanActorSystem
2. UsestheActorSystemtocreateaproxytotheTeacherActor(ActorRef)
3. SendstheQuoteRequestmessagetotheproxy.
Let'sfocusonthesethreepointsalonenow.
1.CreatinganActorSystem
ActorSystem
istheentrypointintotheActorWorld.ActorSystemsarethroughwhichyoucouldcreateand
stopActors.OrevenshutdowntheentireActorenvironment.
Ontheotherendofthespectrum,ActorsarehierarchicalandtheActorSystemisalsosimilartothe
java.lang.Object
or
scala.Any
forallActorsmeaning,itistherootforallActors.Whenyoucreate
anActorusingtheActorSystem's
actorOf
method,youcreateanActorjustbelowtheActorSystem.

ThecodeforinitializingtheActorSystemlookslike
valsystem
=
ActorSystem
(
"UniversityMessageSystem"
)

The
UniversityMessageSystem
issimplyacutenameyougivetoyourActorSystem.
2.CreatingaProxyforTeacherActor?
Let'sconsiderthefollowingsnippet:
valteacherActorRef

:
ActorRef
=
actorSystem
.
actorOf
(
Props
[
TeacherActor
])

The
actorOf
istheActorcreationmethodinActorSystem.But,asyoucansee,itdoesn'treturna
TeacherActorwhichweneed.Itreturnssomethingoftype
ActorRef
.
The
ActorRef
actsasaProxyfortheactualActors.TheclientsdonottalkdirectlywiththeActor.Thisis
ActorModel'swayofavoidingdirectaccesstoanycustom/privatemethodsorvariablesinthe
TeacherActororany
Actor
forthatsake.
Torepeat,yousendmessagesonlytotheActorRefanditeventuallyreachesyouractualActor.Youcan
NEVERtalktoyourActordirectly.Peoplewillhateyoutodeathifyoufindsomemeanwaystodothat.

3.Senda
QuoteRequest
totheProxy
It'sanonelineragain.Youjust
tell
the
QuoteRequest
messagetotheActorRef.Thetellmethodin
Actorisactually
!
.(there'salsoa
tell
methodinActorRefwhichjustdelegatesthecallbackto
!
)
//send a message to the Teacher Actor
teacherActorRef
!
QuoteRequest
That'sit!!!
IfyouthinkIamlying,checktheentirecodeofthe
StudentSimulatorApp
below:

STUDENTSIMULATORAPP.SCALA
packageme
.
rerun
.
akkanotes
.
messaging
.
actormsg1
importakka
.
actor
.
ActorSystem
importakka
.
actor
.
Props
importakka
.
actor
.
actorRef2Scala
importme
.
rerun
.
akkanotes
.
messaging
.
protocols
.
TeacherProtocol
.
_
objectStudentSimulatorApp
extendsApp
{
//Initialize the ActorSystem

valactorSystem

=
ActorSystem
(
"UniversityMessageSystem"
)
//construct the Teacher Actor Ref

valteacherActorRef

=
actorSystem
.
actorOf
(
Props
[
TeacherActor
])
//send a message to the Teacher Actor

teacherActorRef
!
QuoteRequest
//Let's wait for a couple of seconds before we shut down the system

Thread
.
sleep
(
2000
)
//Shut down the ActorSystem.

actorSystem
.
shutdown
()
}
Well,Icheatedalittle.You'llhaveto
shutdown
theActorSystemorotherwise,theJVMkeepsrunning
forever.AndIammakingthemainthreadsleepforalittlewhilejusttogivetheTeacherActortofinishoff
it'stask.Iknowthissoundsstupid.Don'tworryaboutit.We'llwritesomeneattestcasesinthenextpartin
ordertoavoidthishack.

THEMESSAGE
Wejust
told
a
QuoteRequest
totheActorRefbutwedidn'tseethemessageclassatall!!
Hereitcomes:
(Itisarecommendedpracticetowrapyourmessagesinaniceobjectforeasier
organization)
TeacherProtocol
packageme
.
rerun
.
akkanotes
.
messaging
.
protocols
objectTeacherProtocol
{
case

classQuoteRequest
()
case

classQuoteResponse
(
quoteString
:
String
)
}

Asyouknow,the
QuoteRequest
isfortherequeststhatcometotheTeacherActor.TheActorwould
respondbackwitha
QuoteResponse
.

DISPATCHERANDAMAILBOX
The
ActorRef
delegatesthemessagehandlingfunctionalitytothe
Dispatcher
.Underthehood,while
wecreatedthe
ActorSystem
andthe
ActorRef
,a
Dispatcher
anda
MailBox
wascreated.Let'ssee
whattheyareabout.

MailBox
EverActorhasoneMailBox(we'llseeonespecialcaselater).Perouranalogy,everyTeacherhasone
mailboxtoo.TheTeacherhastocheckthemailboxandprocessthemessage.InActorworld,it'stheother
wayroundthemailbox,whenitgetsachanceusestheActortoaccomplishitswork.
AlsothemailboxhasaqueuetostoreandprocessthemessagesinaFIFOfashionalittledifferentfrom
ourregularinboxwherethemostlatestistheoneatthetop.
Now,thedispatcher
Dispatcherdoessomereallycoolstuff.Fromthelooksofit,theDispatcherjustgetsthemessagefromthe
ActorRefandpassesitontotheMailBox.Butthere'soneamazingthinghappeningbehindthescenes:
TheDispatcherwrapsan
ExecutorService
(ForkJoinPoolorThreadPoolExecutor).Itexecutesthe
MailBox
againstthisExecutorService.
Checkoutthissnippetfromthe
Dispatcher
protected
[
akka
]
override
defregisterForExecution
(
mbox
:Mailbox
,
...)
:
Boolean
=
{

..
.
try

{
executorService execute mbox
...

What?DidyoujustsayyouexecutetheMailBox?
Yup.WealreadysawthattheMailBoxholdsallthemessagesinaQueue.AlsosincetheExecutorrunsthe
MailBox
,theMailBoxmustbea
Thread
.You'reright.That'sprettymuchMailBox'sdeclarationand
constructor.
Here'sthesignatureofthe
Mailbox
private
[
akka
]
abstract
classMailbox
(
valmessageQueue
:MessageQueue
)
extends
SystemMessageQueue
withRunnable

TEACHERACTOR

The
MailBox
,whenitgetsits
run
methodfired,dequeuesamessagefromthemessagequeueandpasses
ittothe
Actor
forprocessing.Themethodthateventuallygetscalledwhenyou
tell
amessagetoan
ActorRef
isthe
receive
methodofthetargetActor.TheTeacherActorisarudimentaryclasswhichhas
a
List
ofquotesandobviouslythe
receive
methodwhichhandlesthemessages.
Checkthisout:
TeacherActor.scala
packageme
.
rerun
.
akkanotes
.
messaging
.
actormsg1

importscala
.
util
.
Random
importakka
.
actor
.
Actor
importme
.
rerun
.
akkanotes
.
messaging
.
protocols
.
TeacherProtocol
.
_
/*
* Your Teacher Actor class.
* The class could use refinement by way of
* using ActorLogging which uses the EventBus of the Actor framework
* instead of the plain old System out
*/
classTeacherActor
extendsActor
{
valquotes

=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"

)
defreceive

=
{
caseQuoteRequest

=>
{
importutil

.
Random
//Get a random Quote from the list and construct a response

valquoteResponse

=
QuoteResponse
(
quotes
(
Random
.
nextInt
(
quotes
.
size
)))
println
(
quoteResponse
)
}

}
TheTeacherActor'sreceivemethodpatternmatchesforjustoneMessagethe
QuoteRequest
(actually,it
isagoodpracticetopatternmatchthedefaultcasebutthere'saninterestingstorytotellthere)
Allthatthereceivemethoddoesisto
1. patternmatchfor
QuoteRequest
2. pickarandomquotefromthestaticlistofquotes
3. constructa
QuoteResponse
4. printtheQuoteResponsetotheconsole

CODE
Theentireprojectcouldbedownloadedfrom
githubhere
.

LOGGINGANDTESTINGACTORS2
Inthefirsttwoparts(
one
,
two
),webrieflytalkedaboutActorsandhowmessagingworks.Inthispart,let's
lookatfixingupLoggingandTestingour
TeacherActor
.

RECAP
ThisishowourActorfromthepreviouspartlookedlike:
classTeacherActor
extendsActor
{
valquotes

=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"

)
defreceive

=
{
caseQuoteRequest

=>
{
importutil

.
Random
//Get a random Quote from the list and construct a response

valquoteResponse

=
QuoteResponse
(
quotes
(
Random
.
nextInt
(
quotes
.
size
)))
println
(
quoteResponse
)
}

LOGGINGAKKAWITHSLF4J
Younoticethatinthecodeweareprintingthe
quoteResponse
tothestandardoutputwhichyouwould
obviouslyagreeisabadidea.Let'sfixthatupbyenablingloggingviatheSLF4JFacade.
1.FIXTHECLASSTOUSELOGGING

Akkaprovidesanicelittletraitcalled
ActorLogging
toachieveit.Let'smixthatin:
classTeacherLogActor
extendsActor
withActorLogging
{
valquotes

=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"

)
defreceive

=
{
caseQuoteRequest

=>
{

mportutil
i
.
Random
//get a random element (for now)

valquoteResponse

=
QuoteResponse
(
quotes
(
Random
.
nextInt
(
quotes
.
size
)))
log
.
info
(
quoteResponse
.
toString
())
}

}
//We'll cover the purpose of this method in the Testing section

defquoteList

=
quotes
}

Asmalldetourhere:
Internally,whenwelogamessage,thethe
logging
methodsintheActorLogging(eventually)publishesthe
logmessagetoan
EventStream
.Yes,Ididsay
publish
.So,whatactuallyisanEventStream?
EventStreamandLogging
EventStream
behavesjustlikeamessagebrokertowhichwecouldpublishandreceivemessages.One
subtledistinctionfromaregular
MOM
isthatthesubscribersoftheEventStreamcouldonlybeanActor.
Incaseofloggingmessages,alllogmessageswouldbepublishedtotheEventStream.Bydefault,the
Actorthatsubscribestothesemessagesisthe
DefaultLogger
whichsimplyprintsthemessagetothe
standardoutput.
classDefaultLogger
extendsActor
withStdOutLogger
{
override

defreceive
:Receive
=
{
...

caseevent

:LogEvent print
(
event
)
}

}
So,that'sthereasonwhenwetrytokickoffthe
StudentSimulatorApp
,weseethelogmessagewrittento
theconsole.
Thatsaid,EventStreamisn'tsuitedonlyforlogging.Itisageneralpurposepublishsubscribemechanism
availableinsidetheActorWorldinsideaVM(moreonthatlater).

2.CONFIGUREAKKATOUSESLF4J

akka
{
loggers
=
[
"akka.event.slf4j.Slf4jLogger"
]
loglevel
=
"DEBUG"
logging
-
filter
=
"akka.event.slf4j.Slf4jLoggingFilter"
}

Westorethisinformationinafilecalled
application.conf
whichshouldbeinyourclasspath.Inour
sbtfolderstructure,wewouldthrowthisinyour
main/resources
directory.
Fromtheconfiguration,wecouldderivedthat
1. the
loggers
propertyindicatestheActorthatisgoingtosubscribetothelogevents.What
Slf4jLogger
doesistosimplyconsumethelogsanddelegatethattotheSLF4JLoggerfacade.
2. the
loglevel
propertysimplyindicatestheminimumlevelthatshouldbeconsideredforlogging.
3. the
logging-filter
comparesthecurrentlyconfigured
loglevel
andincominglogmessage
levelandchucksoutanylogmessagebelowtheconfiguredloglevelbeforepublishingtothe
EventStream.
ButWhydidn'twehaveanapplication.confforthepreviousexample?
SimplybecauseAkkaprovidessomesanedefaultssothatweneedn'tbuildaconfigurationfilebeforewe
startplayingwithit.We'llrevisitthisfiletoooftenhereonforcustomizingvariousthings.Therearea
wholebunchofawesomeparametersthatyoucoulduseinsidethe
application.conf
forloggingalone.
3.THROWINALOGBACK.XML
We'llbeconfiguringanSLF4Jloggerbackedby
logback
now.
<?xml version="1.0" encoding="UTF-8"?>
<
configuration
>
<

appender
name
="
FILE
"
class

="
ch.qos.logback.core.rolling.RollingFileAppender
">
<

encoder
>
<

pattern
>
%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} %msg%n
</
pattern
>
</

encoder
>
<

rollingPolicy
class
="
ch.qos.logback.core.rolling.TimeBasedRollingPolicy
">
<

fileNamePattern
>
logs\akka.%d{yyyy-MM-dd}.%i.log
</
fileNamePattern
>
<

timeBasedFileNamingAndTriggeringPolicy
class
="
ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP
">
<

maxFileSize
>
50MB
</
maxFileSize
>
</

timeBasedFileNamingAndTriggeringPolicy
>
</

rollingPolicy
>
</

appender
>
<

root
level
="
DEBUG
">
<

appender-ref
ref
="
FILE
"
/>
</

root
>
</
configuration
>

Ithrewthisinsidethe
main/resources
foldertooalongwith
application.conf
.Pleaseensurethat
the
main/resources
isnowinyoureclipseorotherIDE'sclasspath.Alsoincludelogbackandslf4japi
toyour
build.sbt
.
Andwhenwekickoffour
StudentSimulatorApp
andsendamessagetoournew
TeacherLogActor
,
the
akkaxxxxx.log
filethatweconfiguredlookslikethis.

TESTINGAKKA

PleasenotethatthisisbynomeansanexhaustivecoverageofTestingAkka.Wewouldbebuildingour
testsonmorefeaturesofTestinginthefollowingpartsundertheirrespectivetopicheaders.Thesetestcases
areaimedtocovertheActorswewroteearlier.

Whilethe
StudentSimulatorApp
doeswhatweneed,youwouldagreethatitshouldbedrivenoutof
testcases.
Toeasethetestingpain,Akkacameupwithan
amazingtestingtoolkit
withwhichwecoulddosome
magicalstufflikeprobingdirectlyintotheActorimplementation'sinternals.
Enoughtalk,let'sseethetestcases.
Let'sfirsttrytomaptheStudentSimulatorApptoaTestcase.
Let'slookatthedeclarationalonenow.
classTeacherPreTest
extendsTestKit
(
ActorSystem
(
"UniversityMessageSystem"
))
withWordSpecLike

withMustMatchers

withBeforeAndAfterAll

{
So,fromthedefinitionoftheTestCaseclassweseethat:
1. The
TestKit
traitacceptsan
ActorSystem
throughwhichwewouldbecreatingActors.
Internally,theTestKitdecoratestheActorSystemandreplacesthedefaultdispatchertoo.
2. Weuse
WordSpec
whichisoneofthemanyfunwaystowritetestcaseswithScalaTest.
3. The
MustMatchers
provideconvenientmethodstomakethetestcaselooklikenaturallanguage

4. Wemixinthe
BeforeAndAfterAll
toshutdowntheActorSystemafterthetestcasesare
complete.The
afterAll
methodthatthetraitprovidesismorelikeour
tearDown
inJUnit

1,2SENDINGMESSAGETOACTORS
1)ThefirsttestcasejustsendsamessagetothePrintActor.Itdoesn'tassertanything:(
2)ThesecondcasesendsmessagetotheLogactorwhichusesthe
log
fieldoftheActorLoggingtopublish
themessagetotheEventStream.Thisdoesn'tassertanythingtoo:(
//1. Sends message to the Print Actor. Not even a testcase actually
"A teacher"must

{
"print a quote when a QuoteRequest message is sent"in

{
valteacherRef

=TestActorRef
[
TeacherActor
]
teacherRef
!QuoteRequest
}

//2. Sends message to the Log Actor. Again, not a testcase per se
"A teacher with ActorLogging"must

{
"log a quote when a QuoteRequest message is sent"in

{
valteacherRef

=TestActorRef
[
TeacherLogActor
]
teacherRef
!QuoteRequest
}

3ASSERTINGINTERNALSTATEOFACTORS
Thethirdcaseusesthe
underlyingActor
methodofthe
TestActorRef
andcallsuponthe
quoteList
methodofthe
TeacherActor
.The
quoteList
methodreturnsthelistofquotesback.Weusethislistto
assertitssize.
Ifreferenceto
quoteList
throwsyouback,refertothe
TeacherLogActor
codelistedaboveandlookfor
//From TeacherLogActor
//We'll cover the purpose of this method in the Testing section
defquoteList

=
quotes
//3. Asserts the internal State of the Log Actor.
"have a quote list of size 4"in

{
valteacherRef

=TestActorRef
[
TeacherLogActor
]
teacherRef
.
underlyingActor
.
quoteList must have size
(
4
)
teacherRef
.
underlyingActor
.
quoteList must have size
(
4
)
}

4ASSERTINGLOGMESSAGES
AswediscussedearlierintheEventStreamandLoggingsection(above),alllogmessagesgotothe
EventStream
andthe
SLF4JLogger
subscribestoitandusesitsappenderstowritetothelogfile/console
etc.Wouldn'titbenicetosubscribetotheEventStreamdirectlyinourtestcaseandassertthepresenceof
thelogmessageitself?Lookslikewecandothattoo.
Thisinvolvestwosteps:
1)Youneedtoaddanextraconfigurationtoyour
TestKit
likeso:
classTeacherTest
extendsTestKit
(
ActorSystem
(
"UniversityMessageSystem"
,
ConfigFactory
.
parseString
(
"""akka.loggers =
["akka.testkit.TestEventListener"]"""
)))
withWordSpecLike

withMustMatchers

withBeforeAndAfterAll

{
2)NowthatwehaveasubscriptiontotheEventStream,wecouldassertitfromourtestcaseas:
//4. Verifying log messages from eventStream
"be verifiable via EventFilter in response to a QuoteRequest that is sent"in{
valteacherRef

=TestActorRef
[
TeacherLogActor
]
EventFilter
.
info
(
pattern
=
"QuoteResponse*"
,occurrences
=
1
)intercept
{
teacherRef
!QuoteRequest
}

The
EventFilter.info
blockjustinterceptsfor1logmessagewhichstartswithQuoteResponse
(
pattern='QuoteResponse*
).(Youcouldalsoachieveitbyusinga
start='QuoteResponse'
.If
thereisnologmessageasaresultofsendingamessagetotheTeacherLogActor,thetestcasewouldfail.
5TESTINGACTORSWITHCONSTRUCTORPARAMETERS
PleasenotethatthewaywecreateActorsinthetestcaseisviathe
TestActorRef[TeacherLogActor]
andnotvia
system.actorOf
.ThisisjustsothatwecouldgetaccesstotheActor'sinternalsthroughthe
underlyingActor
methodintheTeacherActorRef.Wewouldn'tbeabletoachievethisviathe
ActorRef
thatwehaveaccessduringtheregularruntime.(Thatdoesn'tgiveusanyexcusetouse
TestActorRefinproduction.You'llbehunteddown).
IftheActoracceptsparameters,thenthewaywecreateTestActorRefwouldbelike:
valteacherRef
=TestActorRef
(
newTeacherLogParameterActor
(
quotes
))
Theentiretestcasewouldthenlooksomethinglike:
//5. have a quote list of the same size as the input parameter
" have a quote list of the same size as the input parameter"in
{
valquotes

=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"

)
valteacherRef

=TestActorRef
(
newTeacherLogParameterActor
(
quotes
))
//val teacherRef = TestActorRef(Props(new TeacherLogParameterActor(quotes)))

teacherRef
.
underlyingActor
.
quoteList must have size
(
4
)
EventFilter
.
info
(
pattern
=
"QuoteResponse*"
,occurrences
=
1
)intercept
{
teacherRef
!QuoteRequest
}

SHUTTINGDOWNACTORSYSTEM
Andfinally,the
afterAll
lifecyclemethod
override
defafterAll
()
{
super

.
afterAll
()
system
.
shutdown
()
}

CODE
Asalways,theentireprojectcouldbedownloadedfrom
githubhere
.

ACTORMESSAGINGREQUESTAND
RESPONSE3
Lasttime
whenwesawActormessaging,wesawhowfirenforgetmessagesaresent(Meaning,wejust
sendamessagetotheActorbutdon'texpectaresponsefromtheActor).
Technically,wefiremessagestoActorsforitssideeffectsALLTHETIME.Itisbydesign.Otherthannot
responding,thetargetActorcouldALSOdothefollowingwiththatmessage
1. Sendaresponsebacktothesender(inourcase,the
TeacherActor
wouldrespondwithaquote
backtothe
StudentActor
OR
2. ForwardaresponsebacktosomeotherActorwhomightbetheintendedaudiencewhichinturn
mightrespond/forward/haveasideeffect.RoutersandSupervisorsareexamplesofthosecases.
(we'lllookatthemverysoon)

REQUEST&RESPONSE
Inthiswriteup,we'llbefocussingonlyonPoint1therequestresponsecycle.

Thepictureconveyswhatwearetryingtoachievethistime.Forsakeofbrevity,Ididn'trepresentthe
ActorSystem,DispatcherorMailboxesinthepicture.
1. The
DriverApp
sendsan
InitSignal
messagetothe
StudentActor
.
2. The
StudentActor
reactstothe
InitSignal
messageandsendsa
QuoteRequest
messageto
the
TeacherActor
.
3. The
TeacherActor
,likewesawinthe
firstdiscussion
,respondswitha
QuoteResponse
.
4. The
StudentActor
justlogsthe
QuoteResponse
totheconsole/logger.
We'llalsocookupatestcasetoverifyit.
Let'slookatthese4pointsindetailnow:
1.THE
DRIVERAPP
SENDSAN
INITSIGNAL
MESSAGETOTHE
STUDENTACTOR

Bynow,youwouldhaveguessedwhatwouldthe
DriverApp
do.Just4things:
1)Initializethe
ActorSystem
//Initialize the ActorSystem
valsystem

=ActorSystem
(
"UniversityMessageSystem"
)
2)Createthe
TeacherActor
//create the teacher actor
valteacherRef

=system
.
actorOf
(
Props
[
TeacherActor
],
"teacherActor"
)

3)Createthe
StudentActor
//create the Student Actor - pass the teacher actorref as a constructor
parameter to StudentActor
valstudentRef

=system
.
actorOf
(
Props
(
newStudentActor
(
teacherRef
)),
"studentActor"
)
You'llnoticethatIampassinginthe
ActorRef
ofthe
TeacherActor
totheconstructorofthe
StudentActor
sothattheStudentActorcouldusetheActorRefforsendingmessagestothe
TeacherActor.Thereareotherwaystoachievethis(likepassinginthe
Props
)butthismethodwouldcome
inhandywhenwelookatSupervisorsandRoutersinthefollowingwriteups.We'llalsobelookingat
childactorsprettysoonbutthatwouldn'tsemanticallybetherightapproachhereStudentcreatingTeacher
doesn'tsoundnice.Doesit?
Lastly,
4)The
DriverApp
wouldthensendan
InitSignal
tothe
StudentActor
,sothattheStudentActor
couldstartsendingtheQuoteRequestmessagetotheTeacherActor.
//send a message to the Student Actor
studentRef
!InitSignal
That'sprettymuchtheDriverClass.The
Thread.sleep
andthe
ActorSystem.shutdown
arejustto
waitforacoupleofsecondsforthemessagesendingtofinishbeforewefinallyshutdownthe
ActorSystem.
DRIVERAPP.SCALA

packageme
.
rerun
.
akkanotes
.
messaging
.
requestresponse
importakka
.
actor
.
ActorSystem
importakka
.
actor
.
Props
importme
.
rerun
.
akkanotes
.
messaging
.
protocols
.
StudentProtocol
.
_
importakka
.
actor
.
ActorRef
objectDriverApp
extendsApp
{
/Initialize the ActorSystem
/
valsystem

=ActorSystem
(
"UniversityMessageSystem"
)
/construct the teacher actor
/
valteacherRef

=system
.
actorOf
(
Props
[
TeacherActor
],
"teacherActor"
)

/construct the Student Actor - pass the teacher actorref as a constructor


/
parameter to StudentActor
valstudentRef

=system
.
actorOf
(
Props
(
newStudentActor
(
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
REACTSTOTHE
INITSIGNAL
MESSAGEANDSENDSA
QUOTEREQUEST
MESSAGETOTHE
TEACHERACTOR
AND
4.THE
STUDENTACTOR
RECEIVESTHE
QUOTERESPONSE
FROM
TEACHERACTOR
ANDJUST
LOGSTOTHECONSOLE/LOGGER

WhydidIcombinePoints2and4?Becauseitissosimpleyou'llhatemeifIseparatethem.


So,Point2theStudentActorreceivesthe
InitSignal
messagefromthe
DriverApp
andsends
QuoteRequest
totheTeacherActor.
defreceive
=
{
caseInitSignal

=>
{
teacherActorRef
!
QuoteRequest
}

...

...

That'sit!!!
Point4TheStudentActorlogsthemessagethatitreceivesfromtheTeacherActor.


Just,aspromised:
caseQuoteResponse
(
quoteString
)
=>
{
log
.
info
(
"Received QuoteResponse from Teacher"
)
log
.
info
(
s
"Printing from Student Actor $quoteString"
)
}
Iamsureyou'dagreethatitalmostlookslikepseudocodenow.
So,theentire
StudentActor
classlookslike:
STUDENTACTOR.SCALA

packageme
.
rerun
.
akkanotes
.
messaging
.
requestresponse
importakka
.
actor
.
Actor
importakka
.
actor
.
ActorLogging
importme
.
rerun
.
akkanotes
.
messaging
.
protocols
.
TeacherProtocol
.
_
importme
.
rerun
.
akkanotes
.
messaging
.
protocols
.
StudentProtocol
.
_
importakka
.
actor
.
Props
importakka
.
actor
.
ActorRef

classStudentActor
(
teacherActorRef
:
ActorRef
)
extendsActor
withActorLogging
{
defreceive

=
{
caseInitSignal

=>
{
teacherActorRef
!
QuoteRequest
}

caseQuoteResponse

(
quoteString
)
=>
{
log
.
info
(
"Received QuoteResponse from Teacher"
)
log
.
info
(
s
"Printing from Student Actor $quoteString"
)
}

3.THE
TEACHERACTOR
RESPONDSWITHA
QUOTERESPONSE
.
Thisistheexactsamecodeaswesawinthe
firenforgetwriteup
.
TheTeacherActorreceivesa
QuoteRequest
messageandsends
QuoteResponse
back.
TEACHERACTOR.SCALA

packageme
.
rerun
.
akkanotes
.
messaging
.
requestresponse
importscala
.
util
.
Random
importakka
.
actor
.
Actor
importakka
.
actor
.
ActorLogging
importakka
.
actor
.
actorRef2Scala
importme
.
rerun
.
akkanotes
.
messaging
.
protocols
.
TeacherProtocol
.
_
classTeacherActor
extendsActor
withActorLogging
{
valquotes

=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"

)
defreceive

=
{
caseQuoteRequest

=>
{
importutil

.
Random

/Get a random Quote from the list and construct a response


/
valquoteResponse

=QuoteResponse
(
quotes
(
Random
.
nextInt
(
quotes
.
size
)))
/respond back to the Student who is the original sender of QuoteRequest
/
sender
!quoteResponse

TESTCASES
Now,ourtestcasewouldsimulatethe
DriverApp
.Since,theStudentActorjustlogsthemessageandwe
won'tbeabletoassertontheQuoteResponseitself,we'lljustassertthepresenceofthelogmessageinthe
EventStream(justlikewe
talkedlasttime
)
So,ourtestcaselookslike:
"A student"must
{
"log a QuoteResponse eventually when an InitSignal is sent to it"in

{
importme

.
rerun
.
akkanotes
.
messaging
.
protocols
.
StudentProtocol
.
_
alteacherRef
v
=system
.
actorOf
(
Props
[
TeacherActor
],
"teacherActor"
)
valstudentRef

=system
.
actorOf
(
Props
(
newStudentActor
(
teacherRef
)),
"studentActor"
)
EventFilter
.
info
(
start
=
"Printing from Student Actor"
,
occurrences
=
1
).
intercept
{
studentRef
!
InitSignal
}

CODE
Theentireprojectcouldbedownloadedfrom
githubhere
.

ACTORSYSTEM(CONFIGURATIONAND
SCHEDULING)4
Aswesawfromourpreviousposts,wecouldcreateanActorusingthe
actorOf
methodofthe
ActorSystem
.There'sactuallymuchmoreyoucoulddowithActorSystem.We'lltouchuponjustthe
ConfigurationandtheSchedulingbitinthiswriteup
Let'slookatthesubsetsofmethodsavailableinthe
ActorSystem
.

1.CONFIGURATIONMANAGEMENT
Rememberthe
application.conf
fileweusedforconfiguringourloglevelinthe
previouswriteup
?
Thisconfigurationfileisjustlikethose
.properties
filesinJavaapplicationsandmuchmore.We'llbe
soonseeinghowwecouldusethisconfigurationfiletocustomizeourdispatchers,mailboxesetc.
(Iamnot
evencloselydoingjusticetothepowerofthe
typesafeconfig
.Pleasegothroughsome
examples
toreally
appreciateitsawesomeness)

So,whenwecreatetheActorSystemusingtheActorSystemobject's
apply
methodwithoutspecifyingany
configuration,itlooksoutfor
application.conf
,
application.json
and
application.properties
intherootoftheclasspathandloadsthemautomatically.So,
valsystem
=
ActorSystem
(
"UniversityMessagingSystem"
)
isthesameas
valsystem
=
ActorSystem
(
"UniversityMessagingSystem"
,ConfigFactory
.
load
())
Toprovideevidencetothatargument,checkouttheapplymethodin
ActorSystem.scala
efapply
d
(
name
:
String
,config
:Option
[
Config
]
=None
,classLoader
:
Option
[
ClassLoader
]
=None
,defaultExecutionContext
:Option
[
ExecutionContext
]
=None
)
:ActorSystem
=
{
valcl

=classLoader
.
getOrElse
(
findClassLoader
())
valappConfig

=config
.
getOrElse
(
ConfigFactory
.
load
(
cl
))
newActorSystemImpl

(
name
,appConfig
,cl
,defaultExecutionContext
).
start
()
}

A.OVERRIDINGDEFAULTCONFIGURATION
Ifyouarenotkeenonusingthe
application.conf
(asintestcases)orwouldliketohaveyourown
customconfigurationfile(asintestingagaintdifferentconfigurationordeployingtodifferentenvironments),
youarefreetooverridethisbypassinginyourownconfigurationinsteadofwantingtheonefromthe
classpath.ConfigFactory.parseStringisoneoption
valactorSystem
=
ActorSystem
(
"UniversityMessageSystem"
,
ConfigFactory
.
parseString
(
"""akka.loggers =
["akka.testkit.TestEventListener"]"""
))
or
simplyinyourTestcaseas
classTeacherTestLogListener
extends
TestKit
(
ActorSystem
(
"UniversityMessageSystem"
,
ConfigFactory
.
parseString
(
"""akka.loggers =
["akka.testkit.TestEventListener"]"""
)))
withWordSpecLike

withMustMatchers

withBeforeAndAfterAll

{
There'salsoaConfigFactory.load
valsystem
=ActorSystem
(
"UniversityMessageSystem"
,
ConfigFactory
.
load
(
"uat-application.conf"
))

Ifyouneedaccesstoyourownconfigparametersinruntime,youcoulddoitviaitsAPIlikeso:
valsystem
=
ActorSystem
(
"UniversityMessageSystem"
,
ConfigFactory
.
parseString
(
"""akka.loggers =
["akka.testkit.TestEventListener"]"""
))
println
(
system
.
settings
.
config
.
getValue
(
"akka.loggers"
))// Results in >
SimpleConfigList(["akka.testkit.TestEventListener"])
B.EXTENDINGDEFAULTCONFIGURATION
Otherthanoverriding,youcouldalsoextendthedefaultconfigurationwithyourcustomconfiguration
usingthe
withFallback
methodofthe
Config
.Let'ssayyour
application.conf
lookslike:
akka
{
loggers
=
[
"akka.event.slf4j.Slf4jLogger"
]
loglevel
=DEBUG
arun
=
"hello"
}
andyoudecidetooverridethe
akka.loggers
propertylike:
valconfig

=
ConfigFactory
.
parseString
(
"""akka.loggers =
["akka.testkit.TestEventListener"]"""
)
valsystem

=
ActorSystem
(
"UniversityMessageSystem"
,
config
.
withFallback
(
ConfigFactory
.
load
()))
Youendupwithamergedconfigurationofboth:
println
(
system
.
settings
.
config
.
getValue
(
"akka.arun"
))//>
ConfigString("hello")
println
(
system
.
settings
.
config
.
getValue
(
"akka.loggers"
))//>
SimpleConfigList(["akka.testkit.TestEventListener"])
So,whydidItellthiswholestoryonconfiguration?Becauseour
ActorSystem
istheonewhichloadsand
providesaccesstoalltheconfigurationinformation.

IMPORTANTNOTE:
Watchouttheorderoffallingbackherewhichisthedefaultandwhichistheextensionconfiguration.
Remember,youhavetofallbacktothedefaultconfiguration.So,
config
.
withFallback
(
ConfigFactory
.
load
())
wouldworkbut
ConfigFactory
.
load
().
withFallback
(
config
)
wouldnotgettheresultsthatyoumayneed.

2.SCHEDULER

Asyoucanseefromthe
APIofActorSystem
,thereisapowerfullittlemethodinActorSystemcalled
scheduler
whichreturnsa
Scheduler
.The
Scheduler
hasavarietyof
schedule
methodswithwhich
wecoulddosomefunstuffinsidetheActorenvironment.
A.SCHEDULESOMETHINGTOEXECUTEONCE

Takingour
StudentTeacherexample
,assumeour
StudentActor
wouldwanttosendmessagetotheteacher
onlyafter5secondsofitreceivingthe
InitSignal
fromourTestcaseandnotimmediately,ourcode
lookslike:
classStudentDelayedActor
(
teacherActorRef
:
ActorRef
)
extendsActor
with
ActorLogging
{
defreceive

=
{
caseInitSignal

=>
{
importcontext

.
dispatcher
context
.
system
.
scheduler
.
scheduleOnce
(
5seconds
,teacherActorRef
,
QuoteRequest
)

/teacherActorRef!QuoteRequest
/
}

...

Testcase
Let'scookupatestcasetoverifythis:
"A delayed student"must
{
"fire the QuoteRequest after 5 seconds when an InitSignal is sent to it"

in
{
importme

.
rerun
.
akkanotes
.
messaging
.
protocols
.
StudentProtocol
.
_
valteacherRef

=system
.
actorOf
(
Props
[
TeacherActor
],
"teacherActorDelayed"
)
valstudentRef

=system
.
actorOf
(
Props
(
new
StudentDelayedActor
(
teacherRef
)),
"studentDelayedActor"
)
EventFilter
.
info
(
start
=
"Printing from Student Actor"
,
occurrences
=
1
).
intercept
{
studentRef
!
InitSignal
}

IncreasingthetimeoutforEventfilterinterception
Ouch.ThedefaulttimeoutfortheEventFiltertowaitforthemessagetoappearinthe
EventStream
is3
seconds.Let'sincreasethatto7secondsnowtoverifyourtestcase.The
filter-leeway
configuration
propertyhelpsusachievethat.
classRequestResponseTest
extends
TestKit
(
ActorSystem
(
"TestUniversityMessageSystem"
,
ConfigFactory
.
parseString
(
"""
akka{
loggers = ["akka.testkit.TestEventListener"]
test{
filter-leeway = 7s
}
}
"""
)))
withWordSpecLike

withMustMatchers

withBeforeAndAfterAll

withImplicitSender

{
...

...

B.SCHEDULESOMETHINGTOEXECUTEREPEATEDLY
Inordertoexecutesomethingrepeatedly,youusethe
schedule
methodoftheScheduler.
OneofthefrequentlyusedoverloadoftheschedulemethodistheonewhichsendsamessagetotheActor
onaregularbasis.Itacccepts4parameters:
1. Howlongshouldbeinitialdelaybebeforethefirstexecutionbegins
2. Frequencyofsubsequentexecutions
3. ThetargetActorRefthatwearegoingtosendamessageto
4. TheMessage
caseInitSignal
=>
{
importcontext

.
dispatcher
context
.
system
.
scheduler
.
schedule
(
0seconds
,
5seconds
,teacherActorRef
,
QuoteRequest
)
//teacherActorRef!QuoteRequest

TRIVIA
Theimport
import context.dispatcher
isveryimportanthere.
Theschedulemethodsrequiresaveryimportantimplicitparameter
ExecutionContext
,thereasonfor
whichwouldbeprettyobviousonceweseetheimplementationofthe
schedule
method:
final
defschedule
(
initialDelay
:FiniteDuration
,
interval
:FiniteDuration
,
receiver
:ActorRef
,
message
:
Any
)(
implicitexecutor
:ExecutionContext
,
sender
:ActorRef
=Actor
.
noSender
)
:Cancellable
=
schedule
(
initialDelay
,interval
,
newRunnable
{
defrun

=
{
receiver
!message
if

(
receiver
.
isTerminated
)
throw

newSchedulerException
(
"timer active for terminated actor"
)
}

})

Theschedulemethodjustwrapsthe
tell
ina
Runnable
whicheventuallyisexecutedbythe
ExecutionContextthatwepassin.

InordertomakeanExecutionContextavailableinscopeasanimplicit,weleverageupontheimplicit
dispatcheravailableonthecontext.
From
ActorCell.scala
(Context)
/**
* Returns the dispatcher (MessageDispatcher) that is used for this Actor.
* Importing this member will place an implicit ExecutionContext in scope.
*/
implicit

defdispatcher
:ExecutionContextExecutor

CODE
Asalways,theentireprojectcouldbedownloadedfrom
githubhere
.

ACTORLIFECYCLEBASIC5
(Pleasenotethatthislifecyclewriteupdoesnotcoverthe
preRestart
orthe
postRestart
methods.
We'lltalkaboutthemwhenwediscusssupervision)
ThebasicActorlifecycleisverymuchintuitive.YoucouldactuallycomparethebasicActorlifecyclewith
aJavaservletlifecyclewithonespecialdifference.
1. Justlikeanyotherregularclass,wehaveaConstructor
2. The
preStart
methodgetscalledbacknext.Here,youcouldinitializeresourcesthatyouwould
liketocleanupin
postStop
3. The"servicing"orthemessagehandlingbythe
receive
methodoccupiesthemajorchunkoftime
andthathappensinbetween.
Let'slookatasimpleactorwhichprintsthelifecycle.
DUMBLIFECYCLEACTOR
packageme
.
rerun
.
akkanotes
.
lifecycle
importakka
.
actor
.{
ActorLogging
,Actor
}
importakka
.
event
.
LoggingReceive
classBasicLifecycleLoggingActor
extendsActor
withActorLogging
{
log
.
info
(
"Inside BasicLifecycleLoggingActor Constructor"
)
log
.
info
(
context
.
self
.
toString
())
override

defpreStart
()
=
{
log
.
info
(
"Inside the preStart method of BasicLifecycleLoggingActor"
)
}

defreceive

=LoggingReceive
{
case

"hello"
=>log
.
info
(
"hello"
)
}

override

defpostStop
()
=
{
log
.
info
(
"Inside postStop method of BasicLifecycleLoggingActor"
)
}

}
APP
The
LifecycleApp
justinitiates,sendsamessagetotheActorandshutsdowntheActorSystem.
importakka
.
actor
.{
ActorSystem
,Props
}

objectLifecycleApp
extendsApp
{
valactorSystem

=
ActorSystem
(
"LifecycleActorSystem"
)
val

lifecycleActor
=
actorSystem
.
actorOf
(
Props
[
BasicLifecycleLoggingActor
],
"lifecycleActor"
)
lifecycleActor
!
"hello"
//wait for a couple of seconds before shutdown

Thread
.
sleep
(
2000
)
actorSystem
.
shutdown
()
}

OUTPUT
Inside BasicLifecycleLoggingActor Constructor
Actor
[
akka
:
/
/
LifecycleActorSystem
/
user
/
lifecycleActor#
-2018741361
]
Inside the preStart method of BasicLifecycleLoggingActor
hello
Inside postStop method of BasicLifecycleLoggingActor
What'sthatspecialdifferencebetweenServletsandthebasicActorlifecycle?
ThatthereisnodifferencebetweenconstructorandpreStartinActorlifecyclemoreorless.
ThereasonwhyIprintedthe
context.self
intheconstructoristhisunlikeServlets,Actorshaveaccess
tothe
ActorContext
eveninsidetheconstructor.ThedifferencebetweenthepreStartandtheconstructor
thenbecomesverysubtle.We'llrevisitthedifferencewhilewetalkaboutsupervisionbutifyouarecurious
callingthe
preStart
whentheActorrestarts(incaseoffailure)couldbecontrolled.Withconstructor,
thatisn'tpossible.
WHENISPOSTSTOPCALLED?
Aswesawfromtheprogram,the
postStop
getscalledwhentheActorSystemshutsdown.Therearea
coupleofothertimeswhenthecallbackgetsinvokedtoo.
1.ACTORSYSTEM.STOP()

WecouldstopanActorusingthe
stop
methodofthe
ActorSystem
andthe
ActorContext
objectLifecycleApp
extendsApp
{
valactorSystem

=
ActorSystem
(
"LifecycleActorSystem"
)
vallifecycleActor

=
actorSystem
.
actorOf
(
Props
[
BasicLifecycleLoggingActor
],
"lifecycleActor"
)
actorSystem
.
stop
(
lifecycleActor
);
...

2.ACTORCONTEXT.STOP

1)Eitherbywayofamessage(externallyorpassingamessagetoitself)
classBasicLifecycleLoggingActor
extendsActor
withActorLogging
{
...
...
defreceive

=LoggingReceive
{
case

"hello"
=>log
.
info
(
"hello"
)
case

"stop"
=>context
.
stop
(
self
)
}

and
objectLifecycleApp
extendsApp
{
valactorSystem

=
ActorSystem
(
"LifecycleActorSystem"
)
vallifecycleActor

=
actorSystem
.
actorOf
(
Props
[
BasicLifecycleLoggingActor
],
"lifecycleActor"
)
lifecycleActor
!
"stop"
...
...
2)ORkillitselffornoreason(thisisjustforfun.NoActorwithanambitionwoulddothis)
classBasicLifecycleLoggingActor
extendsActor
withActorLogging
{
log
.
info
(
"Inside BasicLifecycleLoggingActor Constructor"
)
log
.
info
(
context
.
self
.
toString
())
context
.
stop
(
self
)
...

...

3.POISONPILL

Inthepreviousexample,wepassedamessagecalledstopfromtheLifecycleApptotheActor.TheActor
receivedthatmessageandkilleditselfusinga
context.stop
.Wecouldachievethesamethingby
passinga
PoisonPill
messagetothetargetactor.Pleasenotethatthe
PoisonPill
message,justlikethe
previousstopmessagegetsenqueuedintheregularmailboxandwillbeprocessedwhenitturncomesup.
objectLifecycleApp
extendsApp
{
valactorSystem

=
ActorSystem
(
"LifecycleActorSystem"
)
vallifecycleActor

=
actorSystem
.
actorOf
(
Props
[
BasicLifecycleLoggingActor
],
"lifecycleActor"
)
lifecycleActor
!
PoisonPill
...

...

4.KILL

Insteadofsendinga
PoisonPill
,youcouldalsosenda
Kill
messagetothetargetActor.
lifecycleActor
!Kill

Betweensending
PoisonPill
or
Kill
messages,thedifferenceissubtlebutimportant.
1. With
PoisonPill
,a
Terminated
messageissenttoallwatchers(we'llseethatshortlyinour
DeathWatchpart).
2. Witha
Kill
message,an
ActorKilledException
isthrownbythehostActorwhichgets
propagatedtoitsSupervisor(we'llseethisshortlyinourSupervisionpart)

TRIVIA
WhatdoImeanregularmailbox?Istherea"special"mailboxtoo?Yup.Thereis.Andwe'lltalkaboutit
whenwetalkaboutitwhenwediscusssupervisionand
system
messages.

TERMINATION
OncetheActorisstopped,itissaidtoenterintoa
Terminated
state.Theimmediatequestionthatwould
comeuptoyourmindiswhatwouldhappentothemessagesthatissenttoanActorwhichisalready
terminated?Let'sseethat:
APP
objectLifecycleApp
extendsApp
{
valactorSystem

=
ActorSystem
(
"LifecycleActorSystem"
)
vallifecycleActor

=
actorSystem
.
actorOf
(
Props
[
BasicLifecycleLoggingActor
],
"lifecycleActor"
)
lifecycleActor
!
"hello"
lifecycleActor
!
"stop"
lifecycleActor
!
"hello"//Sending message to an Actor which is already
stopped
}
ACTORJUSTASBEFORE
classBasicLifecycleLoggingActor
extendsActor
withActorLogging
{
defreceive

=LoggingReceive
{
case

"hello"
=>log
.
info
(
"hello"
)
case

"stop"
=>context
.
stop
(
self
)
}

OUTPUT
BasicLifecycleLoggingActor
-hello
akka
.
actor
.
RepointableActorRef
-Message
[
java
.
lang
.
String
]from
Actor
[
akka
:
/
/
LifecycleActorSystem
/
deadLetters
]to
Actor
[
akka
:
/
/
LifecycleActorSystem
/
user
/
lifecycleActor#
-569230546
]was not
delivered
.
[1
]dead letters encountered
.This logging can be turned off or
adjusted with configuration settings
'akka.log-dead-letters'and
'akka.log-dead-letters-during-shutdown'
.
Fromthelogs,youseethattherearesomereferencesof
deadletters
.Anymessagethatyousendtoan
Actorthatisterminatedgetsforwardedtothemailboxofan
internal
Actorcalled
DeadLetterActor
.

CURIOUSASTOWHATHAPPENSNEXT?
TheDeadLetterActorprocessesthemessagesinitsmailbox,wrapseachmessageasa
DeadLetter
and
publishesittothe
EventStream
.
OneotherActorcalled
DeadLetterListener
consumesall
DeadLetter
sandpublishesthatasalog
message.Checkthis
out
.
Remember,whenwe
talkedaboutlogging
,wesawthatalllogmessagesgetspublishedtothe
EventStream
andthatwearefreetosubscribetothatEventStreamjustthatthesubscriberalsoneedstobeanActor.
Let'strythatnow.
Forourexample,we'llsubscribetotheEventStreamandwatchoutforallDeadLettermessagesandwill
printtotheconsole(somuchforcreativity??!!).Frankly,wearefreetodoanythingfromgeneratingan
alert,storingitintoadatabaseorevenfeedingintoanalytics.
SUBSCRIBINGTODEADLETTERSINEVENTSTREAM

importakka
.
actor
.
ActorSystem
importakka
.
actor
.
Props
importakka
.
actor
.
PoisonPill
importakka
.
actor
.
DeadLetter
importakka
.
actor
.
Actor
objectLifecycleApp
extendsApp
{
valactorSystem

=ActorSystem
(
"LifecycleActorSystem"
)
vallifecycleActor

=actorSystem
.
actorOf
(
Props
[
BasicLifecycleLoggingActor
],
"lifecycleActor"
)
valdeadLetterListener

=
actorSystem
.
actorOf
(
Props
[
MyCustomDeadLetterListener
])

actorSystem
.
eventStream
.
subscribe
(
deadLetterListener
,classOf
[
DeadLetter
])
lifecycleActor
!
"hello"
lifecycleActor
!
"stop"
lifecycleActor
!
"hello"

}
classMyCustomDeadLetterListener
extendsActor
{
defreceive

=
{
casedeadLetter

:DeadLetter
=>println
(
s
"FROM CUSTOM LISTENER
$deadLetter"
)
}

}
Output
164
[
LifecycleActorSystem
-
akka
.
actor
.
default
-
dispatcher
-4
]INFO
BasicLifecycleLoggingActor
-hello

167
[
LifecycleActorSystem
-
akka
.
actor
.
default
-
dispatcher
-4
]INFO
akka
.
actor
.
RepointableActorRef
-Message
[
java
.
lang
.
String
]from
Actor
[
akka
:
/
/
LifecycleActorSystem
/
deadLetters
]to
Actor
[
akka
:
/
/
LifecycleActorSystem
/
user
/
lifecycleActor#
-782937925
]was not
delivered
.
[1
]dead letters encountered
.This logging can be turned off or
adjusted with configuration settings
'akka.log-dead-letters'and
'akka.log-dead-letters-during-shutdown'
.
FROM CUSTOM LISTENER
DeadLetter
(
hello
,
Actor
[
akka
:
/
/
LifecycleActorSystem
/
deadLetters
],
Actor
[
akka
:
/
/
L
ifecycleActorSystem
/
user
/
lifecycleActor#
-782937925
])

CHILDACTORSANDACTORPATH6
Actorsarecompletelyhierarchical.WhateverActorsthatyoucreateHAStobeachildofsomeotherActor.
Let'sanalyzethatabit:

PATH
Say,wecreateanActorRefusing
ActorSystem.actorOf
andtrytoprintit's
path
.
valactorSystem
=
ActorSystem
(
"SupervisionActorSystem"
)
valactorRef
=
actorSystem
.
actorOf
(
Props
[
BasicLifecycleLoggingTeacherActor
])
println
(
actorRef
.
path
)// (prints) akka://SupervisionActorSystem/user/$a
Asyousee,apathlooksverysimilartoafilepathinafilesystem.
1. akka
hereisfixedbecausealltheseareaddressesofAkkaActorsmorelike
file://
or
http://
prefix(nothingtodowithprotocolthough).
2. The
SupervisionActorSystem
isjustthenameofyourActorSystemthatyoucreated.
3. We'lltalkaboutthe
user
inthenextsection.
4. The
$a
isthenameofyourActorthatthesystemgeneratedforyou.Howwouldyoulikeifyour
operatingsystemgeneratedrandomfilenamesforyourfiles?You'dobviouslyhateitbecauseyou
wouldwanttorefertothatnameinfuture.So,let'sgiveitapropermeaningfulnametoit:
valactorRef
=
actorSystem
.
actorOf
(
Props
[
BasicLifecycleLoggingTeacherActor
],
"teacherActor"
)
println
(
actorRef
.
path
)
// (prints)
akka://SupervisionActorSystem/user/teacherActor
That'sit.Now,thepathactuallymakessense.


CHILDACTORS
SimilartotoplevelactorswhichwecreateoutofActorSystem,wecouldalsocreatechildactorsoutofthe
ActorContext
.Infact,thepowerofActors'sfaulttoleranceprimarilyliewithinleveragingtheActor
hierarchyandtheabilityofaparenttomanagethelifeofchildactors.
Assumeyouhavea
TeacherSupervisor
andyouwouldliketocreatea
TeacherActor
tobeachildof
theSupervisor,youdoa
ActorContext.actorOf
insteadofa
ActorSystem.actorOf
:
classTeacherSupervisor
extendsActor
withActorLogging
{
valteacherActor

=
context
.
actorOf
(
Props
[
TeacherActor
],
"teacherActor"
)
...
...
Frankly,inanyapplication,youwillbecreatingawholelotofchildactorsthantoplevelactorswhich
meansthatyou'llbecallingalotmore
actorContext.actorOf
than
actorSystem.actorOf
.


You'llnoticethatthepathofthechildactoris
akka://SupervisionActorSystem/user/teacherSupervisor/teacherActor
,whichisvery
similartothepathyougetwhenyoucreateachildfolderwithinaparentfolder.
WhendoyoucreatechildActors?
Yougenerallycreatechildactorswhenaparticulartaskiscomposedofasubtaskormultiplesubtasks.You
alsocreateachildactorwhenaparticulartasktobeexecutedbytheparentiserrorproneandyouwould
wanttoisolateit(sothatifitfails,youcouldrecover).Whenthereisnoparentchildrelationshipbetween
tasks,thenyouDON'Tcreatechildactors.
Also,there'snothingwhichisstoppingachildactorfromcreatingchildrentodelegateitssubtasks.Actors
andtheircreationarereallycheapbutthepowerthatcomeswithitisamazing(we'llseeaboutthiswhile
wetalkaboutsupervision).
NOWWHATISTHAT
USER
INTHEPATH?

Forlackofacreativity,letmecomparethe
ActorSystem
toa
Unixfilesystem
witha
/
rootfolderand
allthose
/etc
,
/usr
,
/bin
andvariousotherfolders.
ActorSystemaremuchlikethat.ItcreatesafewtoplevelActorsthemostimportantbeingtherootActor
withthepath
/
,theuserActorwiththepath
/user
andasystemActorwiththepath
/system
.(there's
alsoa
/deadLetters
thatrepresentthe
DeadLetterActorRef
.Wesawthisinour
previouspost
)
Codewise,the
ActorSystem
composesthreeActorsinsideit(via
ActorRefProvider
).Thosearetherootfor
ALLtheActorsthatgetscreatedundertheActorSystem.
1. systemGuardian
actorrootofallactorsunder
/system
2. guardian
actorrootofallactorsunder
/user
and
3. rootGuardian
Actorrootofboththe
systemGuardian
andthe
userGuardian
actors.
/**

* Reference to the supervisor of guardian and systemGuardian; ....


*/
defrootGuardian

:InternalActorRef
**
/
* Reference to the supervisor used for all top-level user actors.
*/
defguardian

:LocalActorRef
**
/
* Reference to the supervisor used for all top-level system actors.
*/
defsystemGuardian

:LocalActorRef

/user(aka)userguardian
AnyActorthatyoucreateinyourprogramlikethe
StudentActor
orthe
TeacherActor
usingthe
ActorSystem
's
actorOf
methodwoulddirectlyfallunder
/user
.That'sthereasonyour
teacherActor
inthefirstpartofthiswriteuphadthepathslug
/user/teacherActor
.
/system(aka)systemguardian
Thesystemguardianshutsitselfdownwhenitnoticesthatthe
userGuardian
isdead.Makessense
consideringifthe
userGuardian
isdown,thenallthebusinessactorsunderitisalsodownandtherefore
alladministrativeactorsneedstobegonetoo.
WecouldseetwodistinctplaceswhereSystemActorsarebeingcreatedImeanactorsunderthe
/system
hierarchy.
1. Likewesaw
earlier
,anymessagethatyousendtoanActorthatisterminatedgetsforwardedtothe
mailboxofaninternalActorcalled
DeadLetterActor
.TheDeadLetterActorwrapseach
messageasa
DeadLetter
andpublishesittotheEventStream.OneotherActorcalled

DeadLetterListener
consumesallDeadLettersandpublishesthatasalogmessage.Now,the
DeadLetterListenerisasystemActorwithpath
/system/deadLetterListener
.
2. Rememberthe
TestEventListener
thatwecreatedinourprevious
writeup
tosubscribetolog
messagesintheEventStream?TheyareSystemactorstoo.Infact,all
akka.loggers
arecreated
asSystemactors.
classTeacherTest
extendsTestKit
(
ActorSystem
(
"UniversityMessageSystem"
,
ConfigFactory
.
parseString
(
"""akka.loggers =
["akka.testkit.TestEventListener"]"""
)))
...
...
Thedocumentation
here
saysthatanyActorthatisconfiguredintheconfigurationfilesandcreatedand
deployedintotheActorSystemwhileitstarts,fallsunderthe
/system
umbrella.Letmeupdatethispost
whenIfindsomethinginterestingaroundthis
/(aka)rootguardian
Aswesawearlier,the
/
Actoristheparentoftheuserandthesystemguardians.

TRIVIA
Technically,there'sasuperfluousparentfortherootactortoo.ThisActor'sonlyjobistoshutdownthe
entireActorSystemiftherootactorfails.Sinceit'sstrictlynotconsideredwithintheActorhierarchy,the
Akkateamcallsit:
private

[
akka
]
valtheOneWhoWalksTheBubblesOfSpaceTime
:InternalActorRef
=
new
MinimalActorRef
{
...

DEATHWATCH7
Whenwetalkedabout
Actorlifecycle
,wesawthatActorscouldbestoppedbyvariousmeans(using
ActorSystem.stoporActorContext.stoporsendinga
PoisonPill
there'salsothe
Kill
andthe
gracefulStop
).
WhateverreasonanActordies,therearecaseswhenafewotheractorsinthesystemwouldliketoknow
aboutit.Let'stakeatrivialexampleofanActorwhotalkstoadatabaselet'scallita
RepositoryActor
.
Forobviousreasons,therewouldbefewotheractorsinthesystemwhowouldbesendingmessagetothis
RepositoryActor
.These"interested"Actorswouldliketokeepan
eye
onor
watch
thisActorifitgoes
down.Now,thatinActortermsiscalled
DeathWatch
.Andthemethodsto
watch
and
unwatch
overthis
isintuitively
ActorContext.watch
and
ActorContext.unwatch
.Ifwatched,thewatcherswould
receivea
Terminated
messagefromthestoppedActorwhichtheycouldcomfortablyaddintotheir
receive
function.
Unlike
Supervision
,wherethereisastrictenforcementofparentchildhierarchy,anyActorcould
watch
anyotherActorintheActorSystem.

Let'shavealookatthecode.

CODE
QUOTEREPOSITORYACTOR
1. Our
QueryRepositoryActor
holdsabunchof
quotes
asaListandservesarandomoneupon
receivinga
QuoteRepositoryRequest
.
2. Itkeepstrackofthenumberofmessagesreceivedandifitreceivesmorethan3messages,itkills
itselfwitha
PoisonPill
Nothingfancyhere.
packageme
.
rerun
.
akkanotes
.
deathwatch
importakka
.
actor
.{
PoisonPill
,Actor
,ActorLogging
,actorRef2Scala
}
importme
.
rerun
.
akkanotes
.
protocols
.
QuoteRepositoryProtocol
.
_
importscala
.
util
.
Random
classQuoteRepositoryActor
()
extendsActor
withActorLogging
{
valquotes

=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"

)
varrepoRequestCount

:
Int
=
1
defreceive

=
{
caseQuoteRepositoryRequest

=>
{
if

(
repoRequestCount
>
3
){
self

!
PoisonPill
}

else

{
//Get a random Quote from the list and construct a response

valquoteResponse

=
QuoteRepositoryResponse
(
quotes
(
Random
.
nextInt
(
quotes
.
size
)))
log
.
info
(
s
"QuoteRequest received in QuoteRepositoryActor. Sending
response to Teacher Actor $quoteResponse"
)
repoRequestCount
=
repoRequestCount
+
1
sender
!quoteResponse
}

TEACHERACTORWATCHER
Again,nothingfancywith
TeacherActorWatcher
exceptthatitcreatesthe
QuoteRepositoryActor
andwatchesoveritusinga
context.watch
.
packageme
.
rerun
.
akkanotes
.
deathwatch
importakka
.
actor
.{
Terminated
,Props
,Actor
,ActorLogging
}
importme
.
rerun
.
akkanotes
.
protocols
.
TeacherProtocol
.
QuoteRequest
import
me
.
rerun
.
akkanotes
.
protocols
.
QuoteRepositoryProtocol
.
QuoteRepositoryRequest
classTeacherActorWatcher
extendsActor
withActorLogging
{
valquoteRepositoryActor

=
context
.
actorOf
(
Props
[
QuoteRepositoryActor
],
"quoteRepositoryActor"
)
context
.
watch
(
quoteRepositoryActor
)
defreceive

=
{
caseQuoteRequest

=>
{
quoteRepositoryActor
!QuoteRepositoryRequest
}

caseTerminated

(
terminatedActorRef
)
=>
{
log
.
error
(
s
"Child Actor {$terminatedActorRef} Terminated"
)
}

}
TESTCASES
Thisistheinterestingbit.Frankly,Ineverthoughtthatthesecouldbetested.
akkatestkit
FTW.Wewill
analyzethreetestcaseshere:
1.Assertreceiptof
Terminated
messageifwatched
The
QuoteRepositoryActor
shouldsendthetestcasea
Terminated
messageonreceiptofthe4th
message.Thefirstthreemessagesshouldgoinfine.
"A QuoteRepositoryActor"must
{
...

...

...

"send back a termination message to the watcher on 4th message"in

{
valquoteRepository

=
TestActorRef
[
QuoteRepositoryActor
]
valtestProbe

=
TestProbe
()
testProbe
.
watch
(
quoteRepository
)//Let's watch the Actor
within
(
1000millis
)
{
varreceivedQuotes

=List
[
String
]()
(

1to
3
).
foreach
(
_
=>quoteRepository
!QuoteRepositoryRequest
)

receiveWhile
()
{
caseQuoteRepositoryResponse

(
quoteString
)
=>
{
receivedQuotes
=receivedQuotes
:+quoteString
}

receivedQuotes
.
size must be
(
3
)
println
(
s
"receiveCount ${receivedQuotes.size}"
)
//4th message

quoteRepository
!
QuoteRepositoryRequest
testProbe
.
expectTerminated
(
quoteRepository
)
//Expect a Terminated
Message
}

2.Assertnonreceiptof
Terminated
messageifnotwatched/unwatched

Actually,weareoverdoingthingsjusttoshowcasethe
context.unwatch
.Thetestcasewouldworkjust
fineifweremovethe
testProbe.watch
and
testProbe.unwatch
lines.
"not send back a termination message on 4th message if not watched"in

{
valquoteRepository

=
TestActorRef
[
QuoteRepositoryActor
]
valtestProbe

=
TestProbe
()
testProbe
.
watch
(
quoteRepository
)//watching
within
(
1000millis
)
{
varreceivedQuotes

=List
[
String
]()
(

1to
3
).
foreach
(
_
=>quoteRepository
!QuoteRepositoryRequest
)
receiveWhile
()
{
caseQuoteRepositoryResponse

(
quoteString
)
=>
{
receivedQuotes
=receivedQuotes
:+quoteString
}

testProbe
.
unwatch
(
quoteRepository
)//not watching anymore
receivedQuotes
.
size must be
(
3
)
println
(
s
"receiveCount ${receivedQuotes.size}"
)
//4th message

quoteRepository
!
QuoteRepositoryRequest
testProbe
.
expectNoMsg
()//Not Watching. No Terminated Message
}

3.Assertreceiptof
Terminated
messageinthe
TeacherActorWatcher

WesubscribetotheEventStreamandcheckforaspecificlogmessagetoasserttermination.
"end back a termination message to the watcher on 4th message to the

TeacherActor"in
{
//This just subscribes to the EventFilter for messages. We have asserted

all that we need against the QuoteRepositoryActor in the previous testcase


valteacherActor

=
TestActorRef
[
TeacherActorWatcher
]

within
(
1000millis
)
{
(

1to
3
).
foreach
(
_
=>
teacherActor
!
QuoteRequest
)//this sends a message
to the QuoteRepositoryActor
EventFilter
.
error
(
pattern
=
"""Child Actor .* Terminated"""
,
occurrences
=
1
).
intercept
{
teacherActor
!
QuoteRequest//Send the dangerous 4th message
}

The
pattern
propertyofthe
EventFilter
,notsurprisingly,expectsaregexpattern.The
pattern="""Child Actor .* Terminated"""
isexpectedtomatchalogmessagewhichisofthe
format
Child Actor
{Actor[akka://TestUniversityMessageSystem/user/$$d/quoteRepositoryActor#-19059
87636]} Terminated
Asalways,thecodeisavailableat
github
.Watchforthe
deathwatch
package.

ACTORSUPERVISION8
Failuresaremorelikeafeatureamongdistributedsystems.AndwithAkka'sletitcrashfaulttolerance
model,youcouldachieveaclearseparationbetweenyourbusinesslogicandyourfailurehandlinglogic
(supervisionlogic).Allwithverylittleeffort.It'sprettyamazing.Thisisthetopicofourdiscussionnow.

ACTORSUPERVISION
ImagineamethodcallstackandthetopmostmethodinyourstackthrowsanException.Whatcouldbe
donebythemethodsdownthestack?
1. Theexceptioncouldbecaughtandhandledinordertorecover
2. Theexceptioncouldbecaught,maybeloggedandkeptquiet.
3. Themethodsdownthestackcouldalsochoosetoducktheexceptioncompletely(ormaybecaught
andrethrown)
Imagineifallthemethodsuntilthemainmethoddoesn'thandletheexception.Inthatcase,theprogram
exitsafterwritinganessayforanexceptiontotheconsole.

YoucouldalsocomparethesamescenariowithspawningThreads.Ifachildthreadthrowsanexception
andifthe
run
orthe
call
methoddoesn'thandleit,thentheexceptionisexpectedtobehandledbythe
parentthreadorthemainthread,whateverbethecase.Ifthemainthreaddoesn'thandleit,thenthesystem
exits.
Let'sdoitonemoretimeiftheChildActorwhichwascreatedusingthe
context.actorOf
failswithan
Exception,theparentactor(akasupervisor)couldprefertohandleanyfailuresofthechildactor.Ifitdoes,
itcouldprefertohandleitandrecover(
Restart
/
Resume
).Else,ducktheexception(
Escalate
)toit's
parent.Alternatively,itcouldjust
Stop
thechildactorthat'stheendofstoryforthatchild.WhydidIsay
parent(akasupervisor)?SimplybecauseAkka'sapproachtowardssupervisionisParentalsupervision
whichmeansthatonlythecreatorsoftheActorscouldsuperviseoverthem.
That'sit!!Wehaveprettymuchcoveredallthesupervision
Directives
(whatcouldbedoneaboutthe
failures).
STRATEGIES
Ah,Iforgottomentionthisone:YoualreadyknowthatanAkkaActorcouldcreatechildrenandthatthey
couldcreateasmanychildrenastheywant.
Now,considertwoscenarios:
1.OneForOneStrategy
YourActorspawnsmultiplechildactorsandeachoneofthesechildactorsconnecttodifferent
datasources.Sayyouarerunninganappwhichtranslatesanenglishwordintomultiplelanguages.


Suppose,onechildactorfailsandyouarefinetoskipthatresultinthefinallist,whatwouldyouwantto
do?Shutdowntheservice?Nope,youmightwanttojustrestart/stoponlythatchildactor.Isn'tit?Now
that'scalled
OneForOneStrategy
inAkkasupervisionstrategytermsIfoneactorgoesdown,just
handleonealone.
Dependingonyourbusinessexceptions,youwouldwanttoreactdifferently(
Stop
,
Restart
,
Escalate
,
Resume
)todifferentexceptions.Toconfigureyourownstrategy,youjustoverridethe
supervisorStrategy
inyourActorclass.
Anexampledeclarationof
OneForOneStrategy
wouldbe
importakka
.
actor
.
Actor
importakka
.
actor
.
ActorLogging
importakka
.
actor
.
OneForOneStrategy
importakka
.
actor
.
SupervisorStrategy
.
Stop
classTeacherActorOneForOne
extendsActor
withActorLogging
{
...

...

override

valsupervisorStrategy
=
OneForOneStrategy
()
{
case_

:MinorRecoverableException
=>Restart

case_

:Exception
=>Stop

...

...

2.AllForOneStrategy
Assumethatyouaredoingan
ExternalSort
(Onemoreexampletoprovethatmycreativitysucks!!),and
eachofyourchunkishandledbyadifferentActor.Suddenly,oneActorfailsthrowinganexception.It
doesn'tmakeanysensetocontinueprocessingtherestofthechunksbecausethefinalresultwouldn'tbe
correct.So,itislogicalto
Stop
~ALL~theactors.

WhydidIsay
Stop
insteadof
Restart
inthepreviousline?Because
Restart
ingwouldalsonotmake
anysenseforthisusecaseconsideringthemailboxforeachoftheseActorswouldnotbeclearedon
Restart.So,ifwerestart,therestofthechunkswouldstillbeprocessed.That'snotwhatwewant.
RecreatingtheActorswithshinynewmailboxeswouldbetherightapproachhere.

Again,justlikethe
OneForOneStrategy
,youjustoverridethe
supervisorStrategy
withan
implementationof
AllForOneStrategy
Andexamplewouldbe
importakka
.
actor
.{
Actor
,ActorLogging
}
importakka
.
actor
.
AllForOneStrategy
importakka
.
actor
.
SupervisorStrategy
.
Escalate
importakka
.
actor
.
SupervisorStrategy
.
Stop
classTeacherActorAllForOne
extendsActor
withActorLogging
{
...

override

valsupervisorStrategy
=AllForOneStrategy
()
{
case_

:MajorUnRecoverableException
=>Stop
case_

:Exception
=>Escalate
}

...

...

DIRECTIVES
Theconstructorofboth
AllForOneStrategy
andthe
OneForOneStrategy
acceptsa
PartialFunction[Throwable,Directive]
called
Decider
whichmapsa
Throwable
toa
Directive
asyoumayseehere:
case_
:MajorUnRecoverableException
=>Stop

Therearesimplyjustfourkindsofdirectives
Stop
,
Resume
,
Escalate
and
Restart
Stop
Thechildactorisstoppedincaseofexceptionandanymessagestothestoppedactorwouldobviouslygo
tothedeadLettersqueue.
Resume
Thechildactorjustignoresthemessagethatthrewtheexceptionandproceedswithprocessingtherestof
themessagesinthequeue.
Restart
Thechildactorisstoppedandabrandnewactorisinitialized.Processingoftherestofthemessagesinthe
mailboxcontinue.TherestoftheworldisunawarethatthishappenedsincethesameActorRefisattached
tothenewActor.
EscalateThesupervisorducksthefailureandletsitssupervisorhandletheexception.

DEFAULTSTRATEGY

WhatifourActordoesn'tspecifyanyStrategybuthascreatedchildActors.Howaretheyhandled?There
isadefaultstrategydeclaredinthe
Actor
traitwhich(ifcondensed)lookslikebelow:
override
valsupervisorStrategy
=
OneForOneStrategy
()
{
case_

:ActorInitializationException
=>Stop
case_

:ActorKilledException
=>Stop

case_

:DeathPactException
=>Stop

case_

:Exception
=>Restart

}
So,inessence,thedefaultstrategyhandlesfourcases:
1.ACTORINITIALIZATIONEXCEPTION=>STOP

WhentheActorcouldnotbeinitialized,itwouldthrowan
ActorInitializationException
.The
Actorwouldbestoppedthen.Let'ssimulateitbythrowinganexceptioninthe
preStart
callback:
packageme
.
rerun
.
akkanotes
.
supervision
importakka
.
actor
.{
ActorSystem
,Props
}
importme
.
rerun
.
akkanotes
.
protocols
.
TeacherProtocol
.
QuoteRequest
importakka
.
actor
.
Actor
importakka
.
actor
.
ActorLogging
objectActorInitializationExceptionApp
extendsApp
{
valactorSystem

=
ActorSystem
(
"ActorInitializationException"
)
valactor

=
actorSystem
.
actorOf
(
Props
[
ActorInitializationExceptionActor
],
"initializationExceptionActor"
)
actor
!
"someMessageThatWillGoToDeadLetter"
}
classActorInitializationExceptionActor
extendsActor
withActorLogging
{
override

defpreStart
=
{
throw

newException
(
"Some random exception"
)
}

defreceive

=
{
case_

=>
}

}
Runningthe
ActorInitializationExceptionApp
wouldgeneratea
ActorInitializationException
(duh!!)andthenmoveallthemessagesintothemessagequeueof
the
deadLetters
Actor:
Log

[
ERROR
]
[
11
/10/
2014
16
:
08
:
46.569
]
[
ActorInitializationException
-
akka
.
actor
.
default
-
dispatcher
-
2
]
[
akka
:
//
ActorInitializationException
/user/
initializationExceptionActor
]Some
random exception
akka
.
actor
.
ActorInitializationException
:exception during creation
at akka
.
actor
.
ActorInitializationException$
.
apply
(
Actor
.
scala
:
164
)
...
...
Caused by
:java
.
lang
.
Exception
:Some random exception
at
me
.
rerun
.
akkanotes
.
supervision
.
ActorInitializationExceptionActor
.
preStart
(
Acto
rInitializationExceptionApp
.
scala
:
17
)
...
...
[
INFO
]
[
11
/10/
2014
16
:
08
:
46.581
]
[
ActorInitializationException
-
akka
.
actor
.
default
-
dispatcher
-
4
]
[
akka
:
//
ActorInitializationException
/user/
initializationExceptionActor
]
Message
[
java
.
lang
.
String
]from
Actor
[
akka
:
//
ActorInitializationException
/deadLetters] to
Actor[akka://ActorInitializationException/
user
/
initializationExceptionActor#
-
1
290470495
]was not delivered
.
[
1
]dead letters encountered
.This logging can
be turned off or adjusted
withconfiguration settings
'akka
.
log
-
dead
-
letters'
and
'akka
.
log
-
dead
-
letters
-
during
-
shutdown'
.
2.ACTORKILLEDEXCEPTION=>STOP

WhentheActorwaskilledusingthe
Kill
message,thenitwouldthrowan
ActorKilledException
.
ThedefaultstrategywouldstopthechildActorifitthrowstheexception.Atfirst,itseemsthatthere'sno
pointinstoppinganalreadykilledActor.However,considerthis:
1. ActorKilledException
wouldjustbepropagatedtothesupervisor.Whataboutthelifecycle
watchersordeathwatchersofthisActorthatwesawduring
DeathWatch
.Thewatcherswon'tknow
anythinguntiltheActoris
Stopped
.
2. Sendinga
Kill
onanActorwouldjustaffectthatparticularactorwhichthesupervisorknows.
However,handlingthatwith
Stop
wouldsuspendthemailboxofthatActor,suspendsthe
mailboxesofchildactors,stopsthechildactors,sendsa
Terminated
toallthechildactor
watchers,senda
Terminated
toalltheimmediatefailedActor'swatchersandfinallystopthe
Actoritself.
(Wow,that'sprettyawesome!!)
packageme
.
rerun
.
akkanotes
.
supervision
importakka
.
actor
.{
ActorSystem
,Props
}
importme
.
rerun
.
akkanotes
.
protocols
.
TeacherProtocol
.
QuoteRequest
importakka
.
actor
.
Actor

importakka
.
actor
.
ActorLogging
importakka
.
actor
.
Kill
objectActorKilledExceptionApp
extendsApp
{
valactorSystem

=
ActorSystem
(
"ActorKilledExceptionSystem"
)
valactor

=
actorSystem
.
actorOf
(
Props
[
ActorKilledExceptionActor
])
actor
!
"something"
actor
!
Kill
actor
!
"something else that falls into dead letter queue"
}
classActorKilledExceptionActor
extendsActor
withActorLogging
{
defreceive

=
{
casemessage

:
String
=>log
.
info
(
message
)
}

}
Log
Thelogsjustsaythatoncethe
ActorKilledException
comesin,thesupervisorstopsthatactorand
thenthemessagesgointothequeueof
deadLetters
INFO m
.
r
.
a
.
s
.
ActorKilledExceptionActor
-something
ERROR akka
.
actor
.
OneForOneStrategy
-Kill
akka
.
actor
.
ActorKilledException
:Kill
INFO akka
.
actor
.
RepointableActorRef
-Message
[
java
.
lang
.
String
]from
Actor
[
akka
:
//
ActorKilledExceptionSystem
/deadLetters] to
Actor[akka://ActorKilledExceptionSystem/
user
/
$a#
-
1569063462
]was not
delivered
.
[
1
]dead letters encountered
.This logging can be turned off or
adjusted
withconfiguration settings
'akka
.
log
-
dead
-
letters' and
'akka
.
log
-
dead
-
letters
-
during
-
shutdown'
.
3.DEATHPACTEXCEPTION=>STOP

From
DeathWatch
,youknowthatwhenanActorwatchesoverachildActor,itisexpectedtohandlethe
Terminated
messageinits
receive
.Whatifitdoesn't?Yougetthe
DeathPactException

ThecodeshowsthattheSupervisor
watches
thechildactoraftercreationbutdoesn'thandlethe
Terminated
messagefromthechild.
packageme
.
rerun
.
akkanotes
.
supervision
importakka
.
actor
.{
ActorSystem
,Props
}
importme
.
rerun
.
akkanotes
.
protocols
.
TeacherProtocol
.
QuoteRequest
importakka
.
actor
.
Actor
importakka
.
actor
.
ActorLogging
importakka
.
actor
.
Kill
importakka
.
actor
.
PoisonPill
importakka
.
actor
.
Terminated
objectDeathPactExceptionApp
extendsApp
{
valactorSystem

=
ActorSystem
(
"DeathPactExceptionSystem"
)
valactor

=
actorSystem
.
actorOf
(
Props
[
DeathPactExceptionParentActor
])
actor
!
"create_child"//Throws DeathPactException
Thread
.
sleep
(
2000
)//Wait until Stopped
actor
!
"someMessage"//Message goes to DeadLetters
}

classDeathPactExceptionParentActor
extendsActor
withActorLogging
{
defreceive

=
{
case

"create_child"
=>
{
log
.
info
(
"creating child"
)
valchild

=
context
.
actorOf
(
Props
[
DeathPactExceptionChildActor
])
context
.
watch
(
child
)//Watches but doesnt handle terminated message.
Throwing DeathPactException here.
child
!
"stop"
}

case

"someMessage"
=>log
.
info
(
"some message"
)
//Doesnt handle terminated message

//case Terminated(_) =>

}
classDeathPactExceptionChildActor
extendsActor
withActorLogging
{
defreceive

=
{
case

"stop"
=>
{
log
.
info
(
"Actor going to stop and announce that it's terminated"
)
self

!
PoisonPill
}

}
Log
Thelogstellusthatthe
DeathPactException
comesin,thesupervisorstopsthatactorandthenthe
messagesgointothequeueof
deadLetters
INFO m
.
r
.
a
.
s
.
DeathPactExceptionParentActor
-creating child
INFO m
.
r
.
a
.
s
.
DeathPactExceptionChildActor
-Actor going to stop and announce
that it
'sterminated
ERROR akka
.
actor
.
OneForOneStrategy
-Monitored actor
[
Actor
[
akka
:
//
DeathPactExceptionSystem
/user/
$a
/$a#-695506341]] terminated
akka.actor.DeathPactException: Monitored actor
[Actor[akka://DeathPactExceptionSystem/
user
/$a/
$a#
-
695506341
]]terminated
INFO akka
.
actor
.
RepointableActorRef
-Message
[
java
.
lang
.
String
]from
Actor
[
akka
:
//
DeathPactExceptionSystem
/deadLetters] to
Actor[akka://DeathPactExceptionSystem/
user
/
$a#
-
1452955980
]was not delivered
.
[
1
]dead letters encountered
.This logging can be turned off or adjusted
with
configuration settings
'akka
.
log
-
dead
-
letters' and
'akka
.
log
-
dead
-
letters
-
during
-
shutdown'
.

4.EXCEPTION=>RESTART

ForallotherExceptions,thedefault
Directive
isto
Restart
theActor.Checkthefollowingapp.Justto
provethattheActorisrestarted,
OtherExceptionParentActor
makesthechildthrowanexceptionand
immediatelysendsamessage.Themessagereachesthemailboxandwhenthethechildactorrestarts,the
messagegetsprocessed.Nice!!

packageme
.
rerun
.
akkanotes
.
supervision
importakka
.
actor
.
Actor
importakka
.
actor
.
ActorLogging
importakka
.
actor
.
ActorSystem
importakka
.
actor
.
OneForOneStrategy
importakka
.
actor
.
Props
importakka
.
actor
.
SupervisorStrategy
.
Stop
objectOtherExceptionApp
extendsApp
{
valactorSystem

=
ActorSystem
(
"OtherExceptionSystem"
)
valactor

=
actorSystem
.
actorOf
(
Props
[
OtherExceptionParentActor
])
actor
!
"create_child"
}

classOtherExceptionParentActor
extendsActor
withActorLogging
{
defreceive

=
{
case

"create_child"
=>
{
log
.
info
(
"creating child"
)
valchild

=
context
.
actorOf
(
Props
[
OtherExceptionChildActor
])
child
!
"throwSomeException"
child
!
"someMessage"
}

}
classOtherExceptionChildActor
extendsakka
.
actor
.
Actor
withActorLogging
{
override

defpreStart
=
{
log
.
info
(
"Starting Child Actor"
)
}

defreceive

=
{
case

"throwSomeException"
=>
{
throw

newException
(
"I'm getting thrown for no reason"
)
}

case

"someMessage"
=>log
.
info
(
"Restarted and printing some Message"
)
}

override

defpostStop
=
{
log
.
info
(
"Stopping Child Actor"
)
}

}
Log
Thelogsofthisprogramisprettyneat.
1. Theexceptiongetsthrown.Weseethetrace
2. ThechildrestartsStopandStartgetscalled(we'llseeaboutthe
preRestart
and
postRestart
callbackssoon)
3. ThemessagethatwassendtotheChildactorbeforerestartisprocessed.
INFO m
.
r
.
a
.
s
.
OtherExceptionParentActor
-creating child
INFO m
.
r
.
a
.
s
.
OtherExceptionChildActor
-Starting Child Actor
ERROR akka
.
actor
.
OneForOneStrategy
-I
'mgetting thrown
forno reason
java
.
lang
.
Exception
:I
'mgetting thrown
forno reason
at
me
.
rerun
.
akkanotes
.
supervision
.
OtherExceptionChildActor$$anonfun$receive$
2
.
app
lyOrElse
(
OtherExceptionApp
.
scala
:
39
)
~
[
classes
/:
na
]
at akka
.
actor
.
Actor$
class
.
aroundReceive
(
Actor
.
scala
:
465
)
~
[
akka
-
actor_2
.11
-
2.3
.
4
.
jar
:
na
]
...
...
INFO m
.
r
.
a
.
s
.
OtherExceptionChildActor
-Stopping Child Actor

INFO m
.
r
.
a
.
s
.
OtherExceptionChildActor
-Starting Child Actor
INFO m
.r

.
a
.
s
.
OtherExceptionChildActor
-Restarted and printing some Message
ESCALATEANDRESUME

Wesawexamplesof
Stop
and
Restart
viathe
defaultStrategy
.Now,let'shaveaquicklookatthe
Escalate
.
Resume
justignorestheexceptionandproceedsprocessingthenextmessageinthemailbox.It'smorelike
catchingtheexceptionanddoingnothingaboutit.Awesomestuffbutnotalottotalkaboutthere.
Escalatinggenerallymeansthattheexceptionissomethingcriticalandtheimmediatesupervisorwouldnot
beabletohandleit.So,itaskshelpfromit'ssupervisor.Let'stakeanexample.
ConsiderthreeActors
EscalateExceptionTopLevelActor
,
EscalateExceptionParentActor
and
EscalateExceptionChildActor
.Ifthechildactorthrowsanexceptionandiftheparentlevelactor
couldnothandleit,itcould
Escalate
ittotheToplevelActor.TheToplevelactorcouldchoosetoreact
withanyoftheDirectives.Inourexample,wejust
Stop
.
Stop
wouldstoptheimmediatechild(whichis
the
EscalateExceptionParentActor
).Asyouknow,whena
Stop
isexecutedonanActor,allits
childrenwouldalsobestoppedbeforetheActoritselfisstopped.

packageme
.
rerun
.
akkanotes
.
supervision
importakka
.
actor
.
Actor
importakka
.
actor
.
ActorLogging
importakka
.
actor
.
ActorSystem
importakka
.
actor
.
OneForOneStrategy
importakka
.
actor
.
Props
importakka
.
actor
.
SupervisorStrategy
.
Escalate
importakka
.
actor
.
SupervisorStrategy
.
Stop
importakka
.
actor
.
actorRef2Scala
objectEscalateExceptionApp
extendsApp
{
valactorSystem

=ActorSystem
(
"EscalateExceptionSystem"
)
valactor

=actorSystem
.
actorOf
(
Props
[
EscalateExceptionTopLevelActor
],
"topLevelActor"
)
actor
!
"create_parent"
}
classEscalateExceptionTopLevelActor
extendsActor
withActorLogging
{
override

valsupervisorStrategy
=OneForOneStrategy
()
{
case_

:Exception
=>
{
log
.
info
(
"The exception from the Child is now handled by the Top level
Actor. Stopping Parent Actor and its children."
)
Stop//Stop will stop the Actor that threw this Exception and all its
children
}

defreceive

=
{
case

"create_parent"
=>
{
log
.
info
(
"creating parent"
)
valparent

=context
.
actorOf
(
Props
[
EscalateExceptionParentActor
],
"parentActor"
)
parent
!
"create_child"//Sending message to next level
}

}
classEscalateExceptionParentActor
extendsActor
withActorLogging
{
override

defpreStart
=
{
log
.
info
(
"Parent Actor started"
)
}

override

valsupervisorStrategy
=OneForOneStrategy
()
{
case_

:Exception
=>
{
log
.
info
(
"The exception is ducked by the Parent Actor. Escalating to
TopLevel Actor"
)
Escalate
}


}
defreceive

=
{
case

"create_child"
=>
{
log
.
info
(
"creating child"
)
valchild

=context
.
actorOf
(
Props
[
EscalateExceptionChildActor
],
"childActor"
)
child
!
"throwSomeException"
}

override

defpostStop
=
{
log
.
info
(
"Stopping parent Actor"
)
}

}
classEscalateExceptionChildActor
extendsakka
.
actor
.
Actor
withActorLogging
{
override

defpreStart
=
{
log
.
info
(
"Child Actor started"
)
}

defreceive

=
{
case

"throwSomeException"
=>
{
throw

newException
(
"I'm getting thrown for no reason."
)
}

override

defpostStop
=
{
log
.
info
(
"Stopping child Actor"
)
}

}
Log
Asyoucouldseefromthelogs,
1. Thechildactorthrowsexception.
2. Theimmediatesupervisor(
EscalateExceptionParentActor
)escalatesthatexceptiontoits
supervisor(
EscalateExceptionTopLevelActor
)
3. Theresultantdirectivefrom
EscalateExceptionTopLevelActor
istoStoptheActor.Asa
sequence,thechildactorsgetsstoppedfirst.
4. Theparentactorgetsstoppednext(onlyafterthewatchershavebeennotified)
INFO m
.
r
.
a
.
s
.
EscalateExceptionTopLevelActor
-creating parent
INFO m
.
r
.
a
.
s
.
EscalateExceptionParentActor
-Parent Actor started
INFO m
.
r
.
a
.
s
.
EscalateExceptionParentActor
-creating child
INFO m
.
r
.
a
.
s
.
EscalateExceptionChildActor
-Child Actor started
INFO m
.
r
.
a
.
s
.
EscalateExceptionParentActor
-The exception is ducked by the
Parent Actor
.Escalating to TopLevel Actor
INFO m
.
r
.
a
.
s
.
EscalateExceptionTopLevelActor
-The exception from the Child is

now handled by the Top level Actor


.Stopping Parent Actor and its children
.
ERROR akka
.
actor
.
OneForOneStrategy
-I
'mgetting thrown
forno reason
.
java
.
lang
.
Exception
:I
'mgetting thrown
forno reason
.
at
me
.
rerun
.
akkanotes
.
supervision
.
EscalateExceptionChildActor$$anonfun$receive$
3
.
applyOrElse
(
EscalateExceptionApp
.
scala
:
71
)
~
[
classes
/:
na
]
...

...

INFO m
.
r
.
a
.
s
.
EscalateExceptionChildActor
-Stopping child Actor
INFO m
.
r
.
a
.
s
.
EscalateExceptionParentActor
-Stopping parent Actor
Pleasenotethatwhateverdirectivethatwasissuedwouldonlyapplytotheimmediatechildthatescalated.
Say,ifa
Restart
isissuedattheTopLevel,onlytheParentwouldberestartedandanythinginits
constructor/
preStart
wouldbeexecuted.IfthechildrenoftheParentactorwascreatedintheconstructor,
theywouldalsobecreated.However,childrenthatwerecreatedthroughmessagestotheParentActor
wouldstillbeinthe
Terminated
state.
TRIVIA
Actually,youcouldcontrolwhetherthe
preStart
getscalledatall.We'llseeaboutthisinthenextminor
writeup.Ifyouarecurious,justhavealookatthe
postRestart
methodofthe
Actor
defpostRestart
(
reason
:Throwable
)
:
Unit
=
{
preStart
()
}

CODE
Asalways,codeison
github
(my
.gitignore
wasn'tsetuprightforthisproject.willfixittoday.sorry)

You might also like