I'm started to learn Akka and in many official examples I see that request-response implemented using tell pattern. I.e. after worker made his work he sends result as new message back to sender. For example in this Pi approximation official tutorial shown how to design application where Master sends some work to workers and then awaits for results as another message.
Master code:
def receive = {
case Calculate ⇒
for (i ← 0 until nrOfMessages) workerRouter ! Work(i * nrOfElements, nrOfElements)
case Result(value) ⇒
pi += value
nrOfResults += 1
if (nrOfResults == nrOfMessages) {
// Send the result to the listener
listener ! PiApproximation(pi, duration = (System.currentTimeMillis - start).millis)
// Stops this actor and all its supervised children
context.stop(self)
}
}
Worker code:
def receive = {
case Work(start, nrOfElements) ⇒
sender ! Result(calculatePiFor(start, nrOfElements)) // perform the work
}
But I'm wondering why this example didn't use ask pattern? What is wrong with using ask pattern here?
If it's ok to use ask pattern here, then I have another question: how I can stop all worker actors after work is done?
Should my worker actors send PoisonPill message to themselves?
Or should Master actor Broadcast(PoisonPill)?
Or there some another more elegant way?
ask is useful when integrating actors with Future APIs, but they do introduce some overhead. Further, Future completions do not come through an actor's mailbox and are scheduled on a thread separate from the one used by the mailbox, meaning that receiving a future completion inside an actor introduces the need for doing multi-thread coordination.
Meanwhile, if you use tell to send to a worker and have it tell the sender in response, the communication will always flow properly through the mailbox channel.
Additionally, it is much easier to discern the inputs an actor deals with if they all come in via receive rather than some coming in via receive and other's as completions of ask messages.
To address you PoisonPill questions, the answer is likely depends. You might opt for a worker-per-message approach in which case the worker should kill itself. If instead you use a worker pool, you could make the supervisor responsible for scaling that pool up and down, having it send the PoisonPill or have workers time out on idleness, once again killing themselves.
Related
I'm currently porting an Akka Classic app to Akka Typed. I have the following components:
HttpService - Not an Actor
JobDispatcher - An Actor
JobWorker - Child actor of JobDispatcher
The JobDispatcher is a singleton actor that orchestrates jobs. Each JobWorker is responsible for one "job" and knows the job's status.
The HTTP service will make an Ask to JobDispatcher, called GetJobStatuses.
The JobDispatcher will then Ask each of the JobWorkers what their status is, aggregate the results into a list, and reply to HttpService.
The way I did this in Akka Classic was to have JobDispatcher do all the Asks, put the Futures into a list of Futures, and then transform that into a Future of Lists, and when that aggregate Future completed, I would send the results to HttpService. It looked something like this:
val statusFutures: Seq[Future[JobStatus]] = jobWorkers map (jobWorker => (jobWorker ? GetJobStatus).mapTo[JobStatus])
val aggregateFuture: Future[Seq[SearchStatus]] = Future.sequence(statusFutures)
val theSender = context.sender()
aggregateFuture onComplete {
case Success(jobStatuses: Seq[JobStatus]) => {
theSender ! jobStatuses
}
case Failure(exception) => {
theSender ! exception
}
}
So, now that we're moving to Akka Typed, we're not supposed to use Futures / onComplete, but instead turn the Ask response into a message back ourself (JobDispatcher in this case). This is fairly straightforward for simple situations where I'm Asking one other actor for one reply. But in this case, I have a whole list of child actors from which I need to compile their responses.
The only thing I can think of is to make JobDispatcher hold a "state" of a list of JobWorker responses that I'm waiting for, track of which ones have been received, and when I've received them all, send a response message back to the HTTP service. This is further complicated by the fact that I may have multiple simultaneous Asks coming in from the HTTP service, and I'd have to track multiple copies of this "state", and somehow identify which HTTP request each one is for.
This is all WAY more complicated than the the aggregate Future solution above.
What is the simple/correct way to handle situations like this in Akka Typed?
The docs suggest using a per-session child actor for this situation. The child actor, being only associated with a single HTTP request is implicitly tracking exactly one copy of that state and is also able to manage the state of the process of scatter/gathering jobs (e.g. around timeouts and retries).
It's also worth noting that the example classic code has a massive bug: never call sender in code involving futures. Mixing futures and actors is superficially easy but also easy to turn into something that only works by coincidence (with tests often exhibiting that coincidental behavior).
Say I have an IO Actor connection capable of sending and receiving messages over TCP. In my actor, I ask the other side of the connection for a response:
connection.ask(ByteString("stuff")).collect {
case Received(response) => log.debug(response.utf8String)
}
It appears that with this code the ask future times out, and instead the containing actor receives the raw message outside of the ask pattern.
Can you use the ask pattern with Akka IO actors? If not, why not?
I don't know the architecture in detail, but here's how I would explain it to myself:
The problem with Akka IO connector actors here is that they don't work in a request-response manner. And if you think about it - that makes sense, because TCP is not a request-response protocol. TCP doesn't even have a notion of message. From programmer's perspective TCP connection is just a pair of continuous byte streams - one in each direction. That's it.
Akka IO is a minimal actor layer on top of network protocols, so it's not surprising that it mimics this behaviour. When TCP actor receives some data from the network, it only knows one thing - that it should send a Received message to the actor that originally sent the Connect message. That's all. It has no idea that the data it received from the network is somehow related to the data that you sent earlier.
Adding to all of that, ask pattern works only under assumption that when you send message A to some actor, it will respond with the message B by sending it exactly to the sender of message A. As we already know, TCP actor doesn't do that - it simply sends everything to the sender of original Connect message.
The reason why this assumption is required is that ask pattern actually creates some sort of a "phantom" actor that is set as the sender of the message sent using ask. This "phantom" actor will then receive the response and invoke all the callbacks registered on the Future. As a side note - be aware that those callbacks are invoked completely independently of the sending actor, i.e. they may run concurrently to it!
So, I would finally conclude that the ask pattern used like this will not work with Akka IO, because it's simply too low level for such an abstraction. If you still want it, you need to create you own layer of abstraction on top of Akka IO, e.g. some simple intermediate actor that covers TCP connector actor and implements the request-response behaviour.
As an additional reference to #ghik's answer, here's roughly how I created an intermediate actor to enable the ask pattern for IO on the rest of my actors.
class IOAskHandlerActor(address: InetSocketAddress) extends Actor {
override def receive = {
// Connection setup code omitted
case Connected(remote, local) =>
// other code omitted
context become latch(sender())
}
def latch(connection: ActorRef): Receive = {
case outgoing =>
context become receiving(connection, sender())
connection ! MySerializer.write(outgoing)
}
def receiving(connection: ActorRef, asker: ActorRef): Receive = {
case Received(incoming) =>
context become latch(connection)
asker ! MySerializer.read(incoming)
}
}
Instances of this class can be asked for responses. Note that I have only tested this with one simultaneous asker (which is my use case) and this probably doesn't work for multiple askers.
I'm using the Play Framework with scala. I'm new to scala, akka, and play.
This is my actor system. I'm not sure I'm doing this right, but I have 2 routers. 1 for Actor A, and 1 for Actor B:
val system = ActorSystem("ActionSystem")
val actorARouter = system.actorOf(Props[ActionParser].withRouter(
SmallestMailboxRouter(Runtime.getRuntime.availableProcessors())), name = "actorARouter")
val actorBRouter = system.actorOf(Props[ActionDispatcher].withRouter(
SmallestMailboxRouter(Runtime.getRuntime.availableProcessors())), name = "actorBRouter")
This is the current setup that I have:
The play framework provides a Controller for me that receives a http rest call with some json. Whenever the Controller receives a rest call, I do an ask sends the json to a router for Actor A. Here is what that looks like:
(actorARouter ? request.body.asJson.get).map {
case m: controllers.HttpMessages.OK => Ok(m.body)
case m: controllers.HttpMessages.HttpResponse => Status(m.status)(m.body)
}
Actor A then parses the json into a Seq of objects and then sends them via an ask to Actor B. Actor B is supposed to eventually process those by sending them to other actors, but for now is just returning generic responses.
The generic responses are being received back by ActorA via the future, then being parsed to JSON and then returned to the Controller via an OK response... or at least that's what is supposed to happen.
What's happening:
So what's happening is the controller sends to ActorA, ActorA sends to ActorB. ActorB sends generic responses to ActorA. ActorA parses generic responses into JSON and tries to do sender ! OK(json) but I get a message in the console saying it wasn't delivered as it's a "dead letter". when I debug into it when I look at sender, sender is a reference to the actor akka://ActionSystem/deadLetters
My questions:
Obviously I'm doing something wrong. Maybe I shouldn't be chaining these actors responses together like this. Again, I mentioned I only had plans to further this by having ActorB send out requests to other actors.
When I do an ask in an actor, that doesn't hog that thread and stop it from processing other messages while it's waiting for a response does it?
EDIT:
I found out I can save a reference to the sender for later use, and then send to that, and that seems to fix the dead letter problem. But I'm still very uncertain if this is the right way to be doing things. It feels like every time I'm adding another layer of actors 10's of milliseconds are being added onto my response time. Perhaps that's due to other factors though.
Without looking at your code, I cannot really comment on what caused the dead letter, from your edit I guess you closed over sender() instead of assigning it to a variable and closing over that.
To respond to your questions:
It is much easier to construct message flows with actors if you only use fire-and-forget messages. The ask Pattern is useful in some cases, but most of the time you should try to avoid it. What you can do instead is to pass the original sender along through your actors by using forward instead of tell. This way a response can be generated by the last actor in your message flow. The first actor only needs the code to handle the response, and does not need to care about generating the response. Nice separation of concerns right there. If you need to aggregate several responses in order to send out a single response afterwards, you can also use a temporary actor that all other actors will send their response to, and that knows the orignal sender. Temporary actors need to be stopped after doing their work.
As far as I know the ask pattern is asynchronous and uses temporary actors internally. However, if you wait for the result of the future in your actor, that will block that actor, and it will not be able to process further messages. A nice method to use the ask pattern is in combination with the pipeTo Pattern, which you can use to send the result of ask to an actor (usally self)
In an effort to learn both scala and akka I'm writing a Battleship game. I've not started actually writing any code yet, I'm merely thinking about how things would work.
I have agents for ships and player fleets, and messages such as "shot fired", "hit", "miss", "all ships killed". My first stumbling block is that when player 1 shoots it creates a burst of events, and player 2 must wait until all has settled before he can play his turn. How can I make sure of that ? I thought maybe I'd always send a reply no matter what, and then count that a sender receive exactly as many answers as messages sent. Maybe Battleship isn't the best candidate application for agents.
This also brings the question of making the difference between receiving no answer because the message was not processed yet, the agent didn't reply anything, or the agent died. But I'll save that one for later.
There are a number of things you might want to do here:
Have the first actor receive replies to each of its messages and then send a your turn message to the second actor
Send a message to the second actor indicating how many events in a given turn must be received
These are achievable as follows:
import akka.pattern._
(d1 ? m1) zip (d2 ? m2) pipeTo that
in the above example, d1/2 are the destination actors, m1/2 are the messages to be sent. The replies from these actors are zipped together (into a Tuple2) and forwarded on to the second actor (that in the example)
The second mechanism is a bit more involved; I've written similar things using SyncPoints. So, something like this:
case class SyncPoint(id: UUID, participants: ActorRef*)
object SyncPoint {
def newFor(participants: ActorRef*) = SyncPoint(UUID.createRandomUUID, participants)
}
Then the creator of a message sends out a SyncPoint to the ultimate observer first
val sync = SyncPoint.newFor(d1, d2)
that ! sync
Now the ultimate receiver knows it is expecting a message on this SyncPoint for each participant.
d1 ! SyncPart(m1, sync)
d2 ! SyncPart(m2, sync)
Where
case class SyncPart(msg: Any, sync: SyncPoint)
The actors d1 and d2 will forward on to that when they have processed their part in the message.
case class SyncPartial(sync: SyncPoint, participant: ActorRef)
In this way, that knows it is expecting messages from a number of participants and can then track when these participants have performed their processing.
In Scala, an actor can be notified when another (remote) actor terminates by setting the trapExit flag and invoking the link() method with the second actor as parameter. In this case when the remote actor ends its job by calling exit() the first one is notified by receiving an Exit message.
But what happens when the remote actor terminates in a less graceful way (e.g. the VM where it is running crashes)? In other words, how the local actor can discover that the remote one is no longer available? Of course I would prefer (if possible) that the local actor could be notified by a message similar to the Exit one, but it seems not feasible. Am I missing something? Should I continuously polling the state of the remote actor (and in this case I don't know which is the best way to do that) or is there a smarter solution?
But what happens when the remote actor terminates in a less graceful way (e.g. the VM where it is running crashes)
Actor proxy stays alive accepting messages (and loosing them), and waiting for you to restart the JVM with remote actor. Watching for JVM crashes (and other failures happening on the infrastructure level) is far beyond Scala responsibilities. Good choice for that could be monitoring through JMX.
In other words, how the local actor can discover that the remote one is no longer available?
You may define a timeout interval (say 5000 millis). If remote actor doesn't reply during this interval, it's a sign for you that something unexpected is happening to remote actor, and you may either ask it about its state or just treat it as dead.
Should I continuously polling the state of the remote actor (and in this case I don't know which is the best way to do that) or is there a smarter solution?
You may put a kind of a polling load balancer/dispatcher in front of a group of actors, that will use only those actors that are alive and ready to process messages (which makes sense in case of remote actors that may suddenly appear/disappear behind the proxy) -> Can Scala actors process multiple messages simultaneously?
The book Actors in Scala mentions (not tested personally):
Trapping termination notifications.
In some cases, it is useful to receive termination notifications as messages in the mailbox of a monitoring actor.
For example, a monitoring actor may want to rethrow an exception that is not handled by some linked actor.
Or, a monitoring actor may want to react to normal termination, which is not possible by default.
Actors can be configured to receive all termination notifications as normal messages in their mailbox using the Boolean trapExit flag. In the following example actor b links itself to actor a:
val a = actor { ... }
val b = actor {
self.trapExit = true
link(a)
...
}
Note that before actor b invokes link it sets its trapExit member to true;
this means that whenever a linked actor terminates (normally or abnormally) it receives a message of type Exit.
Therefore, actor b is going to be notified whenever actor a terminates (assuming that actor a did not terminate before b’s invocation of link).
So "what happens when the remote actor terminates in a less graceful way"?
It should receive an Exit message even in the case of an abnormal termination.
val b = actor {
self.trapExit = true
link(a)
a ! 'start
react {
case Exit(from, reason) if from == a =>
println("Actor 'a' terminated because of " + reason)
}
}