Late reply from async io in Akka - scala

I am using akka for some time. I started to see some patterns in my code to solve late reply for async io. Is this implementation ok? There is another way to do a late reply without block?
class ApplicationApi(asyncIo : ActorRef) extends Actor {
// store senders to late reply
val waiting = Map[request, ActorRef]()
def receive = {
// an actore request for a user, store it to late reply and ask for asyncIo actor to do the real job
case request : GetUser =>
waiting += (sender -> request)
asyncIo ! AsyncGet("http://app/user/" + request.userId)
// asyncio response, parse and reply
case response : AsyncResponse =>
val user = parseUser(response.body)
waiting.remove(response.request) match {
case Some(actor) => actor ! GetUserResponse(user)
}
}
}

One way to avoid blocking while waiting for a reply is to send using the ask method—a.k.a. ? operator—which returns a Future (unlike ! which returns ()).
Using the onSuccess or foreach methods, you can specify actions to be performed if/when the future is completed with a reply. To use this you need to mix in the AskSupport trait:
class ApplicationApi(asyncIo : ActorRef) extends Actor with AskSupport {
def receive = {
case request: GetUser =>
val replyTo = sender
asyncIo ? AsyncGet("http://app/user/" + request.userId) onSuccess {
case response: AsyncResponse =>
val user = parseUser(response.body)
replyTo ! GetUserResponse(user)
}
}
Avoid using this technique to perform any side effect that modifies the state of the ApplicationApi actor, because the effect will happen out-of-sync with the receive loop. Forwarding messages to other actors should be safe, though.
By the way, here is a trick to capture the current sender as part of the pattern match, avoiding the need to assign it to a variable later.
trait FromSupport { this: Actor =>
case object from {
def unapply(msg: Any) = Some(msg, sender)
}
}
class MyActor extends Actor with FromSupport {
def receive = {
case (request: GetUser) from sender =>
// sender is now a variable (shadowing the method) that is safe to use in a closure
}
}

Related

Akka-Http: How to return a response from an actor?

I am using an actor inside a request with the "ask" pattern:
val route =
pathPrefix("myapp") {
path("search") {
get {
(mainActorRef ? DoSomething("foo")).mapTo[ReturningType].map { result =>
complete(HttpEntity(ContentTypes.`application/json`, result ))
}
}
}
}
The problem is that the main actor communicates with other actors and gets an answer back from one of those actors like this:
class MainActor extends Actor {
override def receive: Receive = {
case d:DoSomething =>
anotherActor ! DoThis(d)
// received from anotherActor as reply to DoThis
case r:DoThisResponse =>
// how do I send the response back to my “route”?
pipe (Future{r}) to ???
}
}
How can I send this answer back to Akka-Http as response?
Using "sender()" in the main actor doesn't work as it won't be the right reference. Should I pass in the DoSomething some reference to use with "tell" (!) inside the main actor? How do I pass this reference?
Use forward instead of tell in the the MainActor when sending to anotherActor. That way anotherActor will not "see" MainActor as the sender .
So, basically, you send new messages in the intermediate steps with forward, but the actor in the line can simply respond to sender, since it does not see the intermediate actors.
Edit: a complete MainActor
class MainActor extends Actor {
override def receive: Receive = {
//delegating some more work to another container
case d:DoSomething =>
anotherActor forward DoThis(d)
// sending a response back to "route"
case r:DoThisResponse =>
sender ! Response
}
}

Execution context for futures in Actors

I have a Actor, and on some message I'm running some method which returns Future.
def receive: Receive = {
case SimpleMessge() =>
val futData:Future[Int] = ...
futData.map { data =>
...
}
}
Is it possible to pass actual context to wait for this data? Or Await is the best I can do if I need this data in SimpleMessage?
If you really need to wait for the future to complete before processing the next message, you can try something like this:
object SimpleMessageHandler{
case class SimpleMessage()
case class FinishSimpleMessage(i:Int)
}
class SimpleMessageHandler extends Actor with Stash{
import SimpleMessageHandler._
import context._
import akka.pattern.pipe
def receive = waitingForMessage
def waitingForMessage: Receive = {
case SimpleMessage() =>
val futData:Future[Int] = ...
futData.map(FinishSimpleMessage(_)) pipeTo self
context.become(waitingToFinish(sender))
}
def waitingToFinish(originalSender:ActorRef):Receive = {
case SimpleMessage() => stash()
case FinishSimpleMessage(i) =>
//Do whatever you need to do to finish here
...
unstashAll()
context.become(waitingForMessage)
case Status.Failure(ex) =>
//log error here
unstashAll()
context.become(waitingForMessage)
}
}
In this approach, we process a SimpleMessage and then switch handling logic to stash all subsequent SimpleMessages received until we get a result from the future. When we get a result, failure or not, we unstash all of the other SimpleMessages we have received while waiting for the future and go on our merry way.
This actor just toggles back and forth between two states and that allows you to only fully process one SimpleMessage at a time without needing to block on the Future.

How to send iterables between actors or from an actor to a Future?

A future from the main method of a program sends a msg to its actor asking for an iterable object. The actor then creates another future that asks for the iterable object (say an ArrayBuffer) from a remote actor. After receiving the ArrayBuffer from the remote actor, how would the actor send it back to the first future in the main method? It seems creating a local alias of sender and creating a separate case class to represent the iterable does not prevent dead letters from being encountered.
Here is a sample code:
case class SequenceObject(sqnce:Seq[someArrayBuffer])
//...
implicit val timeout = Timeout(10 seconds)
val fut1: Future[Any] = myActor ? iNeedAnArrayBufferObject
fut1.onSuccess {
case listOfItems: SequenceObject => {
//do sth with listofItems.sqnce
}
class myActor extends Actor {
implicit val timeout = Timeout(1 seconds)
def receive = {
case a: iNeedAnArrayBufferObject => {
val originalSender = sender
val fut: Future[Any] = (remoteActor ? a)
fut.onSuccess {
case list: SequenceObject => {
originalSender ! SequenceObject(list.sqnce)
}
}
The remote actor code is:
class ServerActorClass extends Actor {
def receive = {
case a: iNeedAnArrayBufferObject => {
val closer = sender()
closer ! SequenceObject(ArrayBufferObject[information])
}
}
The above does not seem to work. The remote actor and the local actor can communicate and messages are received correctly. However, the iterable object is never send back to fut1. Why is that? Thanks in advance.
Check pipeTo pattern in Ask: Send-And-Receive-Future section

Initializing an actor before being able to handle some other messages

I have an actor which creates another one:
class MyActor1 extends Actor {
val a2 = system actorOf Props(new MyActor(123))
}
The second actor must initialize (bootstrap) itself once it created and only after that it must be able to do other job.
class MyActor2(a: Int) extends Actor {
//initialized (bootstrapped) itself, potentially a long operation
//how?
val initValue = // get from a server
//handle incoming messages
def receive = {
case "job1" => // do some job but after it's initialized (bootstrapped) itself
}
}
So the very first thing MyActor2 must do is do some job of initializing itself. It might take some time because it's request to a server. Only after it finishes successfully, it must become able to handle incoming messages through receive. Before that - it must not do that.
Of course, a request to a server must be asynchronous (preferably, using Future, not async, await or other high level stuff like AsyncHttpClient). I know how to use Future, it's not a problem, though.
How do I ensure that?
p.s. My guess is that it must send a message to itself first.
You could use become method to change actor's behavior after initialization:
class MyActor2(a: Int) extends Actor {
server ! GetInitializationData
def initialize(d: InitializationData) = ???
//handle incoming messages
val initialized: Receive = {
case "job1" => // do some job but after it's initialized (bootstrapped) itself
}
def receive = {
case d # InitializationData =>
initialize(d)
context become initialized
}
}
Note that such actor will drop all messages before initialization. You'll have to preserve these messages manually, for instance using Stash:
class MyActor2(a: Int) extends Actor with Stash {
...
def receive = {
case d # InitializationData =>
initialize(d)
unstashAll()
context become initialized
case _ => stash()
}
}
If you don't want to use var for initialization you could create initialized behavior using InitializationData like this:
class MyActor2(a: Int) extends Actor {
server ! GetInitializationData
//handle incoming messages
def initialized(intValue: Int, strValue: String): Receive = {
case "job1" => // use `intValue` and `strValue` here
}
def receive = {
case InitializationData(intValue, strValue) =>
context become initialized(intValue, strValue)
}
}
I don't know wether the proposed solution is a good idea. It seems awkward to me to send a Initialization message. Actors have a lifecycle and offer some hooks. When you have a look at the API, you will discover the prestart hook.
Therefore i propose the following:
When the actor is created, its preStart hook is run, where you do your server request which returns a future.
While the future is not completed all incoming messages are stashed.
When the future completes it uses context.become to use your real/normal receive method.
After the become you unstash everything.
Here is a rough sketch of the code (bad solution, see real solution below):
class MyActor2(a: Int) extends Actor with Stash{
def preStart = {
val future = // do your necessary server request (should return a future)
future onSuccess {
context.become(normalReceive)
unstash()
}
}
def receive = initialReceive
def initialReceive = {
case _ => stash()
}
def normalReceive = {
// your normal Receive Logic
}
}
UPDATE: Improved solution according to Senias feedback
class MyActor2(a: Int) extends Actor with Stash{
def preStart = {
val future = // do your necessary server request (should return a future)
future onSuccess {
self ! InitializationDone
}
}
def receive = initialReceive
def initialReceive = {
case InitializationDone =>
context.become(normalReceive)
unstash()
case _ => stash()
}
def normalReceive = {
// your normal Receive Logic
}
case class InitializationDone
}

Akka Actor - wait for some time to expect a message, otherwise send a message out

Is it possible to make an Actor wait for X amount of seconds to receive any message, and if a message is received, process it as usual, otherwise send a message to some other Actor (pre-determined in the constructor)?
It's possible, have a look at Akka Actor "ask" and "Await" with TimeoutException. But keep in mind that blocking inside an actor is a very bad idea since during that time actor can't handle any other messages. Moreover it blocks one Akka processing thread.
A better approach is to send a message (fire and forget) and schedule some timeout event using Akka scheduler. When the response arrives, cancel that event or set some flag so that it won't trigger if the reply actually came on time.
Yes, if you want to wait for any message, you simply set a receiveTimeout: http://doc.akka.io/docs/akka/current/scala/actors.html#receive-timeout
(The docs is slightly misleading here, you can set the receiveTimeout after every message also)
Might be an overkill, but you might check out the Finite State Machine (FSM) trait.
import akka._
import actor._
import util._
import duration._
import Impatient._
object Impatient {
sealed trait State
case object WaitingForMessage extends State
case object MessageReceived extends State
case object TimeoutExpired extends State
sealed trait Data
case object Unitialized extends Data
// In
case object Message
}
class Impatient(receiver: ActorRef) extends Actor with FSM[State, Data] {
startWith(WaitingForMessage, Unitialized)
when(WaitingForMessage, stateTimeout = 3 seconds) {
case Event(StateTimeout, data) => goto(TimeoutExpired) using data // data is usually modified here
case Event(Message, data) => goto(MessageReceived) using data // data is usually modified here
}
onTransition {
case WaitingForMessage -> MessageReceived => stateData match {
case data => log.info("Received message: " + data)
}
case WaitingForMessage -> TimeoutExpired => receiver ! TimeoutExpired
}
when(MessageReceived) {
case _ => stay
}
when(TimeoutExpired) {
case _ => stay
}
initialize
}
Here it is in action:
object Main extends App {
import akka._
import actor._
import Impatient._
val system = ActorSystem("System")
val receiver = system.actorOf(Props(new Actor with ActorLogging {
def receive = {
case TimeoutExpired => log.warning("Timeout expired")
}
}))
val impatient = system.actorOf(Props(new Impatient(receiver)), name = "Impatient")
impatient ! Message
val impatient2 = system.actorOf(Props(new Impatient(receiver)), name = "Impatient2")
Thread.sleep(4000)
impatient2 ! Message
system.shutdown()
}