Freitag, 11. Dezember 2015

Akka.NET Adventskalender – Tag 11

Strömende Quelle

Diese Woche befassen wir uns ja durchgehend mit einzeln auftretenden Aktoren. Wenn Du gerne mit Terminals (im deutschen Windows Eingabeaufforderung genannt) arbeitest, dann kennst Du bestimmt die Möglichkeiten, die eine Pipe (das | Symbol) besitzt. Oder wenn Du C# programmierst, dann schätzt Du vermutlich die vielfältigen Möglichkeiten, die sich mit Linq bieten oder Du hast bereits Erfahrungen mit Reactive Extensions gemacht. Man schaltet einfach gesprochen mehrere für sich betrachtet einfache Operationen zu einer komplexeren großen Operation hintereinander.

Dazu entwickeln wir heute ein sehr einfaches Stream-Protokoll. Es wird viele Kompromisse und Einschränkungen haben, aber zum Kennenlernen der Möglichkeiten reicht das vollkommen aus.

Unsere Stream Implementierung besitzt dabei ein primitives Protokoll, das aus zwei verschiedenen Nachrichten-Typen besteht:
  • String – eine Text Nachricht, die zu einem Aktor gesendet wird. Man erwartet vom Ziel-Aktor, dass er den Text entgegennimmt und entsprechend seiner Bestimmung verarbeitet.
  • End – ein End Objekt deutet das Ende des Streams an. Danach dürfen keine weiteren String-Nachrichten mehr folgen.
Grundsätzlich sind drei verschiedene Arten von Aktoren denkbar:
  • Quell Aktoren, die keinen Eingang besitzen, aber an deren Ausgang String- und End-Nachrichten bereit stellen. Anwendungen sind Laden oder Eingabe
  • Durchlass Aktoren, die Ein- und Ausgang besitzen. Dieser Typ Aktor kann wahlweise puffern bis der Stream zu Ende ist bzw. erhaltene Nachrichten sofort oder blockweise weiter reichen.
  • Ziel Aktoren, die nur einen Eingang haben, Nachrichten entgegen nehmen und final verarbeiten. Anwendungen solcher Aktoren sind Speicherung oder Ausgabe.
Für unsere Lernzwecke verarbeiten wir keine Streams parallel, fügen keine Streams zusammen oder teilen sie auf. Und wir behandeln keine Fehler, die über das Neustart-Verhalten von Akka.NET hinausgehen. Da solch ein Verhalten in der Praxis mit Datenverlusten einhergeht, ist es nicht ratsam, das im produktiven Umfeld so ebenfalls zu tun.

Ein einfacher Durchlass-Aktor könnte so aussehen:

class SourceActor : ReceiveActor
{
    SourceActor(IActorRef next)
    {
        // simple passthru without change 
        Receive<string>(s => next.Tell(s));
        Receive<End>(end => next.Tell(end));
    }
}

Ein Quell Aktor wird typischerweise keine Receive<> Anweisungen für unser Protokoll besitzen, sondern irgendwoher Daten erhalten, die er dann an den nächsten Aktor der Kette weiterleitet.

Und ein Ziel-Aktor wird keinen nächsten Aktor als Konstruktor-Argument besitzen aber die Daten gemäß dem Protokoll verarbeiten.

Wie das funktioniert und was man damit anfangen kann, sehen wir morgen.

Keine Kommentare:

Kommentar veröffentlichen