Montag, 7. Dezember 2015

Akka.NET Adventskalender – Tür 7

Aktor klein ging allein...

Willkommen zu unserem zweiten Sprint. Diese Woche werden wir uns auf einzeln auftretende Aktoren konzentrieren und uns gemeinsam ansehen, was wir alles mit ihnen anstellen können.

Wenn Du vorher noch nicht viel mit Aktoren gearbeitet hast wirst Du Dir sicher ein paar Fragen stellen:
  • Wie umfangreich soll so ein Aktor werden?
  • Wie finden die Aktoren sich gegenseitig, wenn sie sich unterhalten wollen?
  • Gibt es eine maximale Anzahl von Aktoren, die Du anlegen darfst?

Wie umfangreich soll so ein Aktor werden?

Wenn Du diese Artikel liest, vermute ich, Du kommst mit einem objektorientierten Hintergrund. Dann kennst Du bestimmt das Single-Responsibility Prinzip, das Robert C. Martin zuerst unter dieser Bezeichnung in seinem Buch "Clean Code" veröffentlicht hat. Gemäß diesem Prinzip bekommt jede Klasse exakt eine Verantwortlichkeit (Original-Formulierung: "one reason to change"). Für schlechtes Design würde man Klassen halten, die zu viel Aufgaben erledigen wie z.B. ConfigurationAdminNotificationRestarterNagiosReporter.  Solch eine Klasse gibt es sicher in keinem Deiner Projekte.

Das gleiche Prinzip gilt für Aktoren. Ein Aktor erfüllt eine Aufgabe und er erfüllt sie vollständig und gut. Versuche also Deine Aktoren klein zu halten, wenn es irgend möglich ist. Wenn ein Aktor mehr erledigen muss, überlege Dir ob das nicht eine Aufgabe für einen weiteren Aktor werden könnte. Dieses Vorgehen hält Aktoren testbar, verständlich und wartbar.

Das führt uns zur zweiten Frage:

Wie finden sich die Aktoren?

Hier hast Du mehrere Möglichkeiten:
  • Wenn Du einen übergeordneten Aktor hast (im OO Umfeld kennst Du dieses Muster unter dem Namen "Mediator"), dann erzeugt dieser seine Mitstreiter und verbindet sie miteinander, indem die diversen Referenzen der einzelnen Aktoren den jeweiligen Aktoren mitgeteilt werden. So "kennt" jeder die notwendigen Aktoren.
  • Alternativ können die Kind-Aktoren auch an den übergeordneten berichten, der dann wiederum entsprechend reagiert.
  • Akka.NET kennt ein Konstrukt namens ActorSelection. Du weißt ja, dass Aktoren in einer Baumstruktur angeordnet sind. Eine Selektion ist ein Pfad (wahlweise absolut von der Wurzel aus oder relativ zur aktuellen Stelle) mit wahlweise Joker-Zeichen. Solch eine Selektion könnte wie "../worker*" oder "/user/dashboard/*" aussehen. Ersteres Beispiel würde alle mit "worker" beginnenden Eltern-Aktoren betreffen, das zweite Beispiel alle unter "user/dashoard" sitzenden Aktoren. Benutzt wird solch eine Selektion fast wie eine AktorRef, zumindest was die Möglichkeiten angeht, einen Tell() Aufruf auszulösen.
  • Es gibt einen globalen EventStream, der es ermöglicht, Ereignisse zu publizieren und zu abonnieren. Auch dazu werden wir in ein paar Tagen ein Beispiel sehen.

Etwas mehr Details zur Addressierung von Aktoren finden sich in der Akka.NET Dokumentation.

Zum demonstrieren erzeugen wir einen einfachen Aktor, der seinen Namen und den erhaltenen Text ausgibt:

using System;
using Akka.Actor;

namespace ActorSelection
{
    public class Writer : ReceiveActor
    {
        public Writer()
        {
            Receive<string>(s =>
                Console.WriteLine("{0} received {1}", Self.Path.Name, s));
        }
    }
}

Und die passende Applikation dazu:

using System;
using Akka.Actor;
using System.Threading;

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

            var writer1 = system.ActorOf(Props.Create<Writer>(), "writer1");
            var writer2 = system.ActorOf(Props.Create<Writer>(), "writer2");
            var writer3 = system.ActorOf(Props.Create<Writer>(), "writer3");

            var xxx1 = system.ActorOf(Props.Create<Writer>(), "xxx1");
            var xxx2 = system.ActorOf(Props.Create<Writer>(), "xxx2");

            var printer = system.ActorOf(Props.Create<Writer>(), "printer");


            // use ActorRef
            writer2.Tell("hello");
            xxx1.Tell("Ola");

            // use ActorSelection
            system.ActorSelection("/user/writer*").Tell("hi");

            Thread.Sleep(500);
            Console.WriteLine("press [enter] to continue");
            Console.ReadLine();

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

