I have a server-client architecture where there are many clients and one server. I'd like to create an actor in each client and put them on the server actor system.
I tried to do this dynamically. After creating the actor remotely (which I'm not sure is successful) I can't select it. Here is my server:
class TestActorSystem {
val system = ActorSystem("testSystem", ConfigFactory.load("server.conf"))
def shutdown = system.shutdown()
Server Config:
akka {
#loglevel = "DEBUG"
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
}
}
Here is my client:
class Client(id: String, remoteAddress: Address) {
def this(id: String) = {
this(id, Address("akka.tcp", "testSystem", "127.0.0.1", 2552))
}
implicit val timeout = Timeout(600.seconds)
val conf = ConfigFactory.load()
val system = ActorSystem("client", ConfigFactory.load("client.conf"))
val remote = remoteAddress.toString
private val broadcaster = system.actorSelection(Props[MyActor].withDeploy(Deploy(scope = RemoteScope(remoteAddress))), name = "joe")
def shutdown() = system.shutdown
def send = {
val c = system.actorSelection("akka.tcp://testSystem#127.0.0.1:2552/user/joe")
c ! "simple message"
}
}
Client Config:
akka {
#log-config-on-start = on
stdout-loglevel = "DEBUG"
loglevel = "DEBUG"
actor {
provider = "akka.remote.RemoteActorRefProvider"
}
remote {
enabled-transports = ["akka.remote.netty.tcp"]
log-sent-messages = on
log-received-messages = on
netty.tcp {
hostname = "127.0.0.1"
port = 0
}
}
}
I start the server and then run the client, here is what I think is the relevant error message:
[INFO] [04/04/2014 09:32:09.631] [testSystem-akka.actor.default-dispatcher-17] [akka://testSystem/user/joe] Message [java.lang.String] from Actor[akka://testSystem/deadLetters] to Actor[akka://testSystem/user/joe] 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've also tried to create remote actors through my configuration file with similar results. Any idea appreciated! I'm not sure if this matters but I'm running this code through my IDE.
You should use actorOf to create actors instead of actorSelection.
val ref = system.actorOf(
Props[SampleActor].withDeploy(
Deploy(scope = RemoteScope(address))))
See Programmatic Remote Deployment
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 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
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
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.