I'm evaluating Akka for a distributed service layer, the following example prints Hello {n} 10 times, but does it one after the other. As I understand it this is intentional for an Akka actor, so where do I go from here to make it concurrent?
import akka.actor._
object HelloActor {
case class SayHello(message: String)
}
class HelloActor extends Actor {
def receive = {
case HelloActor.SayHello(message) =>
Thread.sleep(1000)
println(message)
}
}
object Main extends App {
val system = ActorSystem("ActorSystem")
val hello = system.actorOf(Props[HelloActor])
for (i <- 1 to 10) {
hello ! HelloActor.SayHello(s"Hello $i")
}
}
I've experimented with creating multiple actors from the Main class but that feels wrong somehow, shouldn't I just call the actor then it handles concurrency / spawning more actors on its own? Could anyone provide an example of this (preferably modifying the above code). I've been reading and reading but it feels like a lot to take in immediately and I feel I'm just missing a key concept here somewhere.
Akka's approach to handling concurrency is based on the Actor Model. In an actor-based system, everything is an actor, in much the same way that everything is an object in object-oriented design.
The key abstraction in Akka is the Actor, which provides behavior and can store state. In Akka, actors are guaranteed to be run in a single-threaded illusion, which means that the Akka framework takes care of threading issues while allowing us to focus on the behavior that needs to be implemented.
Akka is a truly reactive framework because everything in the sense of sending and receiving a message to Actors, is lock-less, non-blocking IO, and asynchronous.
Spring, Scala, Erlang, Kafka, and Spring Boot are the most popular alternatives and competitors to Akka.
For your use case you'll probably want to use Routers.
For example:
val hello = system.actorOf(Props[HelloActor].withRouter(
RoundRobinRouter(nrOfInstances = 10)))
hello ! HelloActor.SayHello("Hello!") // Sends to one of the 10
As a side note, you should avoid blocking (ie. Thread.sleep
) in your actor's receive
method.
As @sourcedelica mentioned in the comments, routing is probably what you want to do. A simple refactor to your example using a RoundRobinRouter
could look like this:
import akka.actor._
import akka.routing._
object HelloActor {
case class SayHello
}
class HelloActor extends Actor {
def receive = {
case HelloActor.SayHello =>
println(s"saying hello from: ${self.path}")
}
}
object Main extends App {
val system = ActorSystem("ActorSystem")
val hello = system.actorOf(Props[HelloActor].withRouter(RoundRobinRouter(10)))
for (i <- 1 to 10) {
hello ! HelloActor.SayHello
}
}
This is a pretty simple example as it's using a simple router (round robin) and it's non-resizing. You can do a lot more with the routers and I highly suggest reading up on them more at:
http://doc.akka.io/docs/akka/2.2.3/scala/routing.html
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With