First glance of AKKA.Net

https://getakka.net/

Functional programming, reactive programming that provide us a different thinking and means for dealing the real world problems comparing to object oriented(OO) programming. As we known, OO provide us good flow for handling synchronous data and interactivity between objects. But sometimes for communication/coordination under asynchronous environment, reactive programming give us a more abstract level and efficiency ways(lock free).

Scenario

Assume that we have monitoring center that manage a group of device(s) across different area/zone on network. Each device would dynamic join/leave the group for any reasons, the center would like to control(manage) and monitor stat of each device(s).

Scenario overview

As above picture show that monitoring center will manage below device(s) from different action/behavior. For example, keeping alive, sending alarm event(s), notice messaging…etc.

How to solve using AKKA.Net framework

AKKA has introduced the actor model that passing the event(s) across tree hierarchy. With children-parent relationship make it easily to control each nodes status and handle fault/crash recovering. That’s start initial the management node(from server) and assume that the devices(on client) might join/leave the entire monitor system dynamically. Our project structure as below:

Project structure

Server side

Buildup actor system through AKKA configuration setting(hocon): akkaConfig.xml. Inside the system, create actor: InputActor for handling user input interactivity. Create another actor: SenderActor for processing the communication between clients, the initial stat would be waiting client to register.

// Server entry point
static void Main(string[] args)
{
var builder = new ConfigurationBuilder()
.SetBasePath(Directory.GetCurrentDirectory())
.AddXmlFile("akkaConfig.xml");
Configuration = builder.Build();
var config =
ConfigurationFactory.ParseString(Configuration.GetSection
("akka:hocon").Value);
using (var actorSys =
ActorSystem.Create(Constants.ALARM_ACTOR_SRV_SYS, config))
{
var inputActor = actorSys.ActorOf(Props.Create<InputActor>(), "Input");
var senderActor = actorSys.ActorOf(Props.Create<AlarmSenderActor>(inputActor), Constants.ALARM_SENDER_NAME);
Message cmd = new Message()
{
Type = MessageType.ServerWaitSubscribe
};
senderActor.Tell(cmd);
actorSys.WhenTerminated.Wait();
}
}
}

Configuration over AKKA.Net not support dependency injection for .Net core, so we used xml file instead and parsing source string as workaround.

# akkaConfig.xml
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<akka>
<hocon>
<![CDATA[
akka {
# here we are configuring log levels
log-config-on-start = off
stdout-loglevel = INFO
loglevel = ERROR
# this config section will be referenced as akka.actor
actor {
provider = "Akka.Remote.RemoteActorRefProvider,
Akka.Remote"
debug {
receive = on
autoreceive = on
lifecycle = on
event-stream = on
unhandled = on
}
}
# here we're configuring the Akka.Remote module
remote {
helios.tcp {
transport-class = "Akka.Remote.Transport.Helios.HeliosTcpTransport, Akka.Remote"
#applied-adapters = []
transport-protocol = tcp
port = 8091
hostname = ""
public-hostname = "public.domain.address"
}
}
log-remote-lifecycle-events = INFO
}]]>
</hocon>
/akka>
</configuration>

hostname: left blank might dynamically assign host for the actor system

pubic-hostname: useful while you deploy application inside the docker environment

Client side

Startup from new actor system (initial with client side configuration). Inside the system, create actor: AlarmReceiverActor for subscription to server, after connection setup, bidirectional communication would be available.

// Client entry point
static void Main(string[] args)
{
// load the configuration xml
// ...
using (var actorSys = ActorSystem.Create(Constants.ALARM_ACTOR_CLIENT_SYS, config))
{
Guid subscriberGuid = Guid.NewGuid();
var receiverActor =
actorSys.ActorOf(Props.Create<AlarmReceiverActor>(), string.Format(Constants.ALARM_RECEIVER_NAME, subscriberGuid.ToString()));
receiverActor.Tell(new SubscribeMessage(subscriberGuid));
actorSys.WhenTerminated.Wait();
Thread.Sleep(2000); // let user know the terminate notice message
}

// we could access/communicate the server actor via below snippet code
var senderActor = Context.ActorSelection("akka.tcp://ALARM_ACTOR_SRV_SYS@public.domain .address:8091/user/Sender");
// then send message to actor
senderActor.Tell(....);
}

public.domain.address: same address value inside the section *remote:public-host* (akkaConfig.xml) over the sever side

Simple demonstration

Let’s start the server process first then initialize one client. Server will keep waiting for client to register. While once client(at least one) got subscribed, the server turned into command mode.

Initial server
Initial client-1

Server Begun communication with client via sending [1]alarm message, [2]testing message and [3]terminate message.

Sent message from server

Client received the message immediately.

Received message on client

Registered another client.

Client-2 joined

Sent new alarm message to both client(broadcast).

Client-1 received new alarm message
Client-2 received new alarm message

Finally, sent the terminate message to all clients(broadcast).

Client-2 received terminate message

Reference

Coding for fun