Search code examples
javaakkaakka-remoting

Unable to send a message to an akka remote actor


I'm new to akka remoting and I'm trying to simply send a message to a remote actor and get a response in return. I have 2 actor systems on localhost - different ports: MasterSystem and WorkerSystem. I have created an actor in WorkerSystem and tried sending a message to its remote address. But I keep on getting a 'dead letters encountered' message! Would appreciate any help. Thanks!

MainMaster.java

package pi_swarm_approx;

import akka.actor.ActorRef;
import akka.actor.ActorSelection;
import akka.actor.ActorSystem;
import akka.actor.UntypedActor;
import com.typesafe.config.ConfigFactory;

public class MainMaster extends UntypedActor{
    ActorSystem system;
    ActorRef actor;
    ActorSelection remoteActor;
    
    public MainMaster() {
        system = ActorSystem.create("MasterSystem", ConfigFactory.load("master"));
        System.out.println("MasterSystem created");
        MainWorker mw = new MainWorker();
        System.out.println("MainWorker obj created");
        remoteActor = mw.system.actorSelection("akka://WorkerSystem@localhost:2552/user/workerActor");
        
        System.out.println("Remote actor created");    
        remoteActor.tell("hello", getSelf());
        System.out.println("Message sent to remote actor");
    }
    
    public void onReceive(Object msg) {
        if (msg != null) {
            System.out.println("Got it back");
        }
         else {
            unhandled(msg);
            getContext().stop(getSelf());
         }
    }
}

MainWorker.java

package pi_swarm_approx;

import akka.actor.ActorSystem;
import akka.actor.ActorRef;
import com.typesafe.config.ConfigFactory;
import akka.actor.Props;

public class MainWorker {
    ActorSystem system;
    ActorRef actor;
    public MainWorker() {
        this.system = ActorSystem.create("WorkerSystem", ConfigFactory.load("worker"));
        actor = system.actorOf(Props.create(Worker.class), "workerActor");
    }
}

Worker.java

package pi_swarm_approx;

import akka.actor.UntypedActor;

public class Worker extends UntypedActor {  
    public void onReceive(Object msg) {
        System.out.println("Worker actor got message");
        if (msg != null) {
            getSender().tell("Request processed", getSelf());
        }
         else {
            unhandled(msg);
         }
        getContext().stop(getSelf());
    }
}

master.conf

akka {
  actor {
    provider = "cluster"
  }
  remote {
  transport = ["akka.remote.netty.tcp"]
    netty.tcp {
      hostname = "localhost"
      port = 2551
    }
  }
  
  clustering {
    cluster.name = "MasterSystem"
    role = "master"
  }
 }

worker.conf

akka {
  actor {
    provider = "cluster"
    deployment {
        /workerActor {
          remote = "akka.tcp://WorkerSystem@localhost:2552"
        }
  }
}

Output

In main
MasterSystem created
MainWorker obj created
Remote actor created
Message sent to remote actor
[INFO] [11/22/2021 16:01:34.531] [WorkerSystem-akka.actor.default-dispatcher-5] [akka://WorkerSystem/deadLetters] Message [java.lang.String] from Actor[akka://Main/user/app#402333018] to Actor[akka://WorkerSystem/deadLetters] was not delivered. [1] dead letters encountered. If this is not an expected behavior, then [Actor[akka://WorkerSystem/deadLetters]] may have terminated unexpectedly, This logging can be turned off or adjusted with configuration settings 'akka.log-dead-letters' and 'akka.log-dead-letters-during-shutdown'.
<=========----> 75% EXECUTING [18s]

Solution

  • There are multiple problems with the code that you have posted. I am posting a minimal working code.

    First, you are using a deprecated version of akka.actor.UntypedActor. This was deprecated in 2.4.0. If you are using maven change dependencies accordingly. Code was compiled and run on java 11.

    build.sbt

    libraryDependencies ++=
      Seq(
        "com.typesafe.akka" %% "akka-actor" % "2.6.17",
        "com.typesafe.akka" %% "akka-remote" % "2.6.17",
      )
    

    For provider I have used remote instead of cluster. You can use cluster, but make sure you add the necessary dependencies. Configuration can be further simplified by removing repetitions, but you can do that as you explore.

    master.conf

    akka {
      actor {
        provider = "remote"
      }
      remote {
        artery {
          enabled = on
          transport = tcp
          canonical {
            hostname = "127.0.0.1"
            port = 2552
          }
        }
      }
    }
    

    worker.conf

    akka {
      actor {
        provider = "remote"
      }
      remote {
        artery {
          enabled = on
          transport = tcp
          canonical {
            hostname = "127.0.0.1"
            port = 2551
          }
        }
      }
    }
    

    MainMaster.java

    import akka.actor.AbstractActor;
    
    public class MainMaster extends AbstractActor {
        @Override
        public Receive createReceive() {
            return receiveBuilder()
                .match(
                        String.class,
                        System.out::println)
                .matchAny(o -> System.out.println("received unknown message"))
                .build();
        }
    }
    

    Worker.java

    public class Worker extends AbstractActor {
    
        @Override
        public Receive createReceive() {
            return receiveBuilder()
                .match(
                        String.class,
                        msg -> {
                            System.out.println(msg);
                            getSender().tell("Request processed", getSelf());
                        })
                .matchAny(o -> System.out.println("received unknown message"))
                .build();
        }
    }
    

    MainWorker.java

    public class MainWorker {
    
        public static void main(String[] args) {
            ActorSystem system = ActorSystem.create("WorkerSystem", ConfigFactory.load("worker.conf"));
            ActorRef actor = system.actorOf(Props.create(Worker.class), "workerActor");
            System.out.println("worker started");
        }
    }
    

    Main.java

    public class Main {
        public static void main(String[] args) {
            System.out.println("In main");
            ActorSystem system = ActorSystem.create("MasterSystem", ConfigFactory.load("master.conf"));
            ActorRef master = system.actorOf(Props.create(MainMaster.class), "master");
    
            ActorSelection remoteActor = system.actorSelection("akka://[email protected]:2551/user/workerActor");
            remoteActor.tell("Hello Worker", master);
        }
    }