I try to make akka cluster with distributed messages work, but I'm stuck. My actor is properly started and subscribed to topic but no messages are received. Here is the code
import akka.actor.{Actor, ActorSystem, Props}
import akka.cluster.client.ClusterClient.Publish
import akka.cluster.pubsub.DistributedPubSub
import akka.cluster.pubsub.DistributedPubSubMediator.{Subscribe, SubscribeAck}
case object DistributedMessage
object ClusterExample extends App {
val system = ActorSystem("ClusterSystem")
val actor = system.actorOf(Props(classOf[ClusterExample]), "clusterExample")
}
class ClusterExample extends Actor {
private val mediator = DistributedPubSub(context.system).mediator
mediator ! Subscribe("content", self)
override def receive = {
case SubscribeAck(Subscribe("content", None, `self`)) =>
(1 to 100) foreach (_ => {
mediator ! Publish("content", msg = DistributedMessage)
})
case DistributedMessage => println("received message from queue!")
}
}
And here is configuration:
akka {
log-dead-letters = 0
log-dead-letters-during-shutdown = on
actor {
provider = "akka.cluster.ClusterActorRefProvider"
enable-additional-serialization-bindings = on
}
remote {
log-remote-lifecycle-events = off
netty.tcp {
hostname = "127.0.0.1"
port = 2552
bind-hostname = "0.0.0.0"
bind-port = 2552
}
}
extensions = ["akka.cluster.pubsub.DistributedPubSub"]
cluster {
seed-nodes = [
"akka.tcp://ClusterSystem#127.0.0.1:2552"
]
}
}
"received message from queue" is actually never printed
Silly mistake. So, the problem was invalid import. This:
import akka.cluster.client.ClusterClient.Publish
should be replaced by:
import akka.cluster.pubsub.DistributedPubSubMediator.Publish
Related
I write a simple code, but the problem with my code is that, whenever I run the remote actor the remoting starts and listening at host "27.0.1.1" and at "2552" port which are the default one, although I mentioned the port "5150" in config file of remote actor, Moreover the local actor also bind at the default host and port same as remote actor, so it throughs an exception of "java.net.BindException: [/127.0.1.1:25520] Address already in use". Where is the issue in my code, can anyone help me to resolve the issue?
The code is given below.
Here is my RemoteActor code.
class RemoteActor extends Actor {
override def receive: Receive = {
case msg: String => {
println("remote received " + msg + " from " + sender)
sender ! "hi"
}
case _ => println("Received unknown msg ")
}
}
object RemoteActor extends App {
val configFile = getClass.getClassLoader.getResource("remote_application.conf").getFile
val config = ConfigFactory.parseFile(new File(configFile))
val system = ActorSystem("RemoteSystem" , config)
val remote = system.actorOf(Props[RemoteActor], name="remote")
println("remote is ready")
}
here is its config file(remote_application.conf).
akka {
loglevel = "INFO"
actor {
provider = "akka.remote.RemoteActorRefProvider"
}
remote {
enabled-transports = ["akka.remote.netty.tcp"]
netty.tcp {
hostname = "127.0.0.1"
port = 5150
}
log-sent-messages = on
log-received-messages = on
}
}
here is the LocalActor code who is going to send message to remote actor at 5150 port.
class LocalActor extends Actor {
#throws[Exception](classOf[Exception])
override def preStart(): Unit = {
val remoteActor = context.actorSelection("akka.tcp://RemoteSystem#127.0.0.1:5150/user/remote")
println("That 's remote:" + remoteActor)
remoteActor ! "hi"
}
override def receive: Receive = {
case msg: String =>
println("got message from remote" + msg)
}
}
object LocalActor extends App {
val configFile = getClass.getClassLoader.getResource("local_application.conf").getFile
val config = ConfigFactory.parseFile(new File(configFile))
val system = ActorSystem("ClientSystem",config)
val localActor = system.actorOf(Props[LocalActor], name="local")
}
here is the config file of local actor(local_application.conf).
akka {
loglevel = "INFO"
actor {
provider = "akka.remote.RemoteActorRefProvider"
}
remote {
enabled-transports = ["akka.remote.netty.tcp"]
netty.tcp {
hostname = "127.0.0.1"
port = 0
}
log-sent-messages = on
log-received-messages = on
}
}
I'm just messing around with some basics to learn akka remoting, and I'm running into an issue where my proxy class sends Identify messages to my backend, but never receives a response back.
I've verified that the backend receives messages sent to the ActorSelection, and I've seen a logging message where the backend actor says it will use xxx for serialization of the ActorIdentity messages. Not sure where I'm getting it wrong.
Here is my proxy class:
package remoting
import akka.actor.{Actor, ActorIdentity, ActorRef, Identify, ReceiveTimeout, Terminated}
import com.typesafe.config.ConfigFactory
import scala.concurrent.duration._
class BackendProxyActor extends Actor {
context.setReceiveTimeout(3.seconds)
val path = createPath
val backendSelection = context.actorSelection(path)
println(f"BackendSelection is $backendSelection")
override def preStart(): Unit = backendSelection ! Identify(1)
override def receive: Receive = {
case ActorIdentity(1, Some(actor)) =>
context.setReceiveTimeout(Duration.Undefined)
context.watch(actor)
context.become(active(actor))
case ActorIdentity(1, None) =>
println("Backend actor not available")
case ReceiveTimeout =>
backendSelection ! Identify(1)
case msg: Any => println(f"Received $msg while identifying backend")
}
def active(backend: ActorRef): Receive = {
case msg: Any => backend ! msg
case Terminated(backend) =>
println("backend terminated, going to identifying state")
context.setReceiveTimeout(3.seconds)
context.become(receive)
}
def createPath: String = {
val config = ConfigFactory.load("frontend").getConfig("backend")
val name = config.getString("name")
val host = config.getString("host")
val port = config.getString("port")
val system = config.getString("system")
val protocol = config.getString("protocol")
f"$protocol://$system#$host:$port/$name"
}
}
Here is my backend class:
package remoting
import akka.actor.{Actor, Identify, PoisonPill}
import com.typesafe.config.ConfigFactory
class BackendActor extends Actor {
val config = ConfigFactory.load("backend")
override def receive: Receive = {
case "stop" => self ! PoisonPill
case msg: Any => println(f"Received $msg")
}
}
My frontend class:
package remoting
import akka.actor.{Actor, Props}
class FrontendActor extends Actor {
val proxy = context.actorOf(Props[BackendProxyActor], "backendProxy")
override def receive: Receive = {
case msg: Any => proxy ! msg
}
}
and finally my App class:
package remoting
import akka.actor.{ActorSystem, Props}
import com.typesafe.config.ConfigFactory
object Main extends App {
val frontendConfig = ConfigFactory.load("frontend")
val frontend = ActorSystem("frontend", frontendConfig)
val frontendActor = frontend.actorOf(Props[FrontendActor], "FrontendActor")
(1 to 20).foreach(i => frontendActor ! f"Msg #$i")
frontendActor ! "stop"
}
My backend is being started in another process, and is run on port 2551, while my frontend is on port 2552.
I am trying to setup a simple process with Spark Streaming, using Apache Bahir to connect to Akka. I tried to follow their example together with this older one. I have a simple forwarder actor
class ForwarderActor extends ActorReceiver {
def receive = {
case data: MyData => store(data)
}
}
and I create a stream with
val stream = AkkaUtils.createStream[RSVP](ssc, Props[ForwarderActor], actorName)
the configuration looks like this:
akka {
actor {
provider = "akka.remote.RemoteActorRefProvider"
}
remote {
enabled-transports = ["akka.remote.netty.tcp"]
netty.tcp {
hostname = "localhost"
port = 7777
}
}
}
and my problem is: how do I send messages to the Forwarder actor? Maybe I don't understand how Akka Remote is used in this case. When the app starts, I see a log
[akka.remote.Remoting] Remoting started; listening on addresses :[akka.tcp://test#localhost:7777]
and later on I see
[akka.remote.Remoting] Remoting now listens on addresses: [akka.tcp://streaming-actor-system-0#192.168.192.7:52369]
Which seems to remind to the description in the ScalaDoc:
/**
* A default ActorSystem creator. It will use a unique system name
* (streaming-actor-system-<spark-task-attempt-id>) to start an ActorSystem that supports remote
* communication.
*/
All in all I am not sure how I am supposed to send messages to the Forwarder actor. Thanks for any help!
Akka actors can send messages to other Akka actors running on a remote JVM. So... when the sender actor needs to know the address of the intended receiver actor.
AkkaUtil (Bahir) enables you to create a spark-stream from the messages that a ReceiverActor receives. But, where is is going to receive messages from ? Well... some remote actor. And to send messages this remote actor is going to need the address of your ReceiverActor which is running in your spark-application.
In general, you can not be too sure about the ip which will be running your spark application. So, we will make it so that the actor running with spark will tell the producer actor its reference and request it to send its things.
Just make sure that both applications are written using same version of Scala and are running the same JRE.
Now... lets first write the actor who will be the data source,
import akka.actor.{Actor, ActorRef, ActorLogging, ActorSystem, Props}
import akka.actor.Actor.Receive
import com.typesafe.config.{Config, ConfigFactory}
case class SendMeYourStringsRequest(requesterRef: ActorRef)
case class RequestedString(s: String)
class MyActor extends Actor with ActorLogging {
val theListOfMyStrings = List("one", "two", "three")
override def receive: Receive = {
case SendMeYourStringsRequest(requesterRef) => {
theListOfMyStrings.foreach(s => {
requesterRef ! RequestedString(s)
})
}
}
}
object MyApplication extends App {
val config = ConfigFactory.parseString(
"""
|akka{
| actor {
| provider = remote
| }
| remote {
| enabled-transports = ["akka.remote.netty.tcp"]
| untrusted-mode = off
| netty.tcp {
| hostname="my-ip-address"
| port=18000
| }
| }
|}
""".stripMargin
)
val actorSystem = ActorSystem("my-actor-system", config)
var myActor = actorSystem.actorOf(Props(classOf[MyActor]), "my-actor")
}
Now... lets write our simple spark app,
import akka.actor.{Actor, ActorRef, ActorLogging, ActorSystem, Props}
import akka.actor.Actor.Receive
import com.typesafe.config.{Config, ConfigFactory}
import org.apache.spark.SparkConf
import org.apache.spark.streaming.{Seconds, StreamingContext}
import org.apache.spark.streaming.akka.{ActorReceiver, AkkaUtils}
case class SendMeYourStringsRequest(requesterRef: ActorRef)
case class RequestedString(s: String)
class YourStringRequesterActor extends ActorReceiver {
def receive = {
case RequestedString(s) => store(s)
}
override def preStart(): Unit = {
val myActorPath = ActorPath.fromString("akka.tcp://my-actor-system#my-ip-address:18000/user/my-actor")
val myActorSelection = context.actorSelection(myActorPath)
myActorSelection ! SendMeYourStringsRequest(self)
}
}
object YourSparkApp {
def main(args: Array[String]): Unit = {
val sparkConf = new SparkConf().setAppName("ActorWordCount")
if (!sparkConf.contains("spark.master")) {
sparkConf.setMaster("local[2]")
}
val ssc = new StreamingContext(sparkConf, Seconds(2))
val stringStream = AkkaUtils.createStream[String](
ssc,
Props(classOf[YourStringRequesterActor]),
"your-string-requester-actor"
)
stringStream.foreach(println)
}
}
Note :: Just take care of my-ip-address. If there are any other problems, please let me know in comments.
I am trying to run a server and remote actor. The two are up and running successfully. However, the server does not receive the message sent by the remote worker.
import akka.actor._
import akka.actor.Props
import akka.event.Logging
import com.typesafe.config.ConfigFactory
import java.security.MessageDigest
import akka.actor.{Actor, ActorSystem, Props}
import akka.routing.RoundRobinRouter
object Project {
// Define cases
case class register()
case class remoteWorkerActive()
// Main that accepts String argument to determine if the actor
// is the server or a worker
def main(args: Array[String]) {
println("I have " + args.length + " argument(s)")
println(args(0))
// Declare configurations for server and worker remote akka actors
//Attach configuration file of the Server
val serverConfiguration = ConfigFactory.parseString(
"""
akka{
actor{
provider = "akka.remote.RemoteActorRefProvider"
}
remote{
enabled-transports = ["akka.remote.netty.tcp"]
netty.tcp{
hostname = "127.0.0.1"
port = 2575
}
}
}""")
//Attach configuration file of the Worker
val workerConfiguration = ConfigFactory.parseString(
"""
akka{
actor{
provider = "akka.remote.RemoteActorRefProvider"
}
remote{
enabled-transports = ["akka.remote.netty.tcp"]
netty.tcp{
hostname = "127.0.0.1"
port = 0
}
}
}""")
// Based on the input argument, declare an actor system as either
// Server or Worker
if (!args(0).isEmpty) {
//Check if the argument is a valid IP address for a worker
if (args(0).contains('.')) {
//Create the Worker ActorSystem with the above configuration
val system = akka.actor.ActorSystem(
"Remote", ConfigFactory.load(workerConfiguration))
//Create the worker actor
val remote = system.actorOf(Props(
new Remote(args(0))), name = "remote")
remote ! register()
println("This actor is a worker")
}
else {
//Create the Server ActorSystem with the above configuration
val system = akka.actor.ActorSystem(
"Server", ConfigFactory.load(serverConfiguration))
//Create the server actor
val server = system.actorOf(Props[Server], name = "server")
println("This actor is a server")
server ! "test successful";
}
}
}
class Server extends Actor {
def receive = {
case remoteWorkerActive() =>
println("Registration of worker successful")
sender ! "You are a registered worker"
case msg: String =>
println(s"'$msg'")
case _ =>
println("Invalid message")
}
}
class Remote(ip_address: String) extends Actor {
println(ip_address)
val master = context.actorSelection(
"akka.tcp://Server#" + ip_address + ":2575/user/Server")
def receive = {
case register() =>
println("Trying to register with the server")
master ! remoteWorkerActive()
println("Message sent")
case msg: String =>
println(s"'$msg'")
}
}
}
What am I doing wrong? Also, is there any way I can track the messages being sent? Mostly for debugging.
You have:
val server = system.actorOf(Props[Server], name = "server")
and:
val master = context.actorSelection(
"akka.tcp://Server#" + ip_address + ":2575/user/Server")
If Akka ActorPaths are case sensitive, this will fail. Try name = "Server"
I'm trying to send messages to a remote actor, but failed.
My main code is:
RemoteActorDemo.scala
import akka.actor.{Actor, ActorSystem, Props}
object RemoteActorDemo extends App {
val system = ActorSystem("RemoteActorSystem")
val actor = system.actorOf(Props[RemoteActor], name = "RemoteActor")
actor ! "Remote Actor is alive"
}
class RemoteActor extends Actor {
override def receive: Receive = {
case msg =>
println("### RemoteActor received message: " + msg)
sender ! "Hello from RemoteActor"
}
}
With application.conf:
akka {
actor {
provider = "akka.remote.RemoteActorRefProvider"
}
remote {
enabled-transports = ["akka.remote.netty.tcp"]
netty.tcp {
hostname = "127.0.0.1"
port = 5150
}
}
}
And LocalActorDemo.scala:
import akka.actor.{Actor, ActorSystem, Props}
object LocalActorDemo extends App {
val system = ActorSystem("ActorDemo")
val localActor = system.actorOf(Props[LocalActor])
localActor ! "Start"
}
class LocalActor extends Actor {
val remote = context.actorSelection("akka.tcp://RemoteActorSystem#127.0.0.1:5150/user/RemoteActor")
override def receive: Receive = {
case "Start" =>
println("### LocalActor started")
remote ! "Hello from LocalActor"
case msg => println("*** LocalActor receives msg: " + msg)
}
}
The problem is the local actor can't connect the remote one. It prints in console:
### LocalActor started
[INFO] [10/05/2015 20:57:57.334] [ActorDemo-akka.actor.default-dispatcher-4] [akka://ActorDemo/deadLetters]
Message [java.lang.String] from Actor[akka://ActorDemo/user/$a#-11944341] to Actor[akka://ActorDemo/deadLetters]
was not delivered. [1] dead letters encountered. This logging can be turned off or adjusted with configuration
settings 'akka.log-dead-letters' and 'akka.log-dead-letters-during-shutdown'.
I'm new to akka, not sure where is wrong.
You can see the demo project here: https://github.com/freewind/remote-actors-demo, you can just clone and run it as "README" describes.
Add an application.conf in your local subproject with content like this:
akka {
actor {
provider = "akka.remote.RemoteActorRefProvider"
}
remote {
enabled-transports = ["akka.remote.netty.tcp"]
netty.tcp {
hostname = "127.0.0.1"
port = 0
}
}
}
As the official document says:
To enable remote capabilities in your Akka project you should, at a minimum, add the following changes to your application.conf file
This applied to the client side of a remoting system as well.