Mittwoch , 17 Juli 2019

Azure Service Fabric: Stateless Virtual Actors

Azure Service Fabric (Preview) - Virtual ActorsVergangene Woche berichtete ich bereits über die neue Azure Service Fabric Preview der Microsoft Azure Plattform. Diese soll Entwicklern dabei helfen, schneller und unkomplizierter Anwendungen für die Cloud bauen zu können.
Dabei können verschiedene Programmiermodelle eingesetzt werden, welche ich in diesem, sowie den kommenden Blog Posts, näher vorstellen möchte.
Starten möchte ich mit den Stateless Virtual Actors…

Distributed Virtual Actors

Das Modell der »Distributed Virtual Actors« ist nicht völlig neu.
Es entstand im Rahmen des Orleans Projektes der Microsoft Research.

Project "Orleans" invented the Virtual Actor abstraction, which provides a straightforward approach to building distributed interactive applications, without the need to learn complex programming patterns for handling concurrency, fault tolerance, and resource management. Orleans applications scale-up automatically and are meant to be deployed in the cloud. It has been used heavily by a number of high-scale cloud services at Microsoft, starting with cloud services for the Halo franchise running in production in Microsoft Azure since 2011. It was made available as open source in January 2015.

Wer mehr über die Orleans Virtual Actors wissen möchte, sollte sich das Research Paper durchlesen.

Durch den Azure Service Fabric Dienst, wird dieses Modell mit verschiedenen Funktionalitäten angereichert, wie beispielsweise:

  • Application Lifecycle Management
  • Beeinflussung der Microservice-Platzierung im Cluster
  • Ressourcenverwaltung
  • Sicherheitsfunktionen
  • Dienstüberwachung

Somit stellt dieses Programmiermodell die höchste Ebene in den Schichten der Azure Service Fabric API dar.

Azure Service Fabric - Runtime API Layers

Wann sollte man Virtual Actors verwenden?

Virtual Actors eignen sich besonders, wenn die Applikation aus mehreren unabhängigen Einheiten besteht, welche ihre eigene Geschäftslogik und Statusinformationen enthalten.

Virtual Actors sind isolierte Single-Thread-Komponenten.
Sie interagieren mit dem Rest des Systems, was auch andere Virtual Actors sein können, indem sie asynchrone Nachrichten austauschen (Request-Response-Pattern).
Sie existieren immer, müssen also weder explizit erzeugt noch entfernt werden.

Stateless Virtual Actors

Als Beispiel für einen »Stateless Virtual Actor« möchte ich einen einfachen Taschenrechner hernehmen.

Nach man also, wie in meinem vergangenen Blog Post beschrieben, das Azure Service Fabric SDK installiert hat, kann in Visual Studio ein neues »Stateless Actor Service« Projekt erzeugt werden.

Visual Studio: Stateless Actor Vorlage

Stateless Actor ProjectsGenauer gesagt, werden hierbei 4 Projekte in eine Solution gepackt.

Bei meinem Beispiel wären das:

  • StatelessCalculatorActor
    Dieses Projekt enthält die Implementierung und Konfiguration des Actors
  • StatelessCalculatorActor.Client
    Dieses Projekt enthält eine Konsolenapplikation, um den Actor zu testen
  • StatelessCalculatorActor.Interfaces
    Dieses Projekt enthält die Schnittstellenbeschreibungen, die sich die Actors und Clients teilen.
  • StatelessCalculatorActorApplication
    Dieses Projekt enthält das »Applicaton Manifest«, sowie die PowerShell-Skripte, die benötigt werden, um den Dienst paketieren und installieren bzw. deinstallieren zu können
  • Das Calculator-Projekt habe ich nachträglich angefügt und enthält eine WPF-Applikation, die den Actor verwendet.

Implementierung des Taschenrechner-Actors

Als Erstes benötigt unser Taschenrechner-Actor eine Schnittstellenbeschreibung im StatelessCalculatorActor.Interfaces Projekt.
Diese beinhaltet Methoden für die 4 Grundrechenarten unseres Taschenrechners.

public interface IStatelessCalculatorActor : IActor
{
  Task<double> Add(double x, double y);
  Task<double> Substract(double x, double y);
  Task<double> Multiply(double x, double y);
  Task<double> Divide(double x, double y);
}

Als Zweites benötigen wir die Implementierung des Taschenrechner-Actors im StatelessCalculatorActor Projekt.
Diese leitet von der Basis-Klasse Actor ab und implementiert unsere Schnittstelle.
Außerdem können wir die mitgelieferte ActorEventSource Klasse nutzen, um Statusinformationen an die Service Fabric Runtime zu melden. 

public class StatelessCalculatorActor : Actor, IStatelessCalculatorActor
{
  public async Task<double> Add(double x, double y)
  {
    ActorEventSource.Current.ActorMessage(this, "Add {0} and {1}", x, y);
    return await Task.FromResult(x + y);
  }

  public async Task<double> Divide(double x, double y)
  {
    ActorEventSource.Current.ActorMessage(this, "Divide {0} by {1}", x, y);
    return await Task.FromResult(x / y);
  }

  public async Task<double> Multiply(double x, double y)
  {
    ActorEventSource.Current.ActorMessage(this, "Multiply {0} and {1}", x, y);
    return await Task.FromResult(x * y);
  }

  public async Task<double> Substract(double x, double y)
  {
    ActorEventSource.Current.ActorMessage(this, "Substract {1} from {0}", x, y);
    return await Task.FromResult(x - y);
  }
}

Zuletzt müssen nur noch die Clients implementiert werden.
Auch diese referenzieren das Schnittstellen-Projekt, sowie die Service Fabric NuGet-Pakete.

Um den Actor ansprechen zu können wird ein Proxy-Objekt benötigt.
Dieses kann über die ActorProxy-Factoryklasse erzeugt werden.
Dabei verwende ich eine zufällige Actor-ID, die mich auf einen der verfügbaren Actor-Instanzen verweist, sowie den Pfad zur übergeordneten Dienst-Applikation.
Auf diese Konzepte werde ich in einem späteren Blog Post näher eingehen.

public static void Main(string[] args)
{
  var proxy = ActorProxy.Create<IStatelessCalculatorActor>(
    ActorId.NewId(), "fabric:/StatelessCalculatorActorApplication");

  Console.WriteLine(
    "[Actor {0}]: 1 + 5 = {1}",
    proxy.GetActorId(),
    proxy.Add(1, 5).Result);

  Console.WriteLine(
    "[Actor {0}]: 11 - 6 = {1}",
    proxy.GetActorId(),
    proxy.Substract(11, 6).Result);

  Console.WriteLine(
    "[Actor {0}]: 3 * 5 = {1}",
    proxy.GetActorId(),
    proxy.Multiply(3, 5).Result);

  Console.WriteLine(
    "[Actor {0}]: 9 / 2 = {1}",
    proxy.GetActorId(),
    proxy.Divide(9, 2).Result);
}

Testen & Debuggen

Anschließend kann die Solution – wie gewohnt – getested und debugged werden.

Als kleine Besonderheit werden beim Debuggen in Visual Studio diverse Statusmeldungen ausgegeben.

Stateless Calculator Actor

Zusätzlich wird mit dem Azure Service Fabric SDK, auch ein kleines Tool, der Service Fabric Explorer, installiert, mit dem diverse Informationen des lokalen Clusters einsehbar sind.

Service Fabric Explorer

Quellcode des Beispiels

Das komplette Taschenrechner-Beispiel habe ich hier zur Verfügung gestellt.