Rechenleistung auf Befehl – WCF Cluster

Hier hatte ich mir schon mal ein paar Gedanken zu Clustern und Rechenleistung gemacht. Jetzt hab ich die Idee ein bisschen ausgebaut und einen ersten Entwurf mit WCF umgesetzt.

1. Die Architektur

Ein Cluster baut sich aus drei Teilen auf, dem Client (fordert die Abarbeitung einer Aufgabe an), dem Distributor (verteilt die Aufgaben) und dem Calculator (arbeitet die Aufgaben tatsächlich ab). Dabei kann es beliebig viele Clients und Calcultors geben, aber (erst mal) nur einen Distributor.

Der Reihe nach passiert dann folgendes:

  1. Der Distributor startet
  2. Ein Calculator startet und verbindet sich mit dem Distributor
  3. Ein Client startet und übergibt dem Distributor eine Aufgabenstellung
  4. Der Distributor leitet die an den Calculator weiter
  5. Der Calculator erledigt die Aufgabe und schickt das Ergebnis direkt an den Client zurück

Aus Performancegründen schickt der Calculator sein Ergebnis direkt zum Client zurück.

cluster

2. Die Implementierung

Die Implementierung basiert auf WCF. Jeder der drei Teilnehmer ist ein eigenständiger WCF-Service. Das ist nötig, denn Streaming (was ich hier für nötig halte, weil man ja auch mal ein paar größere Aufgaben erledigen will) läuft über einen eigenen WCF-Transport Channel, wir können also keine Callbacks verwenden. Der Vorteil ist, dass der Client einfach seine Service-Adresse als string an den Distributor und der an den Calculator übermitteln kann und dann die direkte Verbindung von Calculator zu Client ganz einfach hergestellt werden kann. Ok, ich hab gesagt ich hab einen Entwurf, also Code, hier ist er:

 1: ComputingPowerConsumer powerConsumer = new ComputingPowerConsumer();
 2: powerConsumer.CommandExecuted += (operationID, output) =>
 3: {
 4:     Console.WriteLine(output);
 5: };
 6:
 7: powerConsumer.ExecuteCommand(SayHelloCommand.SayHelloCommandID, Guid.NewGuid().ToString(), null);
 8: Console.ReadLine();

Das ist alles, was nötig ist um einen Command/Aufgabe ans Cluster abzusetzen.

  1. Einen neuen PowerConsumer erstellen (Helper Klasse von mir)
  2. Auf das Event registrieren, wenn die Aufgabe fertig ist
  3. Aufgabe abschicken, alles was man braucht ist
    1. Eine Command ID (später mehr dazu)
    2. Eine Operation ID (wird einfach überall durchgeschleift und kommt am Ende im Event wieder rein, damit man weiß zu welchem Aufruf man gerade was empfängt)
    3. Ein object als Parameter, das dem Command zur Ausführung übergeben wird

Ja und was ist nun dieser Command? Eigentlich nur eine Klasse, die von IComputingPowerCommand (noch ein Baustein von mir) ableitet:

 1: public class SayHelloCommand : IComputingPowerCommand
 2: {
 3:     public const string SayHelloCommandID = "{D89930D2-06CD-4B9B-B09A-AE54CF7DD818}";
 4:
 5:     public string CommandID
 6:     {
 7:         get
 8:         {
 9:             return SayHelloCommandID;
 10:         }
 11:     }
 12:
 13:     public object Execute(object input)
 14:     {
 15:         return "Hello World!";
 16:     }
 17: }

Jeder Command hat eine Execute Methode, die das object vom Clientaufruf reingereicht bekommt und eine eindeutige ID, die der Client im Aufruf mitgibt. Die DLL in der sich der Command befindet muss in jedem bin-Verzeichnis von jedem Calculator sein, und das wars auch schon!

Das einzige was noch fehlt ist die Angabe der Distributor-Adresse in der App-Config und fertig ist der Cluster Client:

 1: <system.serviceModel>
 2:     <client>
 3:         <endpoint name="SRSIPowerConsumerDistributor1"
 4:                     address="net.tcp://winwf7:8002/DistributorService"
 5:                     binding="netTcpBinding"
 6:                     bindingConfiguration="streamedTcp"
 7:                     contract="Selen.Clustering.PowerDistributor.IPowerConsumerDistributor"/>
 8:     </client>
 9:     <bindings>
 10:         <netTcpBinding>
 11:             <binding name="streamedTcp" transferMode="Streamed">
 12:                 <reliableSession enabled="false" ordered="false"/>
 13:                 <security mode="None"/>
 14:             </binding>
 15:         </netTcpBinding>
 16:     </bindings>
 17:     <behaviors>
 18:         <serviceBehaviors>
 19:             <behavior name="IComputingPowerDistributorBehaviour">
 20:                 <serviceMetadata/>
 21:                 <serviceDebug includeExceptionDetailInFaults="true"/>
 22:             </behavior>
 23:         </serviceBehaviors>
 24:     </behaviors>
 25: </system.serviceModel>

Wer sich hier die Config-Datei mal genau anschaut sieht sicherlich den komischen Namen vom Distributor-Endpoint (“SRSIPowerConsumerDistributor1”), das hängt damit zusammen, dass die Verbindung zum Distributor intern über ein weiteres kleines WCF-Framework von mir aufgebaut wird, das es ermöglicht mehrere Endpoints anzugeben zwischen denen dann im Fall eines Fehlers automatisch umgeschaltet wird um eine hohe Ausfallsicherheit zu gewährleisten.

Leider bin ich noch nicht ganz so weit das ganze Framework zu veröffentlichen, in ein paar Tagen, gibt´s dann aber das Selen.Clusteing Framework 1.0 und noch einen Artikel zum Thema WCF und Ausfallsicherheit.

Images in Silverlight – Effects und Reflection

Seit heute steht meine neue Silverlight-Webseite online. Das Design ist ziemlich cool und hat auch ein bisschen Arbeit gemacht. Dabei sind ein paar Bilder, denen ich Effekte verpasst habe, hier mal zwei schöne Möglichkeiten das UI einer Silverlight-Webseite mit Images zu verschönern:

1. Einfache Effects definieren

Über die Effect Eigenschaft (die man natürlich auch auf anderen UI Elementen genauso definieren kann) ist es ganz einfach möglich einem Image zum Beispiel einen Schatten anzuhängen. Eine schöne dezente Konfiguration, die ich oft verwendet habe ist die:

 1: <Image Source="rss.png" Height="35" Margin="2">
 2:     <Image.Effect>
 3:         <DropShadowEffect Color="Gray" Opacity="0.6"/>
 4:     </Image.Effect>
 5: </Image>

Ohne Effect:

without

Mit Effect:

shadow

Je nach Anwendungsfall (und Größe vom Bild) macht es natürlich Sinn auch die anderen Eigenschaften von DropShadowEffect noch zu setzen (z.B. ShadowDepth um den Schatten weiter nach unten zu setzten usw.), ist aber in den meisten Fällen denke ich gar nicht nötig.

Der andere Effect, den Silverlight noch bereitstellt, ist der BlurEffect. Den finde ich allerdings nicht so hübsch und nur selten passend und hab ihn deshalb auch nicht eingesetzt. Man kann hier nur den Radius der Verwischung festlegen, für das gleiche Beispiel sähe das dann so aus:

 1: <Image Source="rss.png" Height="35" Margin="2">
 2:     <Image.Effect>
 3:         <BlurEffect Radius="5"/>
 4:     </Image.Effect>
 5: </Image>

Ohne Effect:

without

Mit Effect:

blur

2. Images spiegeln

Bis jetzt war der XAML Code ja eigentlich recht simpel, beim Spiegeln müssen wir uns jetzt schon ein paar mehr Gedanken machen. In WPF gibts für solche Fälle den VisualBrush, leider nicht in Silverlight, deshalb sind wir hier gezwungen zwei Bilder anzulegen, ein Originalbild und ein gespiegeltes, die wir dann zusammen in ein ItemsControl (z.B. StackPanel) packen:

 1: <StackPanel>
 2:     <Image Source="nn.png" Width="150"/>
 3:     <Image Source="nn.png" Width="150">
 4:         <Image.RenderTransform>
 5:             <TransformGroup>
 6:                 <ScaleTransform ScaleY="-1"/>
 7:                 <TranslateTransform Y="140"/>
 8:             </TransformGroup>
 9:         </Image.RenderTransform>
 10:         <Image.OpacityMask>
 11:             <LinearGradientBrush StartPoint="0.5, 1" EndPoint="0.5, 0">
 12:                 <GradientStop Offset="0" Color="#FFFFFFFF"/>
 13:                 <GradientStop Offset="0.5" Color="#22FFFFFF"/>
 14:                 <GradientStop Offset="0.65" Color="#00FFFFFF"/>
 15:             </LinearGradientBrush>
 16:         </Image.OpacityMask>
 17:     </Image>
 18: </StackPanel>

Das Originalbild ist natürlich ganz simpel, das gespiegelte Bild definiert 2 interessante Dinge:

  1. Eine RenderTransform: Verschiebt / Streckt / Zerrt … das Objekt ohne Rücksicht auf seine Umgebung (in WPF gibt es noch eine LayoutTransform, die vorher alles, was im Weg ist zur Seite schiebt). Die RenderTransform skaliert das Image erst einmal mit dem Faktor –1 (entspricht einer Spiegelung an der X-Achse), dann verschieben wir es mit Translate nach unten, damit es unser erstes Bild nicht überdeckt.
  2. Natürlich wollen wir, dass das ganze auch ein bisschen realistisch rüberkommt, das gespiegelte Bild soll also nach unten hin nach und nach immer mehr verschwinden. Das erreichen wir über eine OpacityMask, die über einen Farbverlauf definiert wird. Natürlich sind das hier keine wirklichen Farben, sondern es wird nur der Alpha-Wert (also der erste Wert) der Farbe verwendet, der die Durchsichtigkeit angibt. Bei mir variiert der hier von FF (voll sichtbar) bis 00 (voll durchsichtig).

Und hier das Resultat:

reflection

Neue Features für den EBC Designer

Vor ein paar Tagen hatte ich ja schon meine verbesserte Version des EBC Designers für Visual Studio vorgestellt. Hier jetzt noch mal ein Update, das folgendes beinhaltet:

  1. Die neue Version kann parallel zum alten EBC Designer verwendet werden (man braucht also die alten .ebc Boards nicht neu zu malen). Dadurch, dass ich die Extension auf .ebcx geändert hab gibts auch keine Probleme mit den Dateien
  2. Die Usings auf dem Board werden jetzt automatisch generiert, wenn man einen neuen Typ hinzufügt (als Connection-Typ oder Part-Typ). Damit wird die Darstellung übersichtlicher, weil nur noch die Typnamen (ohne Namespace) im Diagramm angezeigt werden. Das sieht dann ungefähr so für das Diagramm aus:

usings1

 

Und so für die generierten Usings:

usings2

Installation

Achtung: Unbedingt erst den Designer von meinem vorigen Post deinstallieren (weil der sich auch auf ebcx registriert).

Quellcode steht jetzt zum Download unter codeplex. Wer trotzdem die vsix Datei gleich haben möchte findet die wieder hier.

Gedanken über den EBC Designer

Unter codeplex sind einige hilfreiche Tools für die Event-Based-Components zu finden. Interessant ist auch der  grafische Designer dazu. Der hat den Vorteil, dass man den grafischen Verdrahtungsplan 1 zu 1 in Code übersetzen lassen kann, d.h. das Modell entspricht exakt der Realität. Leider ist der Designer aber ziemlich unkomfortabel, was vor allen Dingen daran liegt, dass man alle Daten mit der Hand eingeben muss (z.B. Typnamen) und keine Möglichkeit hat irgendetwas auszuwählen.

Man kann aber mit relativ wenig Aufwand den Designer maßgeblich verbessern, was ich einmal versucht habe. Hier meine ersten Ergebnisse:

1. Typauswahl bei EBC-Komponenten



 

 

Es gibt jetzt 2 Arten von Parts, den PartContract (heißt der Compiler soll ein Interface generieren) und einen Existing-Part der über meinen in Visual-Studio integrierten Type-Browser eine Typauswahl ermöglicht (Auswahl von Typen im aktuellen Projekt und allen Projekten auf die verwiesen wird). Die Typen werden zwar im Clr Format ausgewählt, dann aber zur Anzeige und zum Weiterverarbeiten von mir ins C# Format konvertiert (String wird zu string usw.) Hier noch ein paar Screenshots vom Type-Browser:

2. Die Connections

Auch der Typ, der an den Connections fließt kann jetzt über den Type Browser ausgesucht werden. Zusätzlich kann man über ein Drop-Down Menü Event-Ausgänge oder Methoden-Eingänge auswählen (falls es sich um ein Existing-Part an dem jeweiligen Endpunkt der Connection handelt), ansonsten kann man natürlich auch einfach einen Namen eingeben (Create New). Falls möglich wird der Typ der Connection dann automatisch auf den jeweiligen Parametertyp festgelegt.

Die Ein- und Ausgänge müssen natürlich ebenfalls immer per Hand mit Create New benannt werden.

Download

hier vsix Datei runterladen

.NET/C# Type Browser

Es gibt ja von Microsoft diesen netten Dialog zum Auswählen von einem .NET Type (Type Browser), manchmal könnte man den auch in eigenen Anwendungen gut gebrauchen. Doch leider ist das kaum möglich, weil der Dialog sehr tief in der Workflow Foundation eingebaut ist (warum auch immer), und man ihn kaum außerhalb irgendwie gebrauchen kann. Es gibt zwar Ansätze, die den Type Browser austricksen und ihn mit haufenweise Dummy-Klassen füttern, damit er glaubt er wäre in der WF Umgebung, was aber nicht schön ist und auch aus irgend einem Grund nicht mit Generics funktioniert. Auch der Type Browser der Enterprise Library gefällt mir überhaupt nicht, weil auch der (wenn man sich mal den Quellcode ansieht) keinesfalls leicht verständlich und wiederverwendbar ist und viel Infrastruktur verlangt, die ich nicht in meinen eigenen Anwendungen haben will. Also habe ich mich dran gemacht einen eigenen Type Browser zu schreiben, den ich jetzt einfach mal hier rein stellen möchte, weil ich hoffe (und glaube), dass er ein bisschen brauchbarer ist als das, was Microsoft hier zu bieten hat.

Der Dialog ist denkbar simpel zu bedienen, man ruft den Konstruktor auf und übergibt ein Dictionary, das verschiedene Listen von Assemblies nach Schlüssel sortiert beinhaltet (z.B. referenced assemblies …). Dann kann man über die Property SelectedType  nach dem Aufruf von ShowDialog() den ausgewählten Typ abfragen.

public partial class TypeBrowserDialog : Window
{
    public TypeBrowserDialog(IDictionary<string, IEnumerable<Assembly>> toDisplay)
    {
        ...
    }

    public Type SelectedType
    {
        get;
        set;
    }
}

Zusätzlich habe ich einen abstrakten UITypeEditor (für die Verwendung in einem Property-Grid) implementiert, der den TypeBrowser verwendet. Bei EditValue (also wenn eine  Type Property im PropertyGrid gesetzt werden soll), passiert hier folgendes:

public override object EditValue(ITypeDescriptorContext context, IServiceProvider provider, object value)
{
    var typeBrowser = new TypeBrowserDialog(this.GetAssemblies(context, provider));

    if (typeBrowser.ShowDialog().GetValueOrDefault())
    {
        return typeBrowser.SelectedType;
    }

    return value;
}
protected abstract IDictionary<string, IEnumerable<Assembly>> GetAssemblies(ITypeDescriptorContext context, IServiceProvider provider);

Je nachdem, welche Typen man zur Auswahl haben möchte, kann man dann die abstrakte Methode GetAssemblies() in abgeleiteten Editoren überschreiben. Es gibt momentan zwei Implementierungen von mir, ein Editor, der alle Typen der mscorlib.dll anzeigt und ein in Visual Studio integrierter Editor, der mit dem „Visual studio visualization and modeling sdk“ zusammen verwendet werden kann.

Alle, die den TypeBrowser verwenden möchten finden den kompletten Quellcode zum Download hier.