# How to implement remote messaging?

When two or more applications residing on different machines need to communicate with each other we use remote messaging.

For example to implement a simple master and slave on a remote server, we can start with the slave first registers to the master and the master responds with a welcome message before they start communicating task exchange messages. Here is how we set up remote-messaging for this scenario:

  1. Both the projects need to have a shared kprocess module which has the messages being exchanged defined. For example: SharedProcess.kprocess would have the following messages:
message Register(me:String,gap:Int)
message Welcome(you:String)
//other task related messages
...

  1. The master project will have a kprocess (say MasterProcess.kprocess) with a processor (BigBoss) defined to accept the Register message and send a Welcome to the remote-slave.
processor BigBoss(bigBossHandler) singleton {
    accept message-ref SharedProcess::Register
    send remote (Worker) SharedProcess::Welcome
}

Note:

  • String bigBossHandler is the actorName used to reference the actor through actorPath
  • String Worker here is just any name given to the slave's processor.
  1. Slave project will also have its kprocess (say SlaveProcess.kprocess) with a processor (MyWorker) which has the definition of the remote message as follows:
processor MyWorker(myWorkerHandler) singleton {
    accept message-ref SharedProcess::Welcome
    send remote (BigBossHandler) SharedProcess::Register
}

Note: String BigBossHandler here is just a plain name given to the master's processor.

  1. Inorder to send a remote message to Master we use the SmileActor System's actorSelection api passing the configured actorPath of the master residing in the remote machine as follows:
class MyWorkerProcessorCode extends MyWorkerProcessor.Consumers {

  override def initState: MyWorkerState = new MyWorkerState()

  val masterHostPort = Smile.conf.getString("master.host.port").getOrElse("localhost:2552")
  val bigBossHandlerURL = s"akka.tcp://SmileActorSystem@$masterHostPort/user/bigBossHandler"
  def getBigBossActorSelection: akka.actor.ActorSelection = Smile.actorSystem.akkaActorSystem.actorSelection(bigBossHandlerURL);
  
  override def preStart(): Unit = {
    sendRemoteToBigBossHandler(to = getBigBossActorSelection, message = Register(me = "worker1", gap = 10), from = processor.actorRef)
  }

  override def process(message: Welcome, sender: ActorRef): Unit = {
    println("Welcomed as " + message.you)
  }

}
  1. Set up the akka remote actor configuration in master's Smile.conf with the actual hostname and port:
akka {
  actor {
    provider = "akka.remote.RemoteActorRefProvider"
    warn-about-java-serializer-usage = false
  }
  remote {
    enabled-transports = ["akka.remote.netty.tcp"]
    netty.tcp {
      hostname = "ec2-12-222-178-101.ap-southeast-1.compute.amazonaws.com"
      port = 2552
    }
  }
}
  1. Set up the processor as a pinned-dispatcher: Smile.conf:
bigboss-handler-pinned-dispatcher {
  executor = "thread-pool-executor"
  type = PinnedDispatcher
}

Use this dispatcher as the props in dressUp in the ActorConfig.scala:

object BigBossActorConfig  {
   
   def dressUp(props: Props): Props = props.withDispatcher("bigboss-handler-pinned-dispatcher")

}
  1. In the master's BigBossHandlerCode, just need to reply back to the sender:
class BigBossProcessorCode extends BigBossProcessor.Consumers {
...
override def process(message: Register, sender: ActorRef): Unit = {
    sendRemoteToWorker(to = sender, message = Welcome(message.me), from = processor.actorRef)
  }
...
}