Where is wrong in my remote actor demo? - scala

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.

Related

Hi, I'm new to akka actors, actually I want to implement an application in which a remote actor send message to a local actor, on same machine

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

Apache Bahir, send stuff to ActorReceiver

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.

Akka actor does not receive message with DistributedPubSub

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

Akka Actor: Remote actor exception "Futures timed out after"

i am new for Akka, i am using Akka 2.3.3 version for creating actors. I am going to create remote actor and trying to access with client. Whenever i am going to run test-case, the following exception will throw:
[INFO] [04/27/2016 07:51:23.727] [Localsystem-akka.actor.default-dispatcher-3] [akka://Localsystem/deadLetters] Message [com.harmeetsingh13.chapter2.messages.SetRequest] from Actor[akka://Localsystem/temp/$a] to Actor[akka://Localsystem/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'.
[INFO] [04/27/2016 07:51:23.745] [Localsystem-akka.actor.default-dispatcher-3] [akka://Localsystem/deadLetters] Message [com.harmeetsingh13.chapter2.messages.GetRequest] from Actor[akka://Localsystem/temp/$b] to Actor[akka://Localsystem/deadLetters] was not delivered. [2] 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'.
Futures timed out after [10 seconds]
java.util.concurrent.TimeoutException: Futures timed out after [10 seconds]
at scala.concurrent.impl.Promise$DefaultPromise.ready(Promise.scala:219)
at scala.concurrent.impl.Promise$DefaultPromise.result(Promise.scala:223)
at scala.concurrent.Await$$anonfun$result$1.apply(package.scala:190)
at scala.concurrent.BlockContext$DefaultBlockContext$.blockOn(BlockContext.scala:53)
at scala.concurrent.Await$.result(package.scala:190)
at com.harmeetsingh13.chapter2.SClientIntegrationSpec$$anonfun$1$$anonfun$apply$mcV$sp$1.apply$mcV$sp(SClientIntegrationSpec.scala:18)
at com.harmeetsingh13.chapter2.SClientIntegrationSpec$$anonfun$1$$anonfun$apply$mcV$sp$1.apply(SClientIntegrationSpec.scala:15)
at com.harmeetsingh13.chapter2.SClientIntegrationSpec$$anonfun$1$$anonfun$apply$mcV$sp$1.apply(SClientIntegrationSpec.scala:15)
at org.scalatest.Transformer$$anonfun$apply$1.apply$mcV$sp(Transformer.scala:22)
at org.scalatest.OutcomeOf$class.outcomeOf(OutcomeOf.scala:85)
at org.scalatest.OutcomeOf$.outcomeOf(OutcomeOf.scala:104)
at org.scalatest.Transformer.apply(Transformer.scala:22)
at org.scalatest.Transformer.apply(Transformer.scala:20)
at org.scalatest.FunSpecLike$$anon$1.apply(FunSpecLike.scala:422)
at org.scalatest.Suite$class.withFixture(Suite.scala:1122)
at com.harmeetsingh13.chapter2.SClientIntegrationSpec.withFixture(SClientIntegrationSpec.scala:11)
at org.scalatest.FunSpecLike$class.invokeWithFixture$1(FunSpecLike.scala:419)
at org.scalatest.FunSpecLike$$anonfun$runTest$1.apply(FunSpecLike.scala:431)
at org.scalatest.FunSpecLike$$anonfun$runTest$1.apply(FunSpecLike.scala:431)
at org.scalatest.SuperEngine.runTestImpl(Engine.scala:306)
at org.scalatest.FunSpecLike$class.runTest(FunSpecLike.scala:431)
at com.harmeetsingh13.chapter2.SClientIntegrationSpec.runTest(SClientIntegrationSpec.scala:11)
at org.scalatest.FunSpecLike$$anonfun$runTests$1.apply(FunSpecLike.scala:464)
at org.scalatest.FunSpecLike$$anonfun$runTests$1.apply(FunSpecLike.scala:464)
at org.scalatest.SuperEngine$$anonfun$traverseSubNodes$1$1.apply(Engine.scala:413)
at org.scalatest.SuperEngine$$anonfun$traverseSubNodes$1$1.apply(Engine.scala:401)
............
My Server code as below:
Main.scala
object Main extends App{
private val configFile = getClass.getClassLoader.getResource("application.conf").getFile;
private val config = ConfigFactory.parseFile(new File(configFile ))
val system = ActorSystem("SimpleClientServer", config)
system.actorOf(Props[AkkadmeyDB], name = "akkademy-db")
}
application.conf:
akka{
actor{
provider = "akka.remote.RemoteActorRefProvider"
}
remote{
enabled-transports = ["akka.remote.netty.tcp"]
netty.tcp {
hostname = "127.0.0.1"
port = 2552
}
log-sent-messages = on
log-received-messages = on
}
}
AkkadmeyDB.scala Actor class:
class AkkadmeyDB extends Actor{
val map = new HashMap[String, Object]
val log = Logging(context.system, this)
override def receive: Receive = {
case SetRequest(key, value) =>
log.info("received SetRequest - key: {} value: {}", key, value)
map.put(key, value)
sender() ! Status.Success
case GetRequest(key) =>
log.info("received GetRequest - key: {}", key)
val response = map.get(key)
response match{
case Some(x) => sender() ! x
case None => Status.Failure(new KeyNotFoundException(key))
}
case o => Status.Failure(new ClassNotFoundException())
}
}
Client Code as below:
SClient.scala
class SClient(remoteIp: String) {
private implicit val timeout = Timeout(10 seconds)
private implicit val system = ActorSystem("Localsystem")
private val remoteAddress = s"akka.tcp://SimpleClientServer#$remoteIp/user/akkademy-db";
private val remoteDb = system.actorSelection(remoteAddress)
def set(key: String, value: Object) = {
remoteDb ? SetRequest(key, value)
}
def get(key: String) = {
remoteDb ? GetRequest(key)
}
}
SClientIntegrationSpec.scala Test case:
class SClientIntegrationSpec extends FunSpecLike with Matchers {
val client = new SClient("127.0.0.1:2552")
describe("akkadment-db-client"){
it("should set a value"){
client.set("jame", new Integer(1313))
val futureResult = client.get("james")
val result = Await.result(futureResult, 10 seconds)
result should equal (1313)
}
}
}
When i see the logs of my remote application, this seems like, the request hit doesn't go to the server. what is the problem in my sample code running?
For solving above problem, we need to follow two steps that are metnion below:
When i am creating a server code, i am excluding application.conf from my server application, that's why, client application not able to connect with server. The code are using in built.sbt is as below:
mappings in (Compile, packageBin) ~= { _.filterNot { case (_, name) =>
Seq("application.conf").contains(name)
}}
After commenting above code, the client see the server successfully.
In Learning Scala chapter 2 jasongoodwin explain the code of client and server actor system. But there are some Errata in book and missing application.conf configuration for client. Because when we run both code in same PC, we are facing already port bind exception because by default actors are using 2552 port for accessing and we already define this port for our server application. So, application.conf also need for client as below:
akka {
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
}
}
Here Port 0 means any free port.
After that, above code are running successfully.
There is an application.conf file in the client project as well which is not mentioned in the book.
Make sure you create that file under the resources folder with the following content:
akka {
actor {
provider = "akka.remote.RemoteActorRefProvider"
}
}
See the official github repo

Scala: server and Worker actor bind but fail to exchange messages?

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"