Porting simple Scala remote actor example to Akka actors - scala

I try to port this simple actor example of sending "Ping" and "Pong" from Scala actors to Akka actors, but I keep getting errors and I wonder if it is just a simple error or some fundamental fault.
Consider this code:
import akka.actor.Actor._
import akka.actor.Actor
case class Message(text: String)
class PingPongActor(name: String) extends Actor {
def receive = {
case Message(msg) =>
println("received: " + msg)
Thread.sleep(1000)
self.reply(Message("Ping"))
case None => println("ping: timed out!")
}
}
object Ping extends App {
remote.start("localhost", 2552)
.register("ping-service", actorOf(new PingPongActor("pong")))
val actor = remote.actorFor("ping-service", "localhost", 2552)
actor ! (Message("Ping"))
}
object Pong extends App {
remote.start("localhost", 2553)
.register("pong-service", actorOf(new PingPongActor("ping")))
val actor = remote.actorFor("pong-service", "localhost", 2553)
actor ! (Message("Pong"))
}
I keep getting this error:
received: Ping
[GENERIC] [07.10.11 23:18] [RemoteServerStarted(akka.remote.netty.NettyRemoteSupport#3ff2cea2)]
[ERROR] [07.10.11 23:18] [akka:event-driven:dispatcher:global-2] [LocalActorRef]
No sender in scope, can't reply.
You have probably:
1. Sent a message to an Actor from an instance that is NOT an Actor.
2. Invoked a method on an TypedActor from an instance NOT an TypedActor.
You may want to have a look at safe_! for a variant returning a Boolean
akka.actor.IllegalActorStateException:
No sender in scope, can't reply.
You have probably:
1. Sent a message to an Actor from an instance that is NOT an Actor.
2. Invoked a method on an TypedActor from an instance NOT an TypedActor.
You may want to have a look at safe_! for a variant returning a Boolean
[laptop_e3263500-f129-11e0-a78d-001636ff8076]
at akka.actor.NullChannel$.$bang(Channel.scala:177)
at akka.actor.ActorRef$class.reply(ActorRef.scala:398)
at akka.actor.LocalActorRef.reply(ActorRef.scala:605)
at PingPongActor$$anonfun$receive$1.apply(RemoteActor.scala:21)
at PingPongActor$$anonfun$receive$1.apply(RemoteActor.scala:15)
at akka.actor.Actor$class.apply(Actor.scala:545)
at PingPongActor.apply(RemoteActor.scala:13)
The idea is that I start up two applications, Ping and Pong which try to send a message to each other every second and print it on the terminal (or print an error message if no message is received for two seconds).

The biggest fundamental problem with your code is that you're sending the message from outside of an actor and so the response has no where to go. You'll notice in the original example, the initial Message("ping") is sent from within the act() loop of the Ping actor. But really you have a couple of issues and it's better to start over, restructuring the code a bit. Here's an example that works, but it depends on starting the clients in a particular order. Of course, you can rewrite this to keep retrying the connection from PingActor, among other things.
sealed trait Message
case class Ping extends Message
case class Pong extends Message
class PingActor extends Actor {
override def preStart = {
val pong = remote.actorFor("pong-service", "localhost", 2553)
pong ! Ping
}
def receive = {
case Pong => {
println("Received pong")
Thread.sleep(1000)
self.reply(Ping)
}
}
}
class PongActor extends Actor {
def receive = {
case Ping => {
println("Received ping")
Thread.sleep(1000)
self.reply(Pong)
}
}
}
object pingApp extends App {
val actor = actorOf(new PingActor)
remote.start("localhost", 2552)
.register("ping-service", actor)
}
object pongApp extends App {
val actor = actorOf(new PongActor)
remote.start("localhost", 2553)
.register("pong-service", actor)
}

Related

Create child actors based on the request pattern

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.

Sending to Akka's Dead Letter Channel from many other actors in the fallback case

New to both Scala + Akka here. I have a small network of actors that all send different messages to each other. Many of them need access to my DeadLetterChannel actor in case they receive a message that they are not sure how to handle:
class DeadLetterChannel(val queue : mutable.Queue[Any]) extends Actor with Logging {
override def receive: Receive = {
case any : Any =>
log.info(s"${this.getClass} just received a message of type ${} from ${sender().getClass}.")
queue.enqueue(any)
case _ =>
log.warn(s"${this.getClass} just received a non-Any message.")
}
}
Then from inside many other actors:
class DeviceManager(val dlc : DeadLetterChannel) extends ActorRef {
override def receive: Receive = {
case Heartbeat =>
// Handle Heartbeat here...
case Connect:
// Handle Connect here...
case Disconnect:
// Handle Disconnect here...
case _ =>
dlc ! ???
}
}
I have two problems here:
I'm getting a compiler error on the sending of the message (specifically the ! overload: "Cannot resolve symbol !"); and
I have no idea what to send to dlc in the _ case, any ideas? Obviously I'm just trying to send the message that DeviceManager received that is neither a Heartbeat, a Connect or a Disconnect type
You may have over-simplified the code for your example, but you can't just send a message to an Actor class instance. You need to create a named actor using actorOf on an ActorSystem, and then use actorSelection on that system to get a handle on the actor.
For the second part of your question, just put a match value in the case and send the result:
case msg =>
dlc ! msg
Also, you might want to use a different name for your class because a dead letter is message that can't be delivered, not a message that can't be handled by the recipient.
Actor classes should extend Actor instead of ActorRef. Also, use ActorRef to reference an actor. DeviceManager, therefore, should be defined as the following:
class DeviceManager(dlc: ActorRef) extends Actor {
// ...
}
The idea here is that you would use the ActorRef of the DeadLetterChannel actor when creating the device actors. For example:
val dlc: ActorRef = context.actorOf(/* .. */)
val device1: ActorRef = context.actorOf(Props(classOf[DeviceManager], dlc)
val device2: ActorRef = context.actorOf(Props(classOf[DeviceManager], dlc)
Then you can do as Tim suggested in his answer and capture the default case in the receive block and send this message to dlc:
class DeviceManager(dlc: ActorRef) extends Actor {
def receive = {
/* other case clauses */
case msg =>
dlc ! msg
}
}
Also, instead of passing a mutable queue to DeadLetterChannel as a constructor argument, encapsulate this state inside the actor as an immutable queue and expose it only via message passing:
class DeadLetterChannel extends Actor with akka.actor.ActorLogging {
var queue = collection.immutable.Queue[Any](0)
def receive = {
case GetMessages => // GetMessages is a custom case object
sender() ! queue
case msg =>
val s = sender()
log.info(s"DLC just received the following message from [$s]: $msg")
queue = queue.enqueue(msg)
}
}
Another approach is to not define a default case (i.e., leave out the case msg => clause) and use Akka's mechanisms for dealing with unhandled messages. Below are a couple of options:
Enable the built-in logging for unhandled messages, which logs these messages at the DEBUG level:
akka {
actor {
debug {
# enable DEBUG logging of unhandled messages
unhandled = on
}
}
}
This approach is perhaps the simplest if you just want to log unhandled messages.
As the documentation states:
If the current actor behavior does not match a received message...by default publishes an akka.actor.UnhandledMessage(message, sender, recipient) on the actor system’s event stream.
In light of this, you could create an actor that subscribes to the event stream and handles akka.actor.UnhandledMessage messages. Such an actor could look like the following:
import akka.actor.{ Actor, UnhandledMessage, Props }
class UnhandledMessageListener extends Actor {
def receive = {
case UnhandledMessage(msg, sender, recipient) =>
// do something with the message, such as log it and/or something else
}
}
val listener = system.actorOf(Props[UnhandledMessageListener])
system.eventStream.subscribe(listener, classOf[UnhandledMessage])
More information about the event stream and creating a subscriber to that stream can be found here.

switch actor behavior with akka and scala

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])
}

Test response from child on akka

I'm new to using scala on the akka system.
I have a parent actor (named "manager") which sends a message to a child, and receives an answer, which it forwards to next child.
The final actor saves the result of the whole process.
I want to create an end to end test which sends a message to the parent ("manager"), and expect a message from the last actor which recieves the result.
I'm looking for a simple way to direct the final actor to send a message back to the test. Since the test is not the sender, and it's not a simple actor, i don't know how to direct the message correctly.
Following is code of the test:
class EndToEndTest extends TestKit(ActorSystem("MyActorSystem"))
with FunSuiteLike with Matchers with BeforeAndAfterAll with ImplicitSender {
override def afterAll {
TestKit.shutdownActorSystem(system)
}
test("should send result") {
val actor = system.actorOf(Props(new Manager(name = "ActorManager")))
actor ! tPoint(1000L)
actor ! tPoint(2000L)
expectMsg(ActorResult(1))
}
}
and of the last child actor:
class SensorHealthReportMySqlActor extends Actor with ActorLogging {
def receive = {
case Result(result: Long) =>
//this is where i would like to send the test a message with Result(result)
case _ =>
log.error("Unknown message type")
}
}
Any help would be appreciated.
I think the solution you want is to pass an ActorRef as a parameter in a message so the receiver knows where to send the reply when the message should be sent to some actor other than the sender of the current message.
So something along these lines:
import akka.actor.{Actor, Props, ActorRef, ActorLogging}
case class tPoint(n: Long)
case class Result(result: Long, replyTo: ActorRef)
case class ActorResult(result: Long)
class Manager(name: String) extends Actor {
val child = context.actorOf(Props(new SensorHealthReportMySqlActor))
override def receive: Receive = {
case msg: tPoint ⇒
child ! Result(msg.n, sender())
}
}
class SensorHealthReportMySqlActor extends Actor with ActorLogging {
def receive = {
case Result(result: Long, replyTo: ActorRef) =>
//this is where i would like to send the test a message with Result(result)
replyTo ! ActorResult(1)
case _ =>
log.error("Unknown message type")
}
}
In this case the sender() in the Manager receive method is the test itself, which is an ImplicitSender (as you declared in your test). This is an actor created behind the scenes which can receive messages.
In a non-test system, you can use the ask pattern or an Inbox for this same purpose. See the documentation at Actors.

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()
}