Listening to a remote akka ActorSystem's log stream - scala

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!

Related

Akka how to reach remote actors in cluster

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.

How do I control the run-time execution of an actor system in Akka?

I have been trying to figure out a way to control the run-time message passing within an actor system with some sort of external (separate from the actor system) controller. In other words, given an actor system (that I do not want to change): how do I set up a sort of controller that controls the message passing within it?
For example, imagine that the given actor system have the following setup:
object Program extends App {
val system = ActorSystem("system")
val B = system.actorOf(Props[B], "B")
val A = system.actorOf(Props(new A(B)), "A")
A ! "Start"
system.terminate()
}
class A(B: ActorRef) extends Actor {
def receive = {case "Start" => B ! "Message"}
}
class B extends Actor {
def receive = {case "Message" => println("Some logic")}
}
I want to accomplish the following:
Run this program synchronously, on a single thread
For each message being passed within the system: examine the content, sender and recipient, and based on that; execute some logic.
In the above example, I would want a "controller" that would do something like:
Actor A received message "Start" from the outside and sends "Message" to Actor B
Perform some blocking logic on the controller, i.e the actor system will idly wait for this logic to be performed.
Now that the logic has been executed, the controller sends a green light for the actor system to resume the message passing.
Actor B receives "Message" and prints "Some Logic"
Controller checks whether the actor system is terminated, which it is, and performs some additional logic.
In short, I want the external controller to be able to control the message passing within the actor system at run-time.
I was thinking that this controller could possibly be implemented using a dispatcher, router actor logic and futures. I didn't find any examples in the Akka documentation regarding this, so is this even possible to accomplish?

How to avoid saving state in Scala class?

I am currently writing a client application in Scala which makes HTTP requests to an API. In this client application I have implemented a service connector which encapsulates all API related logic. Before making API calls, I want to authenticate the user, but I want to abstract this process. Which means that my actor would only call the service connector to initiate the API call, something like that:
class MessageProcessor(credentials: Credentials) extends Actor with ActorLogging {
implicit val ec = context.dispatcher
override def receive: Receive = {
case sendMsg: SendMessage =>
log.info(s"Sending message ${sendMsg.body}.")
sendMessage(sendMsg)
}
def sendMessage(msg: SendMessage) = {
ServiceConnector.sendMessage(credentials, msg).map { result =>
// My result
}
}
}
object MessageProcessor {
def props(credentials: Credentials) = Props(classOf[MessageProcessor], credentials)
}
In my service connector, I want to somehow save "the Scala way" the JWT token and if I am not yet authenticated, send an authentication request before making the actual API call.
How can I code such a service in an immutable manner with Futures in mind?
I thought about creating additional actors and just sending messages around with the token, but is this really necessary?
You need multiple Akka states to do it the proper "Scala way". I'm not completely sure how your API works, but the following example shows a basic approach. In its first state, it authenticates before sending the message. Once the authentication is confirmed, it sends the message. All following messages are immediately sent. If the authentication is lost somehow, you can also add a logout or timeout case that switches back to the first receive state.
class MessageProcessor(credentials: Credentials) extends Actor with ActorLogging {
implicit val ec = context.dispatcher
override def receive: Receive = {
case sendMsg: SendMessage =>
log.info(s"Authenticating...")
sender ! NotAuthenticated(credentials) // or authenticate somehow
context become waitingAuthentication(sendMsg)
}
def waitingAuthentication(sendMsg: SendMessage): Receive = {
case _: AuthenticationConfirmation =>
log.info(s"Sending message ${sendMsg.body}.")
sendMessage(sendMsg)
context become authenticated
}
def authenticated: Receive = {
case sendMsg: SendMessage =>
log.info(s"Sending message ${sendMsg.body}.")
sendMessage(sendMsg)
}
}
It's just an example and doesn't consider all cases (e.g., a SendMessage during waitingAuthentication, therefore you would need a queue of SendMessages). If multiple actors need to know the authentication state or the credentials, you need to broadcast to them if you don't want a bottleneck actor that handles and verifies all messages. In that case, all of them would also need multiple authentication states as described above.
Actors Are Designed For State
This seems like a false exercise. The entire point of Actors is that they can warehouse state. From the documentation specifically on state:
The good news is that Akka actors conceptually each have their own
light-weight thread, which is completely shielded from the rest of the
system. This means that instead of having to synchronize access using
locks you can just write your actor code without worrying about
concurrency at all.
Therefore, "the scala way" is just to keep it in a variable within Actors.
One of the reasons to use Actors instead of Futures is so you can maintain state. Why choose Actors and then dismiss one of their primary advantages???
Your question is the equivalent of "how do I use HashMap without doing any hashing (in a scala way)?"

How to initialise a scala/akka Actor in an event driven architecture?

I have got long running processes, let's say 2h to 1day. Each process starts its lifecycle with an update message and then continues to listen further concurrent updates. In the update message there is a unique target identifier.
If I want to represent each process with an Actor, how do I initialize the actor? I clearly need to do an atomic look-up/create operation based on the value of the identifier in the update message? How to do I design this with scala/akka?
Setup a single actor that performs the management of these process actors (say a ProcessManager actor). This actor will support requests to get a process actor for a particular process id. Internally, it will see if that child already exists or not. If it exists, it will respond to the sender with that ref. If not, it will create it and then respond to the sender with that ref. Because this manager actor processes it's mailbox serially (as all actors do), you don't have to worry about race conditions with the lookup/create. A very simplified example of this:
case class GetProcessHandler(processId:Int)
class ProcessManager extends Actor{
def receive = {
case GetProcessHandler(id) =>
val name = s"proc_$id"
val handler = context.child(name).getOrElse(
context.actorOf(Props[ProcessHandler], name)
)
sender ! handler
}
}
class ProcessHandler extends Actor{
def receive = {
...
}
}
You can specify your starting actors in your application.conf. And then your main program you can create/initialize these actors by using your ActorSystem.

Akka and java.nio. Non-blocking selector

I'm trying to write server which listen for tcp connection.
I'm using nonblockig java.nio.
My selector.select() starts when actor receive "STARTLISTEN" message and it's ok, but when i sent messages to actor it not receive messages until selector not received any data.
How to avoid this? For example i want to send message "STOP" to my actor to stop selecting?
Is only way to call select with timeout or selectNow()?
I'm not going to use akka.io.
It's not full code but it illustrates the problem:
class MyActor(val host: String, port: Int) extends Actor {
val serverChannel = ServerSocketChannel.open()
val channelSelector = Selector.open()
serverChannel.configureBlocking(false);
serverChannel.bind(new InetSocketAddress( InetAddress.getByName(host), port ))
serverChannel.register(channelSelector, SelectionKey.OP_ACCEPT);
override def receive = {
case "STARTLISTEN" => {
val keys = channelSelector.select()
// processing read write etc....
}
case "STOP" => {
// I want to stop selection here....
}
}
}
Even though you dismiss it out of hand, I’d like to point out (on the record) that using the proven implementation of exactly this which ships with Akka already is probably the better way to go. (I’m linking to the 2.3.0-RC2 documentation because some of the experimental features which were introduced in the 2.2.x series will not be part of Akka going forward—survival of the fittest.)
You might be able to accomplish this by splitting your actor into multiple actors. The first actor receives messages from outside, and the second blocks on select(). The first actor can use wakeup() to stop the second from blocking before sending it any messages.