Created
October 29, 2014 12:25
-
-
Save thomasdarimont/7cd7cb07613d732b57f9 to your computer and use it in GitHub Desktop.
akka Actor API and Actor DSL example for ping pong
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package example | |
import akka.actor._ | |
case object PingMessage | |
case object PongMessage | |
case object StartMessage | |
case object StopMessage | |
class Ping(pong: ActorRef) extends Actor { | |
var count = 0 | |
def incrementAndPrint { | |
count += 1; println("ping") | |
} | |
def receive = { | |
case StartMessage => | |
incrementAndPrint | |
pong ! PingMessage | |
case PongMessage => | |
incrementAndPrint | |
if (count > 99) { | |
sender ! StopMessage | |
println("ping stopped") | |
context.stop(self) | |
} else { | |
sender ! PingMessage | |
} | |
} | |
} | |
class Pong extends Actor { | |
def receive = { | |
case PingMessage => | |
println(" pong") | |
sender ! PongMessage | |
case StopMessage => | |
println("pong stopped") | |
context.stop(self) | |
} | |
} | |
/** | |
* @author Thomas Darimont | |
*/ | |
object AkkaActorApiExample extends App{ | |
println("Akka scala example") | |
val system = ActorSystem("PingPongSystem") | |
val pong = system.actorOf(Props[Pong], name = "pong") | |
val ping = system.actorOf(Props(new Ping(pong)), name = "ping") | |
// start them going | |
ping ! StartMessage | |
} | |
/* | |
As you can see from the import statement, I'm using Akka actors, | |
not the traditional/older Scala actors. | |
When you implement an Akka actor, you define the receive method, | |
and implement your desired behavior in that method. | |
Messages between actors should be immutable, and | |
case classes are great for this purpose. | |
The code in my PingPongTest class shows the two ways to create Akka actors | |
(see the 'val pong' and 'val ping' lines.) I've written more | |
about this in my Simple Scala Akka actors examples tutorial. | |
I get everything started by sending a StartMessage to my "ping" actor. | |
After that, the two actors bounce messages back and forth as fast as they can until they stop. | |
*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package example | |
import akka.actor.ActorDSL._ | |
import akka.actor.{Props, ActorSystem} | |
case object PingMessage | |
case object PongMessage | |
case object StartMessage | |
case object StopMessage | |
/** | |
* @author Thomas Darimont | |
*/ | |
object AkkaActorDslExample extends App{ | |
println("Akka scala example") | |
implicit val system = ActorSystem("PingPongSystem") | |
var pong = actor(new Act{ | |
become { | |
case PingMessage => | |
println(" pong") | |
sender ! PongMessage | |
case StopMessage => | |
println("pong stopped") | |
context.stop(self) | |
} | |
}) | |
var ping = actor(new Act{ | |
var count = 0 | |
def incrementAndPrint { | |
count += 1; println("ping") | |
} | |
become { | |
case StartMessage => | |
incrementAndPrint | |
pong ! PingMessage | |
case PongMessage => | |
incrementAndPrint | |
if (count > 99) { | |
sender ! StopMessage | |
println("ping stopped") | |
context.stop(self) | |
} else { | |
sender ! PingMessage | |
} | |
} | |
}) | |
// start them going | |
ping ! StartMessage | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Everything looks great in both code samples.
If I were to review something I'd get rid of Start and StopMessage. Actors should not have that knowledge.
Then, from the 'main' code you could already send a ping to pingActor and have the actors start their thing.
that still leaves the ending open... well, let them crash! If Ping has a ref to Pong, then Ping could context.watch(pong) and implement 'case Terminated(pong)'. Then, I'd move the logic if(x>99) to pong and have pong suicide when 100 balls exchanged (like you already do with context.stop(self)). Upon pong's suicide, ping would run the case Terminate and should suicide too.
In that case you should call them Romeo and Juliet.
Also, I'd get rid of the XxxxMessage notation. You can create an object and put the case objects inside. For example:
In occasions, and actually here it may be a good option, message case object/class are placed in a companion object of the actor so if I know I'm sending s/thin to Pong, I can do Pong.Reverse or Pong.Smash, etc...
There's a thing I hate about the ping pong example is that it's misleading to me. I don't have any friend who are named Ping nor Pong. Also, in this example Ping and Pong (created as different Actors) should be the same class definition and main should instantiate two. Then, messages would not be Ping and Pong, they would be something more interesting: Reverse, Trick, Smash, etc... ;-). But that's an opinion that has nothing to do with scala idiomacy.
About the second example, it looks great to me. I was not aware of the ActorDSL and it looks quite good actually. I went and read the docs and I think I'll eventually use it.
It all looks pretty good to me.