Die Ausgabe könnte so aussehen. Die Reihenfolge kann bei jedem Aufruf anders sein.

writer1 received hi
writer2 received hello
writer2 received hi
writer3 received hi
xxx1 received Ola

Bleibt noch die dritte Frage ungeklärt.

Wieviele Aktoren darf ich erzeugen?

Die Antwort darauf ist einfach: Bis Dein Speicher voll ist. Aktoren selbst belegen reativ wenig Speicher. Die genaue Zahl kann ich aktuell leider nicht nennen, aber es kursieren immer wieder Werte von 300-400 Byte pro Aktor. Zu einem Thread werden Aktoren erst, sobald eine Nachricht empfangen wird und dieser Vorgang erfolgt kontrolliert durch das Laufzeitsystem. Also: es darf durchaus einer mehr sein, das spielt überhaupt keine Rolle, selbst tausende von Aktoren sind kein Drama.

Das war's für heute. Morgen werden wir ein kleines Spiel gemeinsam programmieren.

Sonntag, 6. Dezember 2015

Akka.NET Adventskalender – Tür 6

Arten von Nachrichten

Glückwunsch! Du hast bis zum Ende unseres ersten (leider recht theoretischen) Sprints geschafft. Nächste Woche wird es deutlich praktischer zugehen – versprochen! Aber heute werden wir noch ein paar Muster rund um Nachrichten besprechen.

Bislang haben wir hauptsächlich String-Objekte an unsere Aktoren gesandt. Es mag Situationen geben, in denen das vollkommen ausreicht, aber die Versuchung ist sicher groß, Objekte als Nachrichten zu versenden. Doch – wie sollen wir solche Objekte benennen? Vaughn Vernon nennt in seinem Buch "Reactive Messaging Patterns with the Actor Model" drei verschiedene Muster, die auch erheblichen Einfluss auf die Benennung der Klassen haben. Ich werde nachfolgend englische Klassen-Namen verwenden, wenn eure Ubiquitäre Sprache (Ubiquitous language) deutsch ist, sollten selbstverständlich deutsche Namen hier Verwendung finden.

Kommandos (Command)

Wenn Du vor hast, einem Aktor ein Kommando zu erteilen, dann ist ein imperativ die grammatikalische Form, die Du in zwischenmenschlicher Kommunikation verwenden würdest. Das würde ich durch ein Verb in Gegenwart gefolgt von einem Substantiv ausdrücken. Im englischen würden dadurch Namen entstehen wie "PrintLine", "SendMail", "Speak" oder "SplitWhatever".

Das führt dann zu sehr ausdrucksstarken Code Zeilen wie

printer.Tell(new PrintLine( ... ));
notifier.Tell(new SendMail( ... ));
speaker.Tell(new Speak( ... ));
splitter.Tell(new SplitWhatever( ... ));

Die Intention hinter solchen Zeilen sind ohne Zweifel verständlich.

Ereignisse (Event)

Wenn ein Aktor seinen internen Zustand ändert und diese Tatsache anderen mitteilen möchte, dann informiert er über etwas das soeben passierte und nicht mehr zu ändern ist. Es fand ja bereits statt. Insofern ist ein Verb in der ersten Vergangenheit mit einem vorangestellten Substantiv eine gute Wahl. Auch hier wird jeder verstehen, was eben los war. Das führt dann zu Klassen-Namen wie "LinePrinted", "MailSent", "StatusUpdated" or "Spoke".

Auch hier sind die nachfolgenden Code-Zeilen für jeden nachvollziehbar.

someone.Tell(new StatusUpdated( ... ));
someone.Tell(new MailSent( ... ));

Dokumente (Document)

Das dritte Muster für die Namensgebung, die Vaughn Vernon nennt, sind Dokument Nachrichten. Sie bestehen lediglich aus einem Substantiv und deuten an, was vom Inhalt der Nachricht zu erwarten ist. In aller Regel sind solche Nachrichten die Antwort auf eine Anfrage bei einem Aktor.

Um die Probe auf's Exempel zu machen: sind die nachfolgenden Zeilen verständlich?

Receive<GetStatus>(_ => Sender.Tell(new Status( ... )));
Receive<Read>(_ => Sender.Tell(new SensorValue( ... )));

Wie organisiert man Nachrichten?

Gute Frage. Vermutlich wird jeder hier eigene Vorlieben haben. Im Akka.NET Universum wird man vorwiegend diese Vorgehen finden:
  • Nachrichten-Klassen sind verschachtelte Klassen, die innerhalb der Aktoren definiert werden, die diese Nachrichten senden oder empfangen. Wenn Du das machst, wirst Du sehr lesbare Klassen-Namen erhalten, denn die Namen bestehen aus dem Klassen-Namen des Aktors und durch einen Punkt getrennt, der Nachrichten-Klasse. Solche Nachrichten wird man jedoch kaum bei anderen Aktoren verwenden wollen und die Aktor-Klassen werden an Länge und damit Unübersichtlichkeit zunehmen.
  • Wie bisher auch: Jede Klasse kommt in eine Datei und wenn es zu viele werden packst Du die Nachrichten Klassen in einen eigenen Ordner, den Du wahlweise als namespace nutzt.
  • Eine Misch-Form aus beiden. Es liegt bei Dir.

In unserem heutigen Beispiel werden wir die erste Methode verwenden also Nachrichten innerhalb unseres Aktors definieren.

Unser erster Aktor soll zwei Typen von Nachrichten beantworten können: Die Anfrage der aktuellen Zeit und die Bitte, auf eine Zahl 4 zu addieren. Die Antwort wird dem Absender der Anfrage übergeben.

Erzeuge eine Konsolen Anwendung mit einem Actor System wie bisher auch und erstelle diesen Aktor:

using System;
using Akka.Actor;

namespace RequestReply
{
    public class Replyer : ReceiveActor
    {
        #region command messages
        public class ReadTime {}

        public class Add4
        {
            public int Number { get; private set; }

            public Add4 (int number)
            {
                Number = number;
            }
        }
        #endregion

        public Replyer()
        {
            Receive<ReadTime>(_ =>
                Sender.Tell(String.Format("{0:HH:mm:ss}", DateTime.Now))
            );

            Receive<Add4>(add4 =>
                Sender.Tell(add4.Number + 4)
            );
        }
    }
}

Dank der eingebetteten Klassen sind die Receive<>() Aufrufe direkt verständlich und einfach. Auf der Sende-Seite hingegen müssen längere Namen getippt werden (x.Tell(new Replyer.ReadTime())). Aber verständlich ist der Code allemal. Unsere Main Methode des Konsolen-Programms könnte dann so aussehen:

public static void Main(string[] args)
{
    var system = ActorSystem.Create("Reply");
    
    var replyer = system.ActorOf(Props.Create());
    
    // Ask() will reply with a task
    Task time = replyer.Ask(new Replyer.ReadTime());
    time.Wait();
    
    Console.WriteLine("Time is: {0}", time.Result);
    
    Task number = replyer.Ask(new Replyer.Add4(17));
    number.Wait();
    
    Console.WriteLine("Number is: {0}", number.Result);
    
    Console.WriteLine("Press [enter] to continue");
    Console.ReadLine();
    
    system.Shutdown();
    system.AwaitTermination();
}

Moment mal. Bisher war immer die Rede davon, einem Actor mittels Tell() eine Nachricht zu übermitteln, nun verwenden wir plötzlich Ask(). Und noch dazu verwenden wir ominöse Wait() Aufrufe, dabei hieß es doch, dass wir bei Akka.NET niemals mit Tasks arbeiten dürfen. Stimmt dann, wenn es um das Innenleben von Aktoren geht. Da solltest Du auf die Benutzung von Tasks auf jeden Fall verzichten. Auch die Benutzung von Ask() innerhalb Aktoren spricht für schlechtes Design und sollte eher vermieden werden. Das Motto lautet "Tell() – don't Ask()".
Aber immer dann, wenn von Akka-fremdem Code (wie unsere Kommandozeilen-Applikation, aber auch eine Web API wäre ein Beispiel) Aufrufe von Akka.NET Aktoren getätigt werden sollen, die eine Antwort geben. In der Akka.NET Dokumentation zu diesem Thema ist beim Rückgabewert von einer "Future" die Rede, das passendste .NET Mittel dafür ist ein Task. Solltest Du zufällig mit async Methoden arbeiten, wirst Du diese Entscheidung der Akka.NET Entwickler lieben!

So, die Grundlagen hättest Du erfolgreich überstanden. Leider sind wir an vielen Stellen nicht zu tief eingestiegen, ich hoffe dennoch, dass ich einen angenehmen Überblick über die grundlegendsten Dinge rund um Akka.NET vermitteln konnte. Morgen starten wir mit einem neuen Sprint, in dem wir uns mit einzelnen Aktoren und deren Zusammenspiel befassen werden. Ich hoffe, Du bist wieder dabei.

Samstag, 5. Dezember 2015

Akka.NET Adventskalender – Tür 5

Fortpflanzung auf Aktor-isch

Gestern haben wir uns angesehen, wie wir Nachrichten an einen Aktor senden und dort behandeln können. Ich hatte auch schon erwähnt, dass ein Aktor Kinder erzeugen kann, damit er Arbeit an diese abgeben kann. Wenn wir uns für diesen Weg entscheiden, müssen wir uns der Verantwortung bewusst werden, die wir damit eingehen. Unser Aktor ist verantwortlich für seine Kinder und muss sie kontrollieren, beaufsichtigen und eventuell beenden.

Insgesamt entsteht durch die Aktoren eine Hierarchie, die am besten mit einem Dateisystem vergleichbar ist. Direkt aus dem ActorSystem erzeugte Aktoren liegen im "/user" Pfad des Baumes, Kind-Aktoren wie bei verschachtelten Dateisystemen entsprechend an die Eltern-Aktoren angehängt.

Von einem Aktor aus angelegte Kinder werden mittels des sogenannten Context angelegt. Dem Context werden wir noch ein paar mal begegnen. Mich persönlich hat er anfangs eher verwirrt, dabei ist die Trennung zwischen Aktor und Context eigentlich ganz logisch. Der Aktor ist ein Objekt, welches das Verhalten und den Zustand repräsentiert. Der Context wird von der Laufzeitumgebung bereit gestellt und enthält infrastrukturelle Dinge wie die Verbindung zu Eltern, Kindern, dem Aktorsystem etc.

Neue Aktoren werden also so erzeugt:

// system == unser Aktor System
// Aktor unter /user anlegen
system.ActorOf( ... );

// wenn wir uns innerhalb eines Aktors befinden
// legen wir so ein Kind an
Context.ActorOf( ... );

Der Aktor Lebenszyklus

Wenn wir eine Hierarchie von Aktoren aufbauen, dann geschieht das im Sinne einer sinnvollen Arbeitsteilung. Jede Ebene operiert auf unterschiedlichem Niveau und delegiert und kontrolliert damit eventuell darunter liegende Aktoren. Wie im täglichen Leben haben höher angesiedelte Aktoren weniger Risiko aber mehr Verantwortung, weiter unten angesiedelte Aktoren ein deutlich höheres Risiko aber kaum Verantwortung. Wenn ein Aktor stirbt, wird er durch ein baugleiches Modell ersetzt. Klingt grausam, ist aber Realität in der Welt der Aktoren.

Damit wir in den diversen Lebenszyklen (starten, neu starten und anhalten) eingreifen können (möglicherweise dürfen wir bestimmte Daten nicht verlieren), bieten Aktoren vier verschiedene Methoden, die wir bereit stellen können. Sie werden durch die Laufzeitumgebung zum gegebenen Zeitpunkt aufgerufen.

 * protected override void PreStart()
 * protected override void PreRestart(Exception reason, object message)
 * protected override void PostRestart(Exception reason)
 * protected override void PostStop()

Etwas genauer steht es in der Akka.NET Dokumentation.

Um die Ergebnisse der diversen Methodenaufrufe einmal untersuchen zu können, möchte ich Dich bitten, ein kleines Konsolen-Projekt anzulegen und das nachfolgende Programm darin abzulegen. Anschließend werden air einen Aktor mit einem Kind anlegen, das leider öfter Exceptions erzeugt. Standardmäßig wird solch ein Aktor neu gestartet.

using System;
using Akka.Actor;
using System.Threading;

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

            var supervisor = system.ActorOf(
                Props.Create<Supervisor>(),
                // Props.Create<Supervisor>().WithSupervisorStrategy( ... ),
                "Supervisor");

            for (var i=0; i<500; i++)
                supervisor.Tell("message " + i);


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

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

Unser etwas zickiges Kind könnte so wie hier aussehen:

using System;
using Akka.Actor;

namespace AkkaSuperVision
{
    public class Child : ReceiveActor
    {
        private int nrMessagesHandled;

        public Child ()
        {
            nrMessagesHandled = 0;
            Receive<string>(s => HandleStringMessage(s));
        }

        protected override void PreStart()
        {
            Console.WriteLine("PreStart Actor '{0}'", Self.Path.Name);
        }

        protected override void PreRestart(Exception reason, object message)
        {
            Console.WriteLine("PreRestart Actor '{0}', reason: {1}, message: {2}",
                Self.Path.Name, reason.Message, message);
        }

        protected override void PostRestart(Exception reason)
        {
            Console.WriteLine("PostRestart Actor '{0}', reason: {1}",
                Self.Path.Name, reason.Message);
        }

        protected override void PostStop()
        {
            Console.WriteLine("PostStop Actor '{0}'", Self.Path.Name);
        }

        private void HandleStringMessage(string message)
        {
            Console.WriteLine("Received: '{0}'", message);

            if (++nrMessagesHandled >= 3)
                throw new InvalidMessageException("haha");
        }
    }
}

Und natürlich brauchen wir einen überwachenden Aktor. Er wird ebenfalls eine String-Nachricht erhalten und sie einfach an das (zickige) Kind weiterleiten.

using System;
using Akka.Actor;

namespace AkkaSuperVision
{
    public class Supervisor : ReceiveActor
    {
        public IActorRef child;

        public Supervisor()
        {
            child = Context.ActorOf(Props.Create<Child>(),"Child");

            Context.Watch(child);

            Receive<string>(s => HandleStringMessage(s));
        }

        private void HandleStringMessage(string message)
        {
            child.Tell(message);
        }

        // protected override SupervisorStrategy SupervisorStrategy()
        // {
        //     return new OneForOneStrategy(
        //         10,
        //         TimeSpan.FromSeconds(10),
        //         exception =>
        //         {
        //             return Directive.Restart;
        //         }
        //     );
        // }
    }
}

Wenn Du das Kommandozeilen Programm startest, wirst Du einen Kind-Aktor erleben, der die String-Nachrichten empfängt, aber bei jeder 3. Nachricht wie beabsichtigt, eine Exception auslöst. Aber er wird automatisch neu gestartet, ohne dass wir uns darum kümmern müssen. Dahinter steckt die sogenannte OneForOne Strategie. Wenn wir eine eigene Strategie hinterlegen möchten, können wir das in einem Aktor durch Überladen der SupervisorStrategy() Methode oder beim Erzeugen des Aktors erledigen. Damit können wir das Verhalten beim Sterben von Kind-Aktoren entsprechend anpassen.

Das Ergebnis beim Programmlauf könnte so aussehen:

PreStart Actor 'Child'
Received: 'message 0'
Received: 'message 1'
Received: 'message 2'
PreRestart Actor 'Child', reason: haha, message: message 2
PostRestart Actor 'Child', reason: haha

...

Received: 'message 497'
PreRestart Actor 'Child', reason: haha, message: message 497
PostRestart Actor 'Child', reason: haha
Received: 'message 498'
Received: 'message 499'
Press [enter] to continue...

"Stimmt nicht ganz" wirst Du gleich sagen. Hmmm, ok, ein wenig geschummelt habe ich. Standardgemäß werden alle Exceptions mitgeloggt und die Console ist das normalerweise eingestellte  Ziel für die Log Ausgaben. Um es abzuschalten, könntest Du deinem Projekt die nachfolgende Konfigurations-Datei beisteuern. Akka.NET verwendet eine eigene Syntax, die innerhalb der Tags "akka" und "hocon" eingebaut sind. Hier kann z.B. das Logging temporär deaktiviert werden. Für praktische Projekte ist das nicht empfehlenswert, aber für unsere Experimente durchaus legitim

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <configSections>
    <section name="akka" type="Akka.Configuration.Hocon.AkkaConfigurationSection, Akka" />
  </configSections>

  <akka>
    <hocon>
      <![CDATA[
        akka {
            loggers = ["Akka.Event.StandardOutLogger"]
         log-config-on-start = off
           stdout-loglevel = OFF
           loglevel = DEBUG
           actor {
               debug {  
                #  receive = on 
                #   autoreceive = on
                #  lifecycle = on
                #   event-stream = on
                # unhandled = on
               }
           }
      ]]>
    </hocon>
  </akka>
</configuration>

Die kompletten Details zur Konfiguration findest Du in der Akka.NET Dokumentation.

Zeit für Experimente. Was passiert, wenn Du die SupervisorStrategy() Methode oben auskommentierst? Kannst Du Dir das Verhalten erklären? Wie müsstest Du den Code verändern, um das erwartete Verhalten zu erreichen? Und zum Schluss könntest Du mit der WithSuperVisorStrategy() Methode experimentieren – das wäre zumindest der kürzeste Weg!

Morgen werden wir uns einiges rund um Nachrichten ansehen

Freitag, 4. Dezember 2015

Akka.NET Adventskalender – Tür 4

Verschiedene Aktor Typen

Gestern haben wir unseren ersten Aktor erzeugt und ihn von der Klasse UntypedActor abgeleitet. Es gibt aber noch mehr Basisklassen, die für die Erzeugung von Aktoren in Frage kommen. Heute werden wir uns die drei wichtigsten und grundlegendsten ansehen.

Der einfachste

Der UntypedActor, den wir gestern kennengelernt haben, ist der einfachste Typ von Aktor, den Du erstellen kannst. Letztlich ist es Geschmacksfrage, welcher Typ von Aktor von Dir als Basis verwendet wird. Also zeige ich Dir die drei wichtigsten und Du kannst dann von Fall zu Fall entscheiden, welcher Dir für welchen Zweck am besten gefällt.

Beim UntypedActor musst Du lediglich eine Methode bereit stellen, die sämtliche Nachrichten erhält, die an den Aktor gesandt werden.

protected override void OnReceive(object message)
{
    // handle all messages received
}

Du entscheidest, ob Du eine Nachricht annimmst und was Du mit ihr anfängst. Typischerweise wird eine mehr oder weniger lange if-else Kaskade innerhalb dieser Methode stehen. Allerdings kann dieser Aktor bereits verschiedene Verhalten bekommen. Mehr dazu gleich.

Etwas mehr C# bitte

Vermutlich war Dir das obige Beispiel zu wenig an die Fähigkeiten – insbesondere die Typsicherheit von C# angelehnt. Wenn Du einen typsicheren Aktor haben möchtest (dieser kann dann jedoch keine Verhalten bekommen) dann kannst Du Deinen Aktor von TypedActor ableiten. Dieser wird zuätzlich mit jeder Menge IHandle<> Interface Deklarationen verziert und erhält dem Interface entsprechend eine Menge Handle() Methoden, die sich jeweils um die Bearbeitung eines Typs von Nachricht kümmern. Unser HelloWorld-Aktor sähe dann so aus:

using System;
using Akka.Actor;

namespace AkkaHelloWorld
{
    public class HelloTyped : TypedActor, IHandle<string>
    {
        public void Handle(string message)
        {
            Console.WriteLine("Received: '{0}'", message);
        }
    }
}

Auf den ersten Blick sehen solche Aktoren sehr vernünftig aus, vor Allem wenn man Typsicherheit im Hinterkopf hat. Jedoch können solche Aktoren kein Verhalten bekommen, da das verschiedene Reaktionen auf gleiche Nachrichten-Typen erfordert.

Und noch einer

Mein persönlicher Favorit (nein, nein beeinflussen will ich Dich hiermit nicht) ist der ReceiveActor. Es ist eine Mixtur aus Typsicherheit und Flexibilität. Und das häufig gepriesene Verhalten kann er auch. Aber schauen wir uns erst einmal an, wie unser einfacher Hello-Aktor aussehen würde:

using System;
using Akka.Actor;

namespace AkkaHelloWorld
{
    public class HelloReceive : ReceiveActor
    {
        public HelloReceive()
        {
            Receive<string>(s => HandleStringMessage(s));
        }

        public void HandleStringMessage(string message)
        {
            Console.WriteLine("Received: '{0}'", message);
        }
    }
}


Mit diesem Wissen ausgestattet kannst Du künftig entscheiden, wie Du Deine Aktoren baust.

Wie war das jetzt mit dem Verhalten?

Wenn wir an uns Menschen denken, so sind wir durchaus in unterschiedlicher Verfassung. Manchmal sind wir ärgerlich, manchmal freundlich oder unterscheiden zwischen anderen Gemütszuständen. Je nach unserem Zustand reagieren wir auf gleiche Ereignisse möglicherweise unterschiedlich. Genau das ist es, wenn wir bei einem Aktor von Verhalten (behavior) sprechen.  Typischerweise nutzt man die Verhalten, um einen Zustandsautomaten zu modellieren.
Um zu verstehen, was ich meine, bauen wir uns einen einfachen Aktor, der zwischen Wahrheit erzählen und Lügen hin- und herschaltet. Wir fragen ihn nach der Zeit und erhalten gelegentlich eine wahre und gelegentlich eine gelogene Antwort.

using System;
using Akka.Actor;

namespace AkkaHelloWorld
{
    public class TimeReader : ReceiveActor
    {
        private Random random;

        public TimeReader()
        {
            random = new Random();

            Become(TruthTelling);
        }

        #region behaviors
        private void TruthTelling()
        {
            Receive<string>(s => TellCorrectTime(s));
        }

        private void Lying()
        {
            Receive<string>(s => TellAnyTime(s));
        }
        #endregion

        #region message handlers
        private void TellCorrectTime(string _)
        {
            Console.WriteLine("Current time is: {0:hh:mm}-really",
                DateTime.Now
            );
            if (random.Next(100) < 50)
                Become(Lying);
        }

        private void TellAnyTime(string _)
        {
            Console.WriteLine("Current time is: 11:92-do you believe it?");
            Become(TruthTelling);
        }
        #endregion
    }
}


Ein mögliches Resultat könnte so aussehen:

Current time is: 10:26-really
Current time is: 11:92-do you believe it?
Current time is: 10:26-really
Current time is: 11:92-do you believe it?
Current time is: 10:26-really
Current time is: 10:26-really

Wenn Du in der Akka.NET Dokumentation zu diesem Thema nachschlägst, wirst Du noch weitere Informationen erhalten. Zum Beispiel auch zum "stashing", also dem Aufschieben von Nachrichten bis ein Aktor wieder im richtigen Zustand ist.

Aktoren können aber noch viel mehr – das sehen wir uns morgen an.

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.

Mittwoch, 2. Dezember 2015

Akka.NET Adventskalender – Tür 2

Was ist eigentlich ein Aktor?

So langsam wird es Zeit, dass wir herausfinden, was eigentlich ein Aktor ist. Man kann einen Aktor mit einem Objekt aus der Objektorientierung vergleichen.

  • Ein Aktor speichert einen Zustand (in Form von Feldern und Eigenschaften eines CLR Objektes, das wir bequem in C# anlegen). Der Zustand ist für die Außenwelt gar nicht sichtbar. Das ist wichtig, denn das Aktor Modell versucht, den gefürchteten verteilten Zustand (shared state) mit all seinen Problemen (Locking, Deadlocks, inkonsistente Zustände, etc.) dadurch gar nicht erst aufkommen zu lassen. Nur der Aktor selbst hat Zugriff auf seinen Zustand!
  • Jeder Aktor empfängt Nachrichten, die an ihn gesandt werden und führt Aktionen als Folge davon aus. Alle eintreffenden Nachrichten werden in einer als FIFO organisierten Mailbox gespeichert und nacheinander abgearbeitet. Akka.NET sorgt dafür, dass jeder Aktor immer nur eine Nachricht zu jedem Zeitpunkt verarbeitet. Das bedeutet dann leider auch, dass wir typischerweise keine weiteren parallelen Aktionen (z.B. in Form von Tasks) innerhalb eines Aktors ausführen. Das ist einzig die Aufgabe der Laufzeit-Umgebung von Akka.NET.
  • Ein Aktor kann wahlweise verschiedene Verhalten annehmen. So wie wir Menschen mal gut oder schlecht gelaunt sind, kann ein Aktor Zustände (im Sinne eines Zustandsautomaten) annehmen. Technisch gesehen reagiert der Aktor je nach Zustand anders auf Nachrichten, die ihm zugestellt werden.
  • Aktoren dürfen Kind-Aktoren erzeugen, kontrollieren und wieder beenden. Kind-Aktoren und deren korrekte Beaufsichtigung sind der Schlüssel für belastbare Systeme. Wie im täglichen leben: Eltern haften für ihre Kinder. Typischerweise baut man damit hierarchisch gestaltete Verantwortungs-Bäume auf, die von oben nach unten Aufgaben und vielleicht auch Verantwortung deligieren. Je weiter oben die Aktoren stehen, desto mehr Verantwortung und weniger eigenes Risiko besitzen die Aktoren. Die Aktoren am Ende des Baums führen nur noch einfache Aufgaben aus, müssen aber enventuell riskante Tätigkeiten ausführen, die tödlich enden können.

Wo leben die Aktoren?

Aktoren leben in Aktor Systemen. Ein Rechner kann beliebig viele Aktor Systeme beherbergen. Die einzelnen Systeme sind aber zunächst voneinander unabhängig, können aber durchaus miteinander arbeiten. Durch entsprechende Konfiguration gesteuert können Aktoren auch auf anderen Systemen erzeugt werden (location transparent) und dort ihre Arbeit verrichten. Als Entwickler spürt man zunächst keinen Unterschied. Um das zu ermöglichen, werden wir sogenannte Aktor-Referenzen (ActorRef) kennenlernen, die sich wie ein Stellvertreter (Proxy) zum tatsächlichen Aktor Verhalten. Wir sprechen niemals mit dem Aktor direkt sondern immer über die Referenz. Der einzige Unterschied, der möglicherweise auftreten kann, sind Fehler, die z.B. durch Netzwerk-Probleme verursacht werden. Dafür muss man entsprechende Vorkehrungen treffen. Alle Beispiele, die wir in den folgenden Tagen bearbeiten werden, laufen allerdings immer auf dem lokalen Rechner.

Morgen werden wir unseren ersten Aktor erstellen.

Dienstag, 1. Dezember 2015

Akka.NET Adventskalender – Tür 1

Worum geht es?

An den nächsten 24 Tagen (heute mitgezählt) kannst Du täglich einen Artikel über Akka.NET hier lesen. Falls Du Dich fragst, was dahinter steckt und wozu man Akka.NET benutzen kann, dann könnte diese Serie für Dich interessant sein. Dich erwarten ein paar Grundlagen, diverse Muster und viele Code Beispiele, die hoffentlich zum Verständnis beitragen. Wenn ich es schaffe, Dein Interesse bis zum Schluss zu behalten und Du selbst mit Akka.NET programmierst, dann habe ich mein Ziel erreicht.

Warum also Akka.NET einsetzen?

Akka.NET baut auf dem Aktor Modell auf (Alternative Beschreibung), das eine sehr elegante Lösung für die diversen Probleme bietet, die sich bei nebenläufigen, parallelen oder verteilten Systemen ergeben. Die zum Teil sehr umfangreichen Grundlagen möchte ich mir an dieser Stelle ersparen, aber wir alle haben vermutlich schon diverse Erfahrungen mit Threads, Prozessen und verteilten Systemen gemacht und so mache Lektion schmerzhaft erfahren müssen. Hinzu kommt leider die Tatsache, dass moderne CPUs unserer Rechner nicht mehr mit zunehmenden Taktfrequenzen aufwarten, sondern eher die Anzahl der Kerne, die zur Ausführung unserer Programme bereitstehen, zunehmen. Während wir vor 10 Jahren typischerweise einen Kern hatten, sind heute selbst in mobilen Computern 8 Kerne denkbar. Insofern müssen wir uns mit Parallel-Programmierung anfreunden, wenn wir unsere Hardware möglichst gut ausnutzen möchten.

Setzt man Werkzeuge wie Akka.NET ein, dann hält sich der Aufwand in puncto "Lernkurve" in Grenzen, sofern man mit Grundlagen der Objektorientierten Programmierung vertraut ist. Erste Ergebnisse sind sehr schnell sichtbar, große Dinge im Rahmen des Machbaren und Skalierung innerhalb einer Maschine (scale-in) ode die Skalierung auf zusätzliche Maschinen (scale-out) aus Sicht des Anwendungs-Entwicklers fast ohne Unterschied.

Wird das Aktor Modell überleben?

Gute Frage. Leider ist meine Glaskugel heute etwas trüb...  Was meiner Meinung nach großes Vertrauen schafft, ist die Tatsache, dass viele Leute auf dieses Modell setzen. Damit kann man zumindest heute diese Technologie als zukunftsträchtig einstufen. Programmiersprachen wie Erlang oder Elixir und viele andere mehr bieten native Unterstützung für Aktoren. So gut wie jede Plattform besitzt zahlreiche Implementierungen davon, meist mehr oder weniger an das in Scala geschriebene Akka angelehnt. Und in der .NET Welt gibt es zwei sehr prominente Kandidaten. Akka.NET ist vom Einstieg her relativ leicht beherrschbar und Microsoft bietet mit Orleans eine interessante quelloffene Alternative sowie mit Service Fabric die kommerzielle Variante, die innerhalb Azure bereits seit Jahren Verwendung findet. Grund genug also, sich mit dem Thema auseinander zu setzen.

Wir sind Reaktiv!

Reaktiv zu programmieren ist vermutlich inzwischen zu einem Modewort geworden. Aber wenn wir Akka.NET einsetzen, dann betreten wir die Welt der reaktiven Programmierung. Dahinter steckt ein Paradigma, welches durch das Reactive Manifesto beschrieben ist. Reaktive Systeme sind (Kurzform)

  • Responsiv und beantworten Anfragen innerhalb gegebener Zeit. Dahinter steckt vor allem der Gedanke, Probleme frühzeitig zu erkennen und geeignet zu reagieren.
  • Belastbar und reagieren selbst unter Last noch vernünftig.
  • Elastisch durch Hinzu- oder Wegnahme bestimmter Resourcen
  • Nachrichtengesteuert. Aktoren senden sich Nachrichten, um sich gegenseitig über bestimmte Zustände oder Ereignisse zu informieren oder Dinge abzufragen bzw. auszulösen.

Was erwartet Dich an den nächsten Tagen?

Die nachfolgenden Artikel sind eine Mixtur aus etwas Theorie, ein paar Grundlagen sowie Entwurfsmuster, die das Leben eines Reaktiven Entwicklers leichter machen. Der Schwerpunkt liegt auf praktischen Experimenten. Wir werden gemeinsam an Beispielen allgemeine Problemstellungen der Reaktiven Programmierung erarbeiten. Allerdings werde ich in dieser Artikel-Serie nicht immer sehr weit in die Details eintauchen. Es gibt ein wunderbares Tutorial, mit dem ich nicht konkurrieren kann und das ich auch nicht kopieren möchte. Du kannst es gerne gleichzeitig zu diesen Artikeln oder einfach anschließend absolvieren – es lohnt sich auf jeden Fall!

Die nachfolgenden Artikel sind als 4 Sprints organisiert, die jeweils an einem Sonntag (bzw. Heilig Abend) enden. Jeder Sprint steht unter jeweils einem eigenen Motto und behandelt die dazu passenden Grundlagen und Muster. Wir werden mit dem Beschnuppern von Akka.NET beginnen, uns dann den Einsatz einzelner Aktoren sowie Mengen an Aktoren ansehen und zum Schluss mit Publish/Subscribe Experimente anstellen.

Was muss ich vorbereiten?

Alle Code Beispiele sind in C# geschrieben (F# wäre auch möglich, mir fehlt jedoch das Know-how dazu). Du brauchst passende Entwicklungswerkzeuge. Je nach Deiner präferierten Plattform kannst Du mit MonoDevelop oder Visual Studio alle Beispiele nachvollziehen. Für größere Beispiele werden rechtzeitig git Repositories bereit gestellt sein.

Also – wenn ich Dich nicht abgeschreckt habe – bereite Deinen Rechner vor und morgen geht es los!