Donnerstag, 3. Dezember 2015

Akka.NET Adventskalender – Tür 3

Hallo Aktor

Jetzt wo wir über die notwendigen Grundlagen verfügen, können wir unsere erste Anwendung schreiben und unseren ersten Aktor einsetzen. Starte Deine Entwicklungs-Umgebung und erzeuge ein neues Konsolen-Projekt. Alle Beispiele, die wir bis Weihnachten erstellen, werden Kommandozeilen Anwendungen sein. Der Hauptgrund dahinter ist, dass so plattformbedingte Unterschiede, die es bei GUI Anwendungen gäbe, dann nicht auftreten.
Leider habe ich heute keinen Link auf ein github-Projekt, aber Du wirst sehen, wie wenig wir selbst schreiben müssen.

So, nun wo das Projekt da ist, füge "Akka" zur Liste er NuGet Pakete hinzu. Nun sollte sich diese Klasse compilieren lassen:

using System;
using Akka.Actor;

namespace AkkaHelloWorld
{
    public class MainClass
    {
        public static void Main(string[] args)
        {
            var system = ActorSystem.Create("World");

            // TODO: do something with actor system

            Console.WriteLine("Press [enter] to continue");
            Console.ReadLine();

            system.Shutdown();
            system.AwaitTermination();
        }
    }
}

Was passiert hier?

Leider noch nicht viel, aber wir sehen den typischen Aufbau einer Akka.NET Anwendung. Wir erzeugen ein ActorSystem, geben ihm einen Namen, tun etwas mit diesem System (was noch vor uns liegt) und beenden das System wieder kontrolliert. Damit unser System genug Gelegenheit hat, zu arbeiten, warten wir auf eine Zeilen-Eingabe, damit es sich nicht unverrichteter Dinge wieder beendet.

Zeit unseren ersten Aktor zu schreiben. Leg' einfach eine Klasse "Hello" im gleichen Namensraum an, in dem auch die Konsolen-Applikation liegt. Das sollte für den Moment reichen. Was die Benennung von Aktoren-Klassen angeht, so sieht man auch häufig Namen, die das Wort "Actor" als Anhang tragen und damit mehr den in der .NET Welt üblichen Konventionen entsprechen. In dieser Artikel-Serie verwende ich diese Konvention nicht, es spricht aber nichts dagegen, dass Du es in Deinen Projekten machst. Entscheide solche Dinge einfach mit Deinem Team.

using System;
using Akka.Actor;

namespace AkkaHelloWorld
{
    public class Hello : UntypedActor
    {
        protected override void OnReceive(object message)
        {
            Console.WriteLine("Received: '{0}'", message);
        }
    }
}

Was macht dieser Aktor?

Zunächst bitte ich Dich, die Basisklasse (UntypedActor) zu ignorieren. Wir werden uns mit ein paar Basisklassen für Aktoren morgen intensiver befassen. Für heute genügt zunächst zu wissen, dass die Basisklasse UntypedActor eine Methode OnReceive() erwartet, die für die Behandlung aller eintreffenden Nachrichten verantwortlich ist. Behalte dabei bitte im Kopf, dass die Mailbox ein FIFO Speicher ist und Nachrichten einzeln bearbeitet werden, niemals parallel!

Wie senden wir nun Nachrichten?

Das siehst Du sofort, es ist ganz einfach. Ersetze in der Konsolen Anwendung einfach den mit TODO markierten Kommentar durch diese zwei Zeilen:

var hello = system.ActorOf<Hello>();
hello.Tell("Hello World");

Wenn Du nun Dein Programm startest, solltest Du diese Ausgabe auf deinem Terminal-Fenster erhalten:

Received: 'Hello World'
Press [enter] to continue

Wunderbar! Dein erster Aktor hat seine erste Nachricht (einen String) erhalten.

Was genau ist hier passiert?

Sieht eigentlich ganz einfach aus, macht aber einen recht umständlichen Eindruck. Aktoren sind ja nicht einfach nur Klassen, die instantiiert werden wie Objekte, sondern werden durch eine Laufzeitumgebung gesteuert, müssen be Bedarf neu gestartet werden und laufen möglicherweise auf einem anderen Rechner. Insofern ist die Konstruktion ein wenig aufwändiger, als der reine Konstruktor-Aufruf.

Zur Erzeugung eines Aktors kann ein Objekt des Typs Props verwendet werden. Die nachfolgenden Zeilen zeigen die grundsätzlichen Alternativen, die sich bei der Benutzung von Props bieten. Zusätzlich können dem eigentlichen Konstruktur noch weitere Parameter mitgegeben werden – übergebe sie einfach der Create Methode mit. Und auch die ActorOf() Methode kann wietere Parameter z.B. einen Namen bekommen. Wird kein Name angegeben, so vergibt Akka.NET einen innerhalb dieser Hierarchie eindeutigen.

var hello = system.ActorOf<Hello>();
var hello = system.ActorOf(Props.Create<Hello>());
var hello = system.ActorOf(Props.Create(typeof(Hello));
var hello = system.ActorOf(Props.Create(() => new Hello()));

Alle Erzeugungs-Methoden liefern eine ActorRef. Das ist der vorher erwähnte Proxy, der sich wie ein Aktor verhält und den genauen Aufenthaltsort des Aktors kennt. Wir müssen uns darum nicht kümmern.

Mittels des ActorRef Objekts können wir über die Methode Tell() Nachrichten an den Aktor senden. Technisch gesehen ist eine Nachricht ein beliebiges CLR Objekt – es muss nur serialisierbar sein. Einfache Wert-Datentypen wie double oder int funktionieren ebenso problemlos wie aus Klassen instantiierte Objekte oder string Literale. Wir werden uns aber noch eingehender mit Nachrichten befassen.

Bitte hebe die heutigen Projekt-Dateien auf, wir werden morgen weiter darauf aufbauen.

Kommentare:

  1. Vielen Dank, ich finde diese Art (täglich eine kleine Dosis) etwas kennenzulernen sehr interessant.
    Meine Frage ist: ich erhalte folgende Meldung beim Schliessen des Programms:
    [INFO][03.12.2015 05:13:23][Thread 0009][akka://HelloWorldSystem/user] Message DeathWatchNotification from akka://HelloWorldSystem/user to akka://HelloWorldSystem/user was not delivered. 1 dead letters encountered.
    Wie ist das zu verstehen und könnte man ggf. etwas tun um diese Meldung nicht zu erhalten?

    AntwortenLöschen
  2. Sorry, ich habe wohl eine Mail übersehen, daher die späte Antwort. Und danke für das Interesse.

    Auf Dead Letters gehe ich in der Artikel-Serie leider nicht ein. Dahinter steckt ein Mechanismus, der Nachrichten, die nicht verarbeitet werden, an eine globale Mailbox – vertreten durch einen Aktor, der über Context.System.DeadLetters erreichbar ist, weiter geleitet werden.

    In diesem Fall wird beim Herunterfahren des Aktor-Systems eine DeathWatchNotification von Kind-Aktoren zu deren (inzwischen nicht mehr existenten) Eltern gesandt oder aber von deren Eltern nicht beachtet. Beides führt dann dazu, dass der DeadLetters Aktor diese Nachrichten erhält und loggt.

    Würden solche Dinge im Laufenden Betrieb auftauchen, sollte man sich eventuell Sorgen machen, da man das "Ableben" eines Aktors nicht ordentlich behandelt hat, beim Herunterfahren des Systems halte ich das für unkritisch.

    AntwortenLöschen