Overview
Home of the W application. R application can be found here
Simple explanation of the app
User sends a request (restful) to save a record, the controller then creates a command asking the writeWorker to write this record to db. After that an event is published to the Event-stream.
Rest API
<host>/api/expenses/write?name=<name-of-the-food>&amount=<paid-amount>
Local connection
//command ... com.nshimiye.cqrs.writer.akka.WriteWorker
ActorSelection brokerWorker = AkkaFactory.getActorSystem(SystemType.REMOTE)
.actorSelection("/user/brokerWorker");
/user/brokerWorker
is the local path of brokerWorker actor
Remote connection
Remote Akka Actors connect using either TCP or UDP. In our case, we use TCP connection. Example below shows how it is done
// query ... com.nshimiye.akka.AkkaInitializer
// Akka configuration values
private final String BROKER_URL = "akka.tcp://AKKAREMOTESystem@192.168.99.101:2555/user/brokerWorker";
...
ActorSelection messageBrokerFinder = AkkaFactory.getActorSystem(SystemType.REMOTE)
.actorSelection(BROKER_URL);
akka.tcp://AKKAREMOTESystem@192.168.99.101:2555/user/brokerWorker
is the remote path of brokerWorker actor.
Configuration
When an actor system is created, it looks for configuration information from the application.conf
and/or reference.conf
files. These files are assumed to be in the resource folder of the application.
If none of these are found, the default configuration is used
Below are the required configuration for creating a remote system:
// command ... application.conf
akka {
actor {
provider = "akka.remote.RemoteActorRefProvider"
serialization-bindings {
# specify java objects that need to be serialized and the serialization type to be used
}
}
remote {
enabled-transports = ["akka.remote.netty.tcp"]
netty.tcp {
hostname = 192.168.99.101 # external (logical) hostname, ex: my.domain.com
port = 2555 # external (logical) port
bind-hostname = 0.0.0.0 # internal (bind) hostname, ex: local.address
bind-port = 2553 # internal (bind) port
}
}
}
-
hostname
andport
are used by the actor system to listen to incoming connections from other outside actors. Note: These outside actors have to be part of remote system (same or different from the listening system) in order to connect. - serialization is very important because it informs the system on how to read/convert the received/sent data over a tcp connection.
Actors
Two different types of actor exist:
- Local actor: actor created inside a local system
- Remote actor: actor created inside a remote system
- Local actors can only communication with other (local and remote) actors on the same JVM
- Remote actor can interact with actors on the same JVM and other remote actors on different JVM or different physical machine.
Example of actor creation:
- W application
// command ... com.nshimiye.akka.AkkaInitializer
// local actor
writeWorker = AkkaFactory.getActorSystem(SystemType.LOCAL).actorOf(WriteWorker.createWorker(), "writeWorker");
// remote actor
brokerWorker = AkkaFactory.getActorSystem(SystemType.REMOTE).actorOf(Props.create(BrokerWorker.class), "brokerWorker");
- R application
// query ... com.nshimiye.akka.AkkaInitializer
denomalizer = AkkaFactory.getActorSystem(SystemType.REMOTE).actorOf(Props.create(DenomalizerWorker.class), "denomalizer");
writeWorker
can interact with brokerWorker
. However, it cannot interact with denomalizer
because they are not on the machines.
Event Bus
- Event Bus is a message broker and it's role is to facilitate communication between akka actors.
- Each Akka system has a builtin event bus called Event Stream.
- Event Stream can be used to send messages to multiple actors at once (publish-subscribe model). Below is an example usage
// command ... com.nshimiye.messaging.BrokerWorker
getContext().system().eventStream().subscribe(getSender(), Envelope.class);
...
getContext().system().eventStream().publish((Envelope) message);
In the above example, two things are happening:
- brokerWorker subscribes an actor
getSender()
that requests to be subscribed. - Later the brokerWorker publishes an event of type
Envelope
to the Event Stream. After this, the Event Stream will send a message to the subscribed actors.
Running
- Using docker
gradle clean buildDocker
docker run --rm=true -e HOSTNAME=192.168.99.101 --net=host springio/akka-spring-command
The important part here is --net=host
, which tells the container to bind itself to the docker's main ipaddress instead of going through NAT.