In the initial version, only an Actorsystem (name Akka-Network-Ping) is created.
No Actors are created yet, but there's a command line parser that accepts the following commands from the command-line:
[pingerCount] pi|ping [pingCount] [pingInterval]
s|status
q|quit
The ping (alias pi) will, in a subsequent exercise, create pingerCount Pinger actor(s) that will ping a single PingServer actor. The PingServer actor should respond to a Ping message with a Response message to the actor who sent the Ping message.
In this state of the application, messages are logged at INFO level.
Note that the logging output is directed to a log file named pingpong.log in the project's root folder.
The overall Actor hierarchy will looks as follows:
/user
|
/PingResponseCoordinator
|
+-----------------+----------------+
| |
/pingMaster /pingServer
|
+---------+---------+---------+
| | | |
/pinger1 /pinger2 ... /pingerN
In the first exercise, you will set-up actors PingResponseCoordinator, PingMaster, Pinger and PingServer.
In this exercise, you will define four actors, PingResponseCoordinator, PingMaster, Pinger and PingServer, each with an empty behaviour and a definition of the messages, Ping and Response, relevant to the protocol we want to implement.
Your tasks:
- Change the behaviour of the PingServer actor so that when it receives a
Pingmessage, it responds to the sender of the message with aResponsemessage. Note that both messages carry extra information:
- sequenceNumber: a number that is unique for each
Pingmessage for a particularPingactor.
-
In the
Responseresponse, the value ofrefcontained in thePingmessage should be copied as-is. -
Change the behaviour of the
Pingeractor so that, after creation, it will sendpingCountPingmessages to thePingServeractor. ThesequenceNumbershould be incremented between subsequentPingmessages. For the time being, ignorepingInterval: just send the messages in rapid succession. -
Change the
PingResponseCoordinatoractor. When an instance of this actor is created, it should create aPingServerand aPingMasteractor as children. Name these actorspingServerandpingMasterrespectively. -
When
PingResponseCoordinatorreceives aCreatePingermessage, it should forward this message to thePingMasteractor. -
Create the
PingResponseCoordinatoractor inPingResponseApp(look for the TODO in the code). -
Change the
PingMasterbehaviour as to create aPingerchild actor when it receives aCreatePingermessage. -
Adapt the
createPingermethod in thePingResponseApp(look for the TODO in the code) as to send the appropriate messages to the right actor so that the requested number ofPingeractors are created. -
Run the application, run some ping commands and verify that the output is what is expected.
This code implements a solution for the stated tasks. However, some questions may be raised:
-
How to implement the
pingIntervalbehaviour - whenpingCount> 1: the firstPingshould be sent immediately and subsequentPingmessages should be sent sequentially atpingIntervalintervals. -
In the current application, there's an obvious memory leak:
Pingactors are created and, after having performed their task, are never stopped... -
A number of tests have been added to the project that test the functionality of the
PingerandPingServeractors.
##Exercise 2 - Implementing pingInterval
-
Change
Pingerto send thePingmessages at the specified interval. Have a look at an actor's access to the scheduler (context.system.scheduler.*). Note that we always sendpingCountmessages, i.e. regardless of the number ofResponsemessages that are sent back in response. -
Fix the memory leak, for example, let the
Pingactor stop itself when it has done its job. For this, look at what's available under the actor's context.
##Exercise 2 - Solution & discussion
The sending of Ping messages at regular intervals can be implemented in many ways... In the given solution, the Pinger actor uses the scheduler to schedule delivery of the required Ping messages to itself at the right moment.
-
Also note the utilisation of
context.stop(self)to stop thePingeractor. -
Notice the need to bring an (implicit) execution scope into context - this is needed by the
scheduleOncemethod
##Exercise 3 - Introduce reaction time in PingServer actor
In this exercise, we will let PingServer actor respond to Ping messages with a delay configurable in the application config settings. Change the signature of the PingServer actor as to accept this delay parameter of type FiniteDuration.
Name the configuration parameter AkkaNetworkPing.Response.responseDelay
Perform some ping tests with the responseDelay set to 2 seconds. What is the impact on the behaviour of the application? What is causing this effect?
##Exercise 3 - Solution & discussion
In the proposed solution, we make sure that the PingServer actor is really busy while responding to a Ping. We do this by ignoring any incoming message during during the processing of the Ping. In order to achieve this, we utilise the Akka Stash trait.
##Exercise 3 - Solution - Alternative Pinger implementation
An alternative for the utilisation of mutable state in the Pinger actor.
##Exercise 4 - Scale the PingServer actor
As observed during some testing, the single instance of the PingServer actor has become a limiting factor for the ping-pong throughput.
Use a pooled router with round-robin routing strategy configured via Akka configuration to scale the app.
##Exercise 4 - Solution & discussion
Making the Akka-Network-Ping scale can be achieved by removing the bottleneck in the PingServer actor by turning it into a pooled router. Notice the elegance by which this can be done: a simply change in the deployment configuration and a change on one line in the source code.
##Exercise 5 - Introduction of an unreliable PingServer actor
Let's make PingServer a bit unreliable by having it not send a reply to a Ping message at random:
- Define a configuration parameter named
AkkaNetworkPing.Response.reliabilitywith a value between 0 and 100. - Change the signature of
PingServeras to accept thereliabilityparameter of typeInt. - Change the behaviour of
PingServerso that when it receives aPing, it throws a dice by generating a random number between 0 and 100. If this number is greater thanreliability, respond. Otherwise, ignore the message and continue waiting for a newPing.