Akka how to reach remote actors in cluster - scala

First I would like to say that I am very new to Akka and actors :)
I would like to create a distributed application. I will split the application in a web part(REST API) and a user management part. But what is the right way to access an actor from another part of the application?
I know that I can access an actor selection by providing its address (https://doc.akka.io/docs/akka/2.5/remoting.html#looking-up-remote-actors), but isn't there a way where I don't have to work with addresses?
I just want to create a system where it is very easy to reach a remote actor without using their addresses.

In Cluster Sharding the actor identity is persistanceId and must be part of message which you send to whole cluster. Thats why you need to define extractEntityId.
Read more in documentation:
https://doc.akka.io/docs/akka/2.5/cluster-sharding.html

Maybe you can have a look for Distributed Publish Subscribe in Cluster
But the limit is: your local actorsystem which hosts your local actor & the remote actorsystem which hosts your remote actor should be configured as an akka cluster.
If cluster could be your design, then you can do as follows:
Remote Part:
class Subscriber extends Actor with ActorLogging {
import DistributedPubSubMediator.{ Subscribe, SubscribeAck }
val mediator = DistributedPubSub(context.system).mediator
// subscribe to the topic named "content"
mediator ! Subscribe("content", self)
def receive = {
case s: String ⇒
log.info("Got {}", s)
case SubscribeAck(Subscribe("content", None, `self`)) ⇒
log.info("subscribing")
}
}
Local Part:
class Publisher extends Actor {
import DistributedPubSubMediator.Publish
// activate the extension
val mediator = DistributedPubSub(context.system).mediator
def receive = {
case in: String ⇒
val out = in.toUpperCase
mediator ! Publish("content", out)
}
}
The remote part actor subscribe content topic, and if local part want to talk with remote, it can just publish message to content topic.
Hope it gives you some thoughts.

Related

Akka - Best Approach to configure an Actor that consumes a REST API service (blocking operation)

I have an Akka messaging engine that delivers millions of messages during the day, both SMS and Email. I need to introduce a new type of messaging (PushNotification) which consists in having each request consume a REST API (it will also process millions). I believe that consuming a Webservice is a blocking operation, so from what I have read I need to add a separate dispatcher for this new Actor, my questions is, does it necessarily need to be a thread-pool-executor with a fixed-pool-size as mentioned here? (See https://doc.akka.io/docs/akka-http/current/handling-blocking-operations-in-akka-http-routes.html) or is it possible to use a fork-join-executor instead? Also what is the best approach in order not to affect the current 2 types of Messaging ? (SMS and EMAIL) I mean how do I avoid to starve their thread-pool ? Currently EMAIL is using a separate Dispatcher and SMS is using the Default Dispatcher. Instead of creating a new Dispatcher for the Actor with blocking operation (calling WebService) is there any other way ? Like creating a reactive web service ?
Using a RESTful API from a web service does not have to be blocking.
An easy way to consume a RESTful API from an actor is to use Akka HTTP Client. This allows you to send an HTTP request and have the result sent back as a message to an actor using the pipeTo method.
This is a very cut-down example (lightly modified from the sample in the documentation).
import akka.http.scaladsl.Http
object RestWorker {
def props(replyTo: ActorRef): Props =
Props(new RestWorker(replyTo))
}
class RestWorker(replyTo: ActorRef) extends Actor
{
implicit val ec: ExecutionContext = context.system.dispatcher
override def preStart() = {
Http(context.system).singleRequest(HttpRequest(uri = "https://1.2.3.4/resource"))
.pipeTo(self)
}
def receive = {
case resp: HttpResponse =>
val response = ??? // Process response
replyTo ! response
self ! PoisonPill
}
}

Akka remoting - asking routee doesn't return answer

I am trying to ask something to an actor that has been created remotely in a SmallestMailboxPool but I never seem to get a response. My feeling is that this is because I send the message to the Router, but the Routee is the actor that is responding - not knowing the exact internals of Akka's ask, I can imagine that the replied must be the same as where the message was sent to. On the other hand, this seems so rudimentary that I cannot imagine it wouldn't work.
I am using Play framework 2.3.x and Akka remote 2.3.4
Actor Setup
I have 2 machines with 2 actors on these machines. I will try to keep the example as brief as possible.
Machine A/Actor A
The actual ask is being initiated on Machine A, I added some println statements to show actor addresses. The code is as follows.
class ActorA(remoteActor: ActorRef) extends Actor with ActorLogging {
...
def receive() = {
case a: String => {
println("Remote actor: " + remoteActor)
println("Self: " + self)
// This is where I get a timeout because I never get a reply
Await.result((remoteActor ? a), timeout.duration)
}
}
}
The output I am getting on Machine A is the following:
Remote actor: Actor[akka.tcp://application#192.168.0.101:2552/user/actorB_6f9bac20-2302-4408-9a1b-feece8c20bc3#-1920529606]
Self: Actor[akka://application/user/9df0190a-11fb-438c-9124-4869d015fc4d#-867250738]
Note that the ActorRef I am passing on to ActorA (called remoteActor), is the val actorRef that I create on Machine B (see below), having the MailboxPool.
Machine B/Actor B
On Machine B, I have an Actor B that is created in a SmallestMailboxPool and recieves the message from Actor A (via the Router) after which it replies.
The creation is as follows:
// Creation of the mailbox/Router
val actorRef = Akka.system.actorOf(
SmallestMailboxPool(1).props(
// sourceActor is the actorRef of Actor A, living on machine A
Props(ActorB, sourceActor)
),
name = java.util.UUID.randomUUID.toString
)
println("Mailbox is: " + actorRef)
The actual Actor B does the following:
class ActorB(sourceActor: ActorRef) extends Actor with ActorLogging {
println("I am: " + self)
def receive() = {
case a: String => {
println("I got data from " + sender)
println("Sending it back to: " + sourceActor)
sourceActor ! d
}
}
}
What I am getting from Actor B is the following printlines:
Mailbox is Actor[akka://application/user/actorB_6f9bac20-2302-4408-9a1b-feece8c20bc3#-1920529606]
I am Actor[akka://application/user/actorB_6f9bac20-2302-4408-9a1b-feece8c20bc3/$a#46262703]
I got data from Actor[akka.tcp://application#192.168.0.132:2552/temp/$e]
I am sending it back to: Actor[akka.tcp://application#192.168.0.132:2552/user/9df0190a-11fb-438c-9124-4869d015fc4d#-867250738]
So what I don't get is why the actor address from ActorA appears as temp/$e on ActorB's side. As can also be seen, ActorB gets the message from ActorA, which actually sends it to the Router. ActorB then rightfully tries to reply back to ActorA, but it always gets a timeout - is this because ActorB has a different ActorRef/address than the Router/Mailbox? I also tried sending back to sender instead of the parameter-given sourceActor, but to no avail.
I know I can ask the Mailbox for all its Routees and send an ask directly to them (haven't tried this yet), but that defies the whole purpose of a MailboxPool. EDIT: Tried this, didn't work either
EDIT:
The weirdest of this entire story, is that if I create ActorB on machine A (eg. not remotely), everything works just fine - I can ask the Router's ActorRef and I get a proper reply. As soon as ActorB is created on machine B, I never get a reply anymore. I also tried to see what happens if I just tell ActorB and continue in ActorA without caring for the result. What happens then is that I do get the message from ActorB that should be my response to the original ask... ActorB is hence perfectly capable of sending back to ActorA but for some reason, Akka won't use it as the reply to my original ask...
I managed to get it work. Turns out I was passing around an ActorRef to ActorB to which it was supposed to send, but at ask-time from ActorA, it seems like Akka makes some sort of temp actor that actually needs to be sent to from ActorB (ie. I can't just send to the ActorRef of ActorA that I passed on to ActorB and stored earlier). Using sender was a fix for me. (Although I am still puzzled why sender was actually not my ActorA).
My actual use case is far too complex to share on SO, but I made a small example (where this problem doesn't occur - everything works) for people interested: https://github.com/ErikTromp/AkkaRemoteAskTest. It uses Play's Iteratee library too, since that is part of my use case too.

Listening to a remote akka ActorSystem's log stream

I'm trying to subscribe to the log stream of a remote akka ActorSystem, basically to write a console that shows the running logs of the remote Actors.
The only way I can think to do this is to: create an Actor within the logging ActorSystem, have that Actor subscribe to the ActorSystem.eventStream and then subscribe to that Actor using actorSelection from within my console's ActorSystem.
But this seems very "indirect" since the log pipeline would look like:
logging Actor --> eventStream --> Actor subscribed to eventStream --> local Actor
Is there an easier way to subscribe to the event stream?
From a simplicity viewpoint, nothing forbids you to subscribe a remote actor to your event stream without an additional actor. The Akka documentation mentions:
The event stream is a local facility, meaning that it will not
distribute events to other nodes in a clustered environment (unless
you subscribe a Remote Actor to the stream explicitly). If you need to
broadcast events in an Akka cluster, without knowing your recipients
explicitly (i.e. obtaining their ActorRefs), you may want to look
into: Distributed Publish Subscribe in Cluster.
For illustration purposes, consider the following code fragment which corresponds to the remote system, the one you want to subscribe to:
class PublisherActor extends Actor with ActorLogging { // example publisher actor just to generate some logs
context.system.scheduler.schedule(1.second, 1.second, self, "echo")
def receive = {
case "echo" ⇒
val x = Random.nextInt(100)
log.info(s"I got a random number: $x")
}
}
def runPublisher() = {
println("=== running publisher node ===")
val system = ActorSystem("PublisherSystem")
val selection = system.actorSelection("akka.tcp://SubscriberSystem#127.0.0.1:2553/user/subscriber")
selection.resolveOne(10.seconds) onSuccess { // when the listener actor is available,
case listener ⇒ system.eventStream.subscribe(listener, classOf[LogEvent]) // subscribe it to the event stream
}
val publisher = system.actorOf(Props[PublisherActor], "publisher") // some example publisher
}
And then the corresponding subscriber in the "local" node, from where you want to show the logs:
class SubscriberActor extends Actor with ActorLogging {
log.info("subscriber listening...")
def receive = {
case msg ⇒ log.info(s"Got: $msg")
}
}
def runSubscriber() = {
println("=== running subscriber node ===")
val system = ActorSystem("SubscriberSystem")
val listener = system.actorOf(Props[SubscriberActor], "subscriber")
}
However, there are several caveats to this solution, as the fact that the publisher must be running before the subscriber (or the subscriber implement some retry policy until the publisher is up), the location is hardcoded and so on. If you want to have a more robust and resilient system and it's permissible, follow the advice in the documentation and use a distributed publisher-subscriber in a clustered environment which poses several advantages with a similar amount of boilerplate.
Hope it helped!

Play WebSocketActor createHandler with custom name

I am using (learning to) handle websockets in play application.
My controller is using WebSocket.acceptWithActor
def clientWS = WebSocket.acceptWithActor[JsValue, JsValue] { _ =>
upstream => ClientSesssionActor.props(upstream)
}
and all is well except some other "supervisor" actor needs to be able to use context.actorSelection(...) to communicate with all/some of those ClientSessionActors.
But all my ClientSessionActors are created with a path like this one :
[akka://application/system/websockets/ REQ_ID /handler]
Here is the line where WebsocketActorSupervisor creates them :
val webSocketActor = context.watch(context.actorOf(createHandler(self), "handler"))
That is where the "handler" part of the path comes from.
I would like to pass in a specific name for my ClientSessionActor instead of getting "handler".
Overloading the whole call stack with one more parameter seems inelegant: there is WebSocketActor.scala with Connect, WebSocketActorSupervisor(props and constructor), WebSocketsActor receive and then everything inside the WebSocket.scala.
I know I can pass the supervisor reference to the props, but what about the case when the "supervisor" has been restarted and needs to reconnect with his minions.
One more thing, I realize that I might be able to get all the "handler" actors, but there are more than 2 kinds of handlers. Yes I could have them ignore msgs directed at the other groups of handlers but this just feels so redundant sending out 3 times more msgs than I should have to.
Any suggestions ?
James ? :)
Thank you
How about each ClientSesssionActor sends a Register message to supervisor on preStart and store them in eg. val sessions = new HashMap[String, ActorRef].
And then unregister by sending Unregister in postStop
private class WebSocketsActor extends Actor {
import WebSocketsActor._
def receive = {
case c # Connect(requestId, enumerator, iteratee, createHandler) =>
implicit val mt = c.messageType
context.actorOf(WebSocketActorSupervisor.props(enumerator, iteratee, createHandler),
requestId.toString)
}
}
Here is code how play creates actors for handling websockets, it names with requestId.
I have also same question :) why not make it to name with custom names.

Creating an in-memory storage using Akka actors

I'm trying to familiarize myself with Akka using a pet project. This is basically a tournament-style server, having a pool of players. On each round, it should fetch two random players from the pool, play them against each other, then update a scoreboard. The whole thing sits behind an HTTP server. Players are added via an HTTP Post message, and games are triggered via a 'Play' message that's to be sent from a JS client.
The way I thought of implementing it is as follows:
a PlayerPool actor handling two messages, 'AddPlayer' and 'Play'
a Round actor that takes a message with two players, plays a round, then updates the
GameResults actor, containing a list of played rounds and the winner of each round. It also sports the 'GetScore' message which returns the scoreboard.
The HTTP layer is a Spray Routing actor, exposing calls for 'GetScore', 'AddPlayer' and 'Play', talking to the PlayerPool and GameResults actors.
My problem is that two separate actors are talking to GameResults - both Spray directly, and the Round actor, and apparently two separate instances of GameResults are being created, so that while Round adds a result to one instance, Spray is reading the scoreboard from another instance. Obviously I'm missing something very basic and before attempting to resolve the issue, I'd like to understand what's the canonic way of doing this in Akka? basically this problem can be reduced to an actor that holds a state across different calls.
Would be glad to have someone point me in the right direction.
code snippet example how to pass messages from spray to your game result actor :
hope this helps
object SomeApp extends App{
//create a actor system
val yourSystem = ActorSystem("Your-System")
//init spray actor
val restHandler = yourSystem.actorOf(Props[RestHandler], name = "Rest-spray")
//init play pool actor
val playerPoolActor = yourSystem.actorOf(Props[PlayerPool], name = "playerPool")
//INIT ONLY 1 game result actor
val gameResultsActor = yourSystem.actorOf(Props[GameResults], name = "gameResults")
//spray listen to all IP on port 90210
IO(Http)(yourSystem) ! Http.Bind(restHandler, interface = "0.0.0.0" , port = 90210)
class RestHandler extends Actor with RestHandlerRoute {
def actorRefFactory = context
//nice to hold the route in a diffrent trait
def receive = runRoute(someRoute)
}
}
//only thing the trait is doing is configuring the routing and send message to relavant actor
trait RestHandlerRoute extends HttpService{me: Actor =>
import SomeApp._
val someRoute =
path("getGameResults") { get { respondWithMediaType(`text/html`) { complete {
//case the user requested http://someIP:90210/getGameResults spray will send a message to the game result actor
gameResultsActor ! getResult
} ~
}