I have a 2 actors, Actor1 and Actor2. Actor1 wants to send MyMsg1 to Actor2, and Actor2 after doing some work and getting Future[MyMsg2] wants to send MyMsg2 to Actor1. I have got this working one way but it fails with DI.
Scenario 1 - Working scenario
Actor1 -->MyMsg1-->Actor2
Actor2 MyMsgHandler - Processes message(with Future), does pipeTo to sender with MyMsg2. Works fine, Actor1 recvs MyMsg2
Scenario2 - Failing scenario
Actor1 has a bean injected via MacWire - myBean.
myBean has Actor2 injected as a bean and sends MyMsg1 to Actor2
Actor2 MyMsgHandler processes message(with Future), does pipeTo to sender and tries sending MyMsg2 - Goes to deadLetter. The actor Ref for sender is never set.
How do I fix this?
Pasting code also
class Actor1(failedService: FailedService, actor2: ActorRef ## Actor2) extends Actor{
override def receive: Receive = {
case TriggerActor1() =>
println("Actor1 triggered from REST controller. Send msg to actor 2")
failedService.testSend()
//actor2 ! Msg1()
case Msg2() => println("got msg2 from actor 1")
}
class Actor2 extends Actor {
override def receive: Receive = {
case Msg1() => {
println("send without future")
val origsender = sender()
origsender ! Msg2()
}
}
class FailedService(actor2: ActorRef##Actor2) {
def testSend() = {
actor2 ! Msg1()
}
}
With my current code as shared above, Actor1 is able to send Msg1 to Actor2
and actor2 responds with Msg2 but Msg2 goes to deadletter. I get the below error
akka.actor.DeadLetterActorRef - Message [backup.failedakka.Msg2] from Actor[akka://application/user/actor2#-662746578] to Actor[akka://application/deadLetters] was not delivered. [1] dead letters encountered.
However, if insted of using the line failedService.testSend() in my Actor1, I uncomment the line below it
and use that to communicate, things work fine.
Is the Q clear now? I am injecting dependencies with MacWire
! is defined as:
def !(message: Any)(implicit sender: ActorRef = Actor.noSender): Unit
You can see the problem now, there's no implicit sender in scope of FailedService, although it is in scope "inside" an actor implementation.
I think you want to do:
class FailedService(actor2: ActorRef##Actor2) {
def testSend(implicit sender: ActorRef) = {
actor2 ! Msg1()
}
}
Related
I am trying to create a web socket server using Play Framework where response from server should be synchronous or asynchronous based on request.
The request will be processed in Parent actor .Based on the action in the request, child actor will be created and message will be passed to child actor for processing and response will be sent back to the controller.
There are predefined actions and sample request for some actions are as follows,
[,,]
["1234","Boot","{"system":"ABCD"}"]
["5678","Start","{"system":"EFGH", "currenTime":"1559548762638"}"]
#Singleton
class RequestController #Inject()(cc: ControllerComponents)(implicit system: ActorSystem, mat: Materializer) extends AbstractController(cc) {
def ws = WebSocket.accept[String, String] {req =>
ActorFlow.actorRef { out =>
ParentActor.props(out)
}
}
}
object ParentActor {
def props(out: ActorRef) = Props(new ParentActor(out))
}
class ParentActor(out : ActorRef) extends Actor {
override def receive: Receive = {
case msg: String =>
//String split opeartion to find the action.
//create child actor for the action and pass the message to the child actor
val action = msg.split(",")[2]
if("Boot".equals(action)){
val bootActor: ActorRef = actorSystem.actorOf(Props[BootActor])
childActor ! msg
}else if("Start".equals(action)){
val startActor: ActorRef = actorSystem.actorOf(Props[StartActor])
startActor ! msg
}
case msg: Response => out ! msg
}
}
case class Response(name:String, msg:String)
class BootActor extends Actor{
override def receive: Receive = {
case msg : String =>
sender() ! Response("ABC",msg)
}
}
class StartActor extends Actor{
override def receive: Receive = {
case msg : String =>
sender() ! Response("Efgh",msg)
}
}
Right now i am getting the action from the request and create a child actor for the action and pass the message to the child actor for processing.
But i am not sure is there any better way or design pattern to process the request and create a child actor instead of String operation?
First of all, there appears to be a typo in your code:
if ("Boot".equals(action)) {
val bootActor: ActorRef = actorSystem.actorOf(Props[BootActor])
childActor ! msg
} else if ("Start".equals(action)) {
val startActor: ActorRef = actorSystem.actorOf(Props[StartActor])
startActor ! msg
}
The message in the first conditional clause should be sent to bootActor instead of childActor, which is undefined in your code snippet.
Another issue is that you're using actorSystem.actorOf to create the child actors. This method creates "top-level" actors, which should be kept to a minimum. Actors created with actorSystem.actorOf are under the supervision of the guardian actor. What this means in relation to your code is that when ParentActor is stopped (i.e., when a WebSocket is closed, Play stops the actor used in ActorFlow, as documented here), the multiple instances of BootActor and StartActor will not be stopped, leaving you with a bunch of idle top-level actors. The remedy is to use context.actorOf to create instances of BootActor and StartActor: doing so makes these instances children of ParentActor.
Also, you should use the == operator instead of the equals method.
Here are the aforementioned changes:
if ("Boot" == action) {
val bootActor: ActorRef = context.actorOf(Props[BootActor])
bootActor ! msg
} else if ("Start" == action) {
val startActor: ActorRef = context.actorOf(Props[StartActor])
startActor ! msg
}
The above could be slightly simplified to the following:
val childActor =
if (action == "Boot") context.actorOf(Props[BootActor])
else context.actorOf(Props[StartActor])
childActor ! msg
To further simplify your code, don't create child actors, which in this case aren't necessary. Move all the logic of interacting with the out actor into a single actor.
I have an actor with stash usage. Sometimes, when it crashes, it loses all stashed messages. I found that it depends on what supervision logic I use.
I wrote a simple example.
An actor with the stash:
case object WrongMessage
case object TestMessage
case object InitialMessage
class TestActor extends Actor with Stash {
override def receive: Receive = uninitializedReceive
def uninitializedReceive: Receive = {
case TestMessage =>
println(s"stash test message")
stash()
case WrongMessage =>
println(s"wrong message")
throw new Throwable("wrong message")
case InitialMessage =>
println(s"initial message")
context.become(initializedReceive)
unstashAll()
}
def initializedReceive: Receive = {
case TestMessage =>
println(s"test message")
}
}
In the following code, TestActor never receives stashed TestMessage:
object Test1 extends App {
implicit val system: ActorSystem = ActorSystem()
val actorRef = system.actorOf(BackoffSupervisor.props(Backoff.onFailure(
Props[TestActor], "TestActor", 1 seconds, 1 seconds, 0
).withSupervisorStrategy(OneForOneStrategy()({
case _ => SupervisorStrategy.Restart
}))))
actorRef ! TestMessage
Thread.sleep(5000L)
actorRef ! WrongMessage
Thread.sleep(5000L)
actorRef ! InitialMessage
}
But this code works well:
class SupervisionActor extends Actor {
val testActorRef: ActorRef = context.actorOf(Props[TestActor])
override def supervisorStrategy: SupervisorStrategy = OneForOneStrategy()({
case _ => SupervisorStrategy.Restart
})
override def receive: Receive = {
case message => testActorRef forward message
}
}
object Test2 extends App {
implicit val system: ActorSystem = ActorSystem()
val actorRef = system.actorOf(Props(classOf[SupervisionActor]))
actorRef ! TestMessage
Thread.sleep(5000L)
actorRef ! WrongMessage
Thread.sleep(5000L)
actorRef ! InitialMessage
}
I looked into sources and found that actor supervision uses
LocalActorRef.restart method which backed by system dispatcher logic, but BackoffSupervisor simply creates a new actor after termination of the old one. Is there any way to work around it?
I'm not sure one can make restart under BackoffSupervisor properly send stashed messages without some custom re-implementation effort.
As you've already pointed out that BackoffSupervisor does its own restart that bypasses the standard actor lifecycle. In fact, it's explicitly noted in the BackoffOnRestartSupervisor source code:
Whatever the final Directive is, we will translate all Restarts to our
own Restarts, which involves stopping the child.
In case you haven't read about this reported issue, it has a relevant discussion re: problem with Backoff.onFailure.
Backoff.onStop would also give the wanted BackoffSupervisor feature, but unfortunately it has its own use cases and won't be triggered by an exception.
I would like to know how to efficiently cleanup akka actors that are created on the fly.
To give a bit of background:
Actor Hierarchy created per event.
Supervisor -> child1 -> grandChild1
In my application the supervisor actor dynamically creates other actors(on a periodic event). I wanted to cleanup the actors after the processing steps for that event is complete.
So, I would like to kill all the child actors once the processing is complete.
I am propagating a message (successfulProcessing) after successful completion in the reverse of creation. (Grandchild1 -> child1 -> Supervisor ).
In the Supervisor, I will send a PoisonPill to the child actor.
This is the code for the Supervisor actor.
class Supervisor extends Actor {
def receive={
case onEvent: OnEvent =>
//Create child actor and send message
case successfulProcessing =>
sender() ! PoisonPill
}
override val supervisorStrategy = AllForOneStrategy() {
case e: Exception =>
Stop
}
}
Is this the correct approach to cleanup the dynamically created actors. If there is any disadvantage to this approach or is there a pattern to be followed?
As per Akka Document 2.4.14 ,
Better way to handle PoisonPill/Kill message is to broadcast them.
ActorRef ! Broadcast(PoisonPill)
Note: Do not broadcast messages when using BalancingPool
The pattern I've seen is to have an actor who manages other actors. In the following example from this tutorial, actor1 manages actor2, where actor2 does all the work. actor1 then cleans up.
case class StartCounting(n: Int, actor: ActorRef)
case class CountDown(n: Int)
class CountDownActor extends Actor {
def receive = {
case StartCounting(n, actor) =>
println(n)
actor ! CountDown(n-1)
case CountDown(n) =>
if(n > 0) {
println(n)
sender ! CountDown(n-1)
} else {
context.system.shutdown()
}
}
}
object Main extends App {
val system = ActorSystem("HelloSystem")
// default Actor constructor
val actor1 = system.actorOf(Props[CountDownActor], name = "manager")
val actor2 = system.actorOf(Props[CountDownActor], name = "worker")
actor1 ! StartCounting(10, actor2)
}
You can think of this like recursion: base and inductive cases. You can apply this at depth for all sibling actors being managed their parent.
What is the correct way to switch activity between actors.
for example
actorA - send 100 messages to the actorB.
ActorB will print it into console. After 100 messages actorB will send 100 messages to the actorA and actorA will print it. Switch actor behaviour each 100 msg.
Thansk
Here's how I would do it (this is a very quick solution, probably not the most elegant one, suggestions for improvement are welcome). The main part is the following actor which can either send or receive messages. It changes its behaviour after sending (respectively receiving) more than numberOfMessages messages.
import akka.actor._
import scala.concurrent.duration._
object SenderReceiver {
case class StartSending(other: ActorRef) // see explanation below
case object TheMessage // dummy placeholder for actual message
case object SendMessage // a reminder for the actor itself to send a message
def props(numberOfMessages: Int): Props = Props(new SenderReceiver(numberOfMessages))
}
class SenderReceiver(numberOfMessages: Int) extends Actor with ActorLogging {
import context._
import SenderReceiver._
// initial receive in order to determine the actor's initial role
def receive = {
case StartSending(other) =>
log.info("I'm sending first, yay!")
sendNext(0, other)
case TheMessage =>
log.info("It seems, I'm receiving messages first.")
become(receiveMessages(1))
}
// receive function for when actor is a receiver of messages
def receiveMessages(messagesReceived: Int) : Receive = {
case TheMessage =>
log.info("Message received")
val messagesReceivedNew = messagesReceived + 1
if (messagesReceivedNew < numberOfMessages - 1)
become(receiveMessages(messagesReceivedNew))
else
sendNext(0, sender)
}
// receive function for when actor is a sender of messages
def sendMessages(messagesSent: Int, other: ActorRef) : Receive = {
case SendMessage =>
other ! TheMessage
log.info("Message sent")
val messagesSentNew = messagesSent + 1
if (messagesSentNew < numberOfMessages - 1)
sendNext(messagesSentNew, other)
else
become(receiveMessages(0))
}
def sendNext(messagesSent: Int, other: ActorRef) {
system.scheduler.scheduleOnce(500 milliseconds, self, SendMessage)
become(sendMessages(messagesSent, other))
}
}
Initially, the actor is in a state where it does not know yet whether it is a sender or a receiver. In this case, two things may happen:
Either the actor receives a message, in this case, it knows it is a receiver, and therefore becomes a receiver.
In the other case, the actor needs to be explicitely told that it is a sender (in particular, it needs to know where to send to!), before it can become a sender.
Once the actor knows what its role is, it does exactly what it is supposed to do. It sends (or receives) a certain number of messages, and afterwards switches to the other behavior.
In order to run this code, here are some more snippets you might need. First, here's some simple auxiliary actor that sets up the whole system and stops it after a while (it gets a bit boring after ten seconds or so...)
object Terminator {
case object StopThem
}
class Terminator extends Actor with ActorLogging {
import Terminator._
import SenderReceiver.StartSending
import context._
val actorA = system.actorOf(SenderReceiver.props(3), "actorA")
val actorB = system.actorOf(SenderReceiver.props(3), "actorB")
actorB ! StartSending(actorA)
system.scheduler.scheduleOnce(10 seconds, self, StopThem)
def receive = {
case StopThem =>
log.info("Stopping actors now")
system.shutdown
}
}
And, for the sake of completeness, a simple App to start everything
object SenderReceiverAkka extends App {
val system = ActorSystem("test")
val terminator = system.actorOf(Props[Terminator])
}
The context.parent reference does not do it, for me. In a Play project, I launch a new actor to deal with each incoming request:
val myActor: ActorRef = system.actorOf(Props(classOf[MyActor])
val future: Future[String] = (myActor ? Hello)(5.seconds).mapTo[String]
future.map(result => Ok(result)).recover {
case ex: AskTimeoutException => Ok("timeout expired")
case _ => Ok("error")
}
This triggers more actor messaging in the receive method of myActor: when anotherActor responds to myActor, the latter sends a message AllGood to its parent.
class MyActor extends Actor {
// other stuff, among which setup of anotherActor
def receive: Actor.Receive = {
case Hallo => anotherActor ! Hello
case HelloBack => context.parent ! AllGood
}
}
class AnotherActor extends Actor {
def receive: Actor.Receive = {
case Hallo => sender ! HelloBack
}
}
Everything works fine until myActor tries to send a message to context.parent. I get a deadLetters message in the console.
from Actor[xyz] to Actor[akka://my-system/user] was not delivered. [1] dead letters encountered.
I can make it work if myActor keeps a reference to the original sender, and the code looks like this instead:
class MyActor extends Actor {
var myDad: Option[ActorRef] = None // <--- NEW REFERENCE
// other stuff, among which setup of anotherActor
def receive: Actor.Receive = {
case Hallo => {
myDad = Some(sender)
anotherActor ! Hello
}
case HelloBack => myDad ! AllGood <-- MYDAD INSTEAD OF CONTEXT.PARENT
}
}
class AnotherActor extends Actor {
def receive: Actor.Receive = {
case Hallo => sender ! HelloBack
}
}
The reference myDad is actually something like Actor[akka://my-system/temp/$a], while I would expect it to be the same as the context.parent which was Actor[akka://my-system/user]. Isn't it the same actor?
Can anybody explain this behaviour to me, and suggest a cleaner way to fix this? Thanks.
Looks like you mixed up the concepts of parent and sender.
context.parent [akka://my-system/user] is normal because the actor has been created as a child of the Akka system using system.actorOf(Props(classOf[MyActor]).
the sender method gives you back the actor who sent the message. The important thing here is; this is not the actor system who sent the message to your actor. When using the ask pattern Akka creates a temporary actor (in your case Actor[akka://my-system/temp/$a]) that will send the message and wait for the answer. When the response is received, the Future is completed.
If you want it is possible to forward the original sender so that your AnotherActor can reply directly HelloBack to the temporary actor created by the ask pattern.
To do so just change the way the message is passed to anotherActor.
def receive: Actor.Receive = {
case Hallo => anotherActor.tell(Hello, sender)
}