Why the actorSystem is not creating the actor to run with custom dispatcher - akka

Hi have below typesafe config in file application-typed.conf.
akka {
loggers = ["akka.event.slf4j.Slf4jLogger"]
loglevel = "DEBUG"
logging-filter = "akka.event.slf4j.Slf4jLoggingFilter"
actor {
provider = "local"
}
}
custom-thread-pool {
type = Dispatcher
executor = "thread-pool-executor"
thread-pool-executor {
fixed-pool-size = 40
}
throughput = 2
}
Below is the akka-typed actor code.
import akka.actor.typed.{ActorSystem, Behavior, DispatcherSelector, PostStop, Signal}
import akka.actor.typed.scaladsl.AbstractBehavior
import akka.actor.typed.scaladsl.ActorContext
import akka.actor.typed.scaladsl.Behaviors
import com.typesafe.config.ConfigFactory
import scala.concurrent.ExecutionContext
trait PrintMessage
case class PrintMessageAny(x: Any) extends PrintMessage
object PrintMeActor {
def apply(): Behavior[PrintMessage] =
Behaviors.setup[PrintMessage](context => new PrintMeActor(context))
}
class PrintMeActor(context: ActorContext[PrintMessage]) extends AbstractBehavior[PrintMessage](context) {
val dispatcherSelector: DispatcherSelector = DispatcherSelector.fromConfig("custom-thread-pool")
implicit val executionContext: ExecutionContext = context.system.dispatchers.lookup(dispatcherSelector)
println(s"PrintMeActor Application started in Thread ${Thread.currentThread().getName}")
override def onMessage(msg: PrintMessage): Behavior[PrintMessage] = {
// No need to handle any messages
println(s"Got $msg in Thread ${Thread.currentThread().getName}")
Behaviors.same
}
override def onSignal: PartialFunction[Signal, Behavior[PrintMessage]] = {
case PostStop =>
context.log.info("PrintMeActor Application stopped")
this
}
}
object TestTypedActorApp extends App {
val config = ConfigFactory.load("application-typed.conf")
val as: ActorSystem[PrintMessage] = ActorSystem(PrintMeActor(), "PrintAnyTypeMessage", config)
as.tell(PrintMessageAny("test"))
Thread.sleep(2000)
}
When I run the code, I get the below output.
PrintMeActor Application started in Thread PrintAnyTypeMessage-akka.actor.default-dispatcher-6
Got PrintMessageAny(test) in Thread PrintAnyTypeMessage-akka.actor.default-dispatcher-6
I want this actor to run on the custom-thread-pool but it is not happening. How can I achieve the same?

You associate the dispatcher with the actor when you spawn it, by passing an akka.actor.typed.DispatcherSelector (which extends akka.actor.typed.Props) corresponding to the desired dispatcher.
When spawning the ActorSystem on a custom dispatcher, one can only pass Props through the overloads that take either a Config or an ActorSystemSetup.
If wanting to override the actor for the user guardian actor (the actor with the behavior you passed into the ActorSystem), it may make more sense to make that dispatcher the default dispatcher:
akka.actor.default-dispatcher {
executor = "thread-pool-executor"
thread-pool-executor {
fixed-pool-size = 40
}
throughput = 2
}

Related

Akka Kafka stream supervison strategy not working

I am running an Akka Streams Kafka application and I want to incorporate the supervision strategy on the stream consumer such that if the broker goes down, and the stream consumer dies after a stop timeout, the supervisor can restart the consumer.
Here is my complete code:
UserEventStream:
import akka.actor.{Actor, PoisonPill, Props}
import akka.kafka.{ConsumerSettings, Subscriptions}
import akka.kafka.scaladsl.Consumer
import akka.stream.scaladsl.Sink
import akka.util.Timeout
import org.apache.kafka.clients.consumer.ConsumerConfig
import org.apache.kafka.common.serialization.{ByteArrayDeserializer, StringDeserializer}
import scala.concurrent.duration._
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.{Failure, Success}
import akka.pattern.ask
import akka.stream.ActorMaterializer
class UserEventStream extends Actor {
val settings = Settings(context.system).KafkaConsumers
implicit val timeout: Timeout = Timeout(10 seconds)
implicit val materializer = ActorMaterializer()
override def preStart(): Unit = {
super.preStart()
println("Starting UserEventStream....s")
}
override def receive = {
case "start" =>
val consumerConfig = settings.KafkaConsumerInfo
println(s"ConsumerConfig with $consumerConfig")
startStreamConsumer(consumerConfig("UserEventMessage" + ".c" + 1))
}
def startStreamConsumer(config: Map[String, String]) = {
println(s"startStreamConsumer with config $config")
val consumerSource = createConsumerSource(config)
val consumerSink = createConsumerSink()
val messageProcessor = context.actorOf(Props[MessageProcessor], "messageprocessor")
println("START: The UserEventStream processing")
val future =
consumerSource
.mapAsync(parallelism = 50) { message =>
val m = s"${message.record.value()}"
messageProcessor ? m
}
.runWith(consumerSink)
future.onComplete {
case Failure(ex) =>
println("FAILURE : The UserEventStream processing, stopping the actor.")
self ! PoisonPill
case Success(ex) =>
}
}
def createConsumerSource(config: Map[String, String]) = {
val kafkaMBAddress = config("bootstrap-servers")
val groupID = config("groupId")
val topicSubscription = config("subscription-topic").split(',').toList
println(s"Subscriptiontopics $topicSubscription")
val consumerSettings = ConsumerSettings(context.system, new ByteArrayDeserializer, new StringDeserializer)
.withBootstrapServers(kafkaMBAddress)
.withGroupId(groupID)
.withProperty(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, "earliest")
.withProperty(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, "true")
Consumer.committableSource(consumerSettings, Subscriptions.topics(topicSubscription: _*))
}
def createConsumerSink() = {
Sink.foreach(println)
}
}
StreamProcessorSupervisor (this is the supervisor class of the UserEventStream class):
import akka.actor.{Actor, Props}
import akka.pattern.{Backoff, BackoffSupervisor}
import akka.stream.ActorMaterializer
import stream.StreamProcessorSupervisor.StartClient
import scala.concurrent.duration._
object StreamProcessorSupervisor {
final case object StartSimulator
final case class StartClient(id: String)
def props(implicit materializer: ActorMaterializer) =
Props(classOf[StreamProcessorSupervisor], materializer)
}
class StreamProcessorSupervisor(implicit materializer: ActorMaterializer) extends Actor {
override def preStart(): Unit = {
self ! StartClient(self.path.name)
}
def receive: Receive = {
case StartClient(id) =>
println(s"startCLient with id $id")
val childProps = Props(classOf[UserEventStream])
val supervisor = BackoffSupervisor.props(
Backoff.onFailure(
childProps,
childName = "usereventstream",
minBackoff = 1.second,
maxBackoff = 1.minutes,
randomFactor = 0.2
)
)
context.actorOf(supervisor, name = s"$id-backoff-supervisor")
val userEventStrean = context.actorOf(Props(classOf[UserEventStream]),"usereventstream")
userEventStrean ! "start"
}
}
App (the main application class):
import akka.actor.{ActorSystem, Props}
import akka.stream.ActorMaterializer
object App extends App {
implicit val system = ActorSystem("stream-test")
implicit val materializer = ActorMaterializer()
system.actorOf(StreamProcessorSupervisor.props,"StreamProcessorSupervisor")
}
application.conf:
kafka {
consumer {
num-consumers = "1"
c1 {
bootstrap-servers = "localhost:9092"
bootstrap-servers = ${?KAFKA_CONSUMER_ENDPOINT1}
groupId = "localakkagroup1"
subscription-topic = "test"
subscription-topic = ${?SUBSCRIPTION_TOPIC1}
message-type = "UserEventMessage"
poll-interval = 50ms
poll-timeout = 50ms
stop-timeout = 30s
close-timeout = 20s
commit-timeout = 15s
wakeup-timeout = 10s
max-wakeups = 10
use-dispatcher = "akka.kafka.default-dispatcher"
kafka-clients {
enable.auto.commit = true
}
}
}
}
After running the application, I purposely killed the Kafka broker and then found that after 30 seconds, the actor is stopping itself by sending a poison pill. But strangely it doesn't restart as mentioned in the BackoffSupervisor strategy.
What could be the issue here?
There are two instances of UserEventStream in your code: one is the child actor that the BackoffSupervisor internally creates with the Props that you pass to it, and the other is the val userEventStrean that is a child of StreamProcessorSupervisor. You're sending the "start" message to the latter, when you should be sending that message to the former.
You don't need val userEventStrean, because the BackoffSupervisor creates the child actor. Messages sent to the BackoffSupervisor are forwarded to the child, so to send a "start" message to the child, send it to the BackoffSupervisor:
class StreamProcessorSupervisor(implicit materializer: ActorMaterializer) extends Actor {
override def preStart(): Unit = {
self ! StartClient(self.path.name)
}
def receive: Receive = {
case StartClient(id) =>
println(s"startCLient with id $id")
val childProps = Props[UserEventStream]
val supervisorProps = BackoffSupervisor.props(...)
val supervisor = context.actorOf(supervisorProps, name = s"$id-backoff-supervisor")
supervisor ! "start"
}
}
The other issue is that when an actor receives a PoisonPill, that's not the same thing as that actor throwing an exception. Therefore, Backoff.onFailure won't be triggered when UserEventStream sends itself a PoisonPill. A PoisonPill stops the actor, so use Backoff.onStop instead:
val supervisorProps = BackoffSupervisor.props(
Backoff.onStop( // <--- use onStop
childProps,
...
)
)
val supervisor = context.actorOf(supervisorProps, name = s"$id-backoff-supervisor")
supervisor ! "start"

I want to create a simple singleton cluster and send message from a remote node

Here i have created a singleton actor. The master and the seed node is the same. From a different project i am trying to add into the cluster and want to send message. I am able to join in the cluster but cannot be able to send message.
My master and seed node:
package Demo
import akka.actor._
import akka.cluster.singleton.{ClusterSingletonManager, ClusterSingletonManagerSettings, ClusterSingletonProxy, ClusterSingletonProxySettings}
import com.typesafe.config.ConfigFactory
import scala.concurrent.duration._
object MainObject1 extends App{
DemoMain1.start(8888)
}
object DemoMain1 {
val role = "test"
val singletonname = "Ruuner"
val mastername = "Master"
def start(port: Int): ActorSystem = {
val conf = ConfigFactory.parseString(
s"""
|akka.actor.provider = "akka.cluster.ClusterActorRefProvider"
|
|akka.remote.netty.tcp.port = $port
|akka.remote.netty.tcp.hostname = 127.0.0.1
|akka.cluster.roles = ["$role"]
|akka.cluster.seed-nodes = ["akka.tcp://DemoMain1#127.0.0.1:8888"]
""".stripMargin
)
val system = ActorSystem("DemoMain1", conf)
val settings = ClusterSingletonManagerSettings(system).withRole(role)
val manager = ClusterSingletonManager.props(Props[DemoMain1], PoisonPill, settings)
val actor=system.actorOf(manager, mastername)
system
}
class DemoMain1 extends Actor with Identification {
import context._
override def preStart(): Unit = {
println(s"Master is created with id $id in $system")
println(self.path.address.host)
system.scheduler.scheduleOnce(100.seconds, self, 'exit)
}
override def receive : Receive={
case 'exit => println("$id is exiting")
context stop self
//SupervisorStrategy.Restart
case msg => println(s"messasge from $system is $msg ")
sender() ! 'how
}
}
}
The other node which is trying to join the cluster and send message.
import akka.actor._
import akka.cluster.singleton.{ClusterSingletonProxy, ClusterSingletonProxySettings}
import com.typesafe.config.ConfigFactory
import scala.concurrent.duration._
object Ping extends App{
def ping: ActorSystem = {
val conf = ConfigFactory.parseString(
s"""
|akka.actor.provider = "akka.cluster.ClusterActorRefProvider"
|
|akka.remote.netty.tcp.port = 0
|akka.remote.netty.tcp.hostname = 127.0.0.1
|akka.cluster.roles = ["slave"]
|akka.cluster.seed-nodes = ["akka.tcp://DemoMain1#127.0.0.1:8888"]
|akka.cluster.auto-down-unreachable-after = 10s
""".stripMargin
)
val system = ActorSystem("DemoMain1", conf)
system.actorOf(Props[Ping])
system
}
class Ping extends Actor {
import context._
val path = "akka.tcp://DemoMain1#127.0.0.1:8888/DemoMain1/user/Master/actor"
val settings = ClusterSingletonProxySettings(system).withRole("slave")
val actor = context.actorOf(ClusterSingletonProxy.props(path, settings))
val pingInterval = 1.seconds
override def preStart(): Unit = {
system.scheduler.schedule(pingInterval, pingInterval) {
println(s"Locate Ping $system")
actor ! 'hi
}
}
override def receive: Receive = {
case msg => println(s"The message is $msg")
}
}
Ping.ping
}
If i give ip addresses of the system then also the message s not sent.
It appears the role in your ClusterSingletonProxySettings(system).withRole("slave") settings for your Ping actor doesn't match that of your ClusterSingletonManagerSettings(system).withRole(role) where role = "test".
ClusterSingletonProxy is supposed to be present on all nodes with the specified role on which the cluster is set up, hence its role settings should match ClusterSingletonManager's. Here's a sample configuration.

How to supervise cluster singleton in Akka?

I'm trying to supervise an Akka Actor, more specifically a Cluster Singleton created using ClusterSingletonManager. I'm trying to achieve more control over exceptions, logs and Actor's life cycle.
Unfortunately, after implementing a solution, I made a Singleton Actor throw exceptions, but nothing was show in the logs, nor the Actor or Cluster was shutdown.
My implementation is as follows:
object SingletonSupervisor {
case class CreateSingleton(p: Props, name: String)
}
class SingletonSupervisor extends Actor with ActorLogging {
override val supervisorStrategy =
OneForOneStrategy(maxNrOfRetries = 0, withinTimeRange = 1.minute) {
case x: ActorInitializationException =>
log.error(s"Actor=<${x.getActor}> trowed an exception=<${x.getCause}> with message=<${x.getMessage}>")
Stop
case x: ActorKilledException => Stop
case x: DeathPactException => Stop
case x: Exception =>
log.error(s"Some actor threw an exception=<${x.getCause}> with message=<${x.getMessage}>, trace=<${x.getStackTrace}>")
Escalate
}
def receive = {
case CreateSingleton(p: Props, name: String) =>
sender() ! context.actorOf(p)
context.actorOf(ClusterSingletonManager.props(
singletonProps = p,
terminationMessage = PoisonPill,
settings = ClusterSingletonManagerSettings(context.system)),
name = name)
}
}
So, is it even possible to supervisor a Cluster Singlegon? If possible, how should I attack this problem?
One possible solution is creating supervisor actor that spawns given child with given supervisorStrategy and forwards messages to its child.
Here is supervisor actor:
class SupervisorActor(childProps: Props, override val supervisorStrategy) extends Actor {
val child = context.actorOf(childProps, "supervised-child")
def receive: Receive = {
case msg => child forward msg
}
}
and here is how you create supervised actor as cluster singleton
context.actorOf(ClusterSingletonManager.props(
singletonProps = Props(classOf[SupervisorActor], p, supervisorStrategy),
terminationMessage = PoisonPill,
settings = ClusterSingletonManagerSettings(context.system)),
name = name)

Time for create TestProbe in Akka TestKit

I have trait for override actorOf in tests:
trait ActorRefFactory {
this: Actor =>
def actorOf(props: Props) = context.actorOf(props)
}
And I have worker actor, which stop self when receive any message:
class WorkerActor extends Actor {
override def receive: Actor.Receive = {
case _ => { context.stop(self) }
}
}
Also I have master actor, who creates actors and hold them in queue:
class MasterActor extends Actor with ActorRefFactory {
var workers = Set.empty[ActorRef]
override val supervisorStrategy = SupervisorStrategy.stoppingStrategy
def createWorker() = {
val worker = context watch actorOf(Props(classOf[WorkerActor]))
workers += worker
worker
}
override def receive: Receive = {
case m: String =>
createWorker()
case Terminated(ref) =>
workers -= ref
createWorker()
}
}
And this test, which is failed:
class ActorTest(val _system: ActorSystem) extends akka.testkit.TestKit(_system)
with ImplicitSender
with Matchers
with FlatSpecLike {
def this() = this(ActorSystem("test"))
def fixture = new {
val master = TestActorRef(new MasterActor() {
override def actorOf(props: Props) = TestProbe().ref
})
}
it should "NOT FAILED" in {
val f = fixture
f.master ! "create"
f.master ! "create"
f.master.underlyingActor.workers.size shouldBe 2
val worker = f.master.underlyingActor.workers.head
system.stop(worker)
Thread.sleep(100)
f.master.underlyingActor.workers.size shouldBe 2
}
}
After Thread.sleep in the test, I give error by "1 was not equal to 2". I have not idea what happening. But, if guess I can assume that TestProbe() can't create in the time. What can I do?
This basically boils down to an asynchronicity issue that you want to try and avoid in unit tests for Akka. You are correctly using a TestActorRef to get hooked into the CallingThreadDispatcher for the master actor. But when you call system.stop(worker), the system still us using the default async dispatcher which will introduce this race condition on the stopping and then re-creating of a worker. The simplest way I can see to fix this issue consistently is to stop the worker like so:
master.underlyingActor.context.stop(worker)
Because you are using the context of master and that actor is using the CallingThreadDispatcher I believe this will remove the asnyc issue that you are seeing. It worked for me when I tried it.

How can I have an Akka actor executed every 5 min?

I'd like to know if there are any mechanism in Akka that can have an actor executed periodically?
You don't really need an actor to do this in Akka 1.3.1 you can schedule a function to be called every 5 minutes like this:
Scheduler.schedule(() => println("Do something"), 0L, 5L, TimeUnit.MINUTES)
However, if you do want it to be an actor for other reasons you would call it like this
case class Message()
val actor = actorOf(new Actor {
def receive = {
case Message() => println("Do something in actor")
}
}).start()
Scheduler.schedule(actor, Message(), 0L, 5L, TimeUnit.MINUTES)
If you're using Akka 2.0 then it would be done like this
val system = ActorSystem("MySystem")
system.scheduler.schedule(0 seconds, 5 minutes)(println("do something"))
Or send a message to an actor every 5 minutes like this
case class Message()
class MyActor extends Actor {
def receive = { case Message() => println("Do something in actor") }
}
val system = ActorSystem("MySystem")
val actor = system.actorOf(Props(new MyActor), name = "actor")
system.scheduler.schedule(0 seconds, 5 minutes, actor, Message())
The approach using schedule is one good approach, although there is a potential for the messages to queue up if the work done on schedule is so great that it might take longer than the scheduled interval. If you want the interval to occur between the end of one iteration and the beginning of the next, then use scheduleOnce with the following pattern:
import akka.actor.Actor
import scala.concurrent.duration._
class SchedulingActor extends Actor {
override def preStart(): Unit = {
self ! "Do Some Work"
}
def receive = {
case "Do Some Work" =>
doWork
context.system.scheduler.scheduleOnce(5 minutes, self, "Do Some Work")
}
def doWork = ???
}
More complete Java example:
import akka.actor.AbstractActor;
import akka.actor.ActorRef;
import scala.concurrent.duration.FiniteDuration;
import java.util.concurrent.TimeUnit;
public class AnActor extends AbstractActor {
private final FiniteDuration SCHEDULED_WORK_DELAY = new FiniteDuration(5, TimeUnit.MINUTES);
#Override
public void preStart() {
getSelf().tell("Do Scheduled Work", ActorRef.noSender());
}
#Override
public Receive createReceive() {
return receiveBuilder()
.matchEquals("Do Scheduled Work", work -> {
doScheduledWork();
context().system().scheduler().scheduleOnce(SCHEDULED_WORK_DELAY, getSelf(),
"Do Scheduled Work", context().dispatcher(), ActorRef.noSender());
})
.build();
}
private void doScheduledWork() { ... }
}
If anyone want java code then they can do like this
Cancellable cancellable = system.scheduler().schedule(Duration.Zero(), Duration.create(5, TimeUnit.MINUTES), cronActor, "tick", system.dispatcher(), null);