Follow my new blog

Donnerstag, 26. Juni 2008

Bound in Space - Skizze der Kommunikation in einem Application Space

Komponenten verteilen, Komponenten parallel arbeiten lassen und überhaupt Software aus Komponenten aufbauen sollte viel, viel einfacher werden. Denn nur dadurch würden sich diese Konzepte breiter durchsetzen. Und nur dadurch würde Software in größerer Menge performanter, skalierbarer, testbarer, evolvierbarer. Dafür habe ich in meinem gestrigen Posting eine Lanze gebrochen.

Heute möchte ich zu dieser Vision eines Application Space (AppSpace) ein Bild zeichnen. Ich stelle mir vor, dass Software in Zukunft sehr einheitlich und ganz grundsätzlich so organisiert sein sollte:

image

Die Grundbausteine von Software sind Komponenten. (Klassen sind auch wichtig, aber unterhalb der Wahrnehmungsschwelle von Softwarearchitekten.) Definieren wir sie einfach mal als binäre Codeeinheiten mit separater Leistungsbeschreibung (Kontrakt). Wenn Sie jetzt "Assembly" denken, liegen Sie nicht falsch.

Diese Komponenten "leben und arbeiten" in etwas, das ich mal Compartment nennen. Solche "Abteile" teilen einen Raum, deshalb finde ich den Begriff ganz passend in Bezug auf die Raum-Metapher, die hinter Application Space steht.

Wie diese Komponenten in den Abteilen aufgehängt sind, ist zunächst einmal nicht so wichtig. Hauptsache, sie haben erstmal ein "Zuhause", in dem manche "zusammen leben" und andere eben in anderen "Zuhauses". (Hm... vielleicht wäre "Appartment" besser als "Compartment"? Dann könnten Komponenten in einer WG leben :-)

Innerhalb der Compartments können Komponenten direkt miteinander kommunizieren, einfach über Methodenaufruf. Sie liegen im selben "Abteil" dicht beisammen (im selben Adressraum) und können daher den Stack nutzen. Alles ist, wie Sie es kennen und lieben. Einzig, die Verbindung zwischen zwei Komponenten wird durch einen DI Framework bzw. Microkernel hergestellt. Den bietet das Compartment als komponentenumfassender Kontext; das ist eine seiner Infrastrukturleistungen.

Wie Sie Compartments auf Prozesse oder Maschienen oder gar Netzwerke verteilen, ist zunächst einmal unerheblich. Das sind Feinheiten des Deployments. In einem AppSpace gilt: Was in einem Compartment gehostet wird, liegt nah beieinander in einem Adressraum, der direkte Kommunikation erlaubt. Deshalb können Sie auch entscheiden, ob Compartments das noch durch eigene AppDomains unterstreichen sollen. Kann sein, muss aber nicht.

Fangen Sie mit 3 Compartments in einem Prozess an und verteilen Sie sie später auf 3 Prozesse. Oder führen Sie Redundanz ein, indem Sie sie auf 7 Prozesse verteilen. Welcher Art die Prozesse sind - also IIS oder NT Service oder Console EXE usw. - ist dabei nicht so wichtig. Diese Compartment Hosts müssen einfach zum Zweck der darin "lebenden" Komponenten passen.

Wegen dieser Flexibilität fehlen im obigen Bild auch Prozesse und Maschinen und Netzwerke. Denn das Credo des AppSpace ist, dass diese Deploymenteinheiten zweitrangig sind. Erstrangig ist hingegen, ob eine Komponente synchron und nah bei anderen läuft oder asynchron und fern von anderen. Darüber gibt ein AppSpace-Diagramm jedoch Auskunft.

Synchrone Komponenten sind einfach - selbst wenn Sie DI benutzen. Asynchrone Komponenten jedoch... da wird es knifflig. Wie sollen die miteinander kommunizieren? Die Asynchronizität beginnt schon, wenn zwei Objekte auf separaten Threads laufen. Sie wird schlimmer, wenn diese Objekte in verschiedenen AppDomains hängen und noch übler wird´s, wenn sie durch Prozesse, Maschinenen oder Netzwerke getrennt sind.

Heute stehen dafür eine Reihen unterschiedlicher Technologien zur Verfügung. Da gibt es eine Pallette von Möglichkeiten, Parallelität einzuführen. Wenn Ihre Anwendung in mehrere Prozesse zerlegt ist, ergibt sich die ganz natürlich. Ansonsten können Sie Threads aber auch von Hand auf die eine oder andere Weise erzeugen. Aber Achtung: nicht zuviele, sonst ist die ganze Asynchronizität kontraproduktiv.

Vielfältiger sind die Möglichkeiten für die Kommunikation zwischen den Threads. Die kann über´s Dateisystem laufen oder gemeinsame Objekte (shared memory), rohe TCP Sockets funktionieren genauso wie WCF.

Diese Vielfalt der Kommunikationsmedien, so verständlich sie ist, halte ich nun inzwischen für nachteilig. Niemand kennt sich mehr aus. Ich plädiere daher für eine radikale Vereinfachung durch Vereinheitlichung, mit der sich vielleicht 80% aller verteilter Kommunikation lösen lässt. Nein, nicht 100%, aber ich sag mal optimistisch 80%. Mir liegt daran, Asychronizität "den Massen" nahezubringen und nicht wirklich jedem zu jeder Zeit die Entscheidung zwischen vielen Optionen aufzubürden.

Die Technologievielfalt kann und soll also erhalten bleiben - unter der Haube. Wer sie braucht, um im Einzelfall mal performanter zu sein, hat sie dann. Aber der allgemeine Fall wird durch Abstraktion einfacher. So wie es schon viele Male in der Geschichte der Softwareentwicklung geschehen ist. Beispiel SQL, Beispiel O/R Mapping, Beispiel GDI+ usw. usf.

Wie soll nun diese Vereinfachung der Kommunikation zwischen asynchronen Komponenten aussehen? Das obige Bild zeigt es: Neben der direkten Kommunikation innerhalb eines Compartments gibt es die indirekte (!) zwischen Compartments. Indirekt ist sie immer, wenn Komponenten asynchron zu anderen laufen. (Insofern kann auch innerhalb desselben Compartments indirekt kommuniziert werden.)

Indirekte Kommunikation bedeutet, dass eine Client-Komponente nicht (!) direkt mit einer Service-Komponente spricht. Client und asynchroner Service sind vielmehr immer (!) durch einen Vermittler verbunden. Im einfachsten Fall ist dieser Vermittler eine Warteschlange, eine Queue. Aber das ist nur der einfachste oder übliche Fall. Ein AppSpace ist nicht darauf beschränkt. Zwischen Client und Service können alle möglichen Vermittlerformen stehen: Queue, Stack, Baum, relationale Tabelle, Liste, Array, Dictionary... you name it.

Gemeinsam ist allen Vermittlern nur, dass sie Datenstrukturen sind. Die eine Komponente manipuliert sie, die andere beobachtet sie. Und dann können die Rollen wechseln. Beobachtung ist entweder aktiv (polling) oder passiv (Ereignisgesteuert, Stichwort: EDA).

Diese Datenstrukturen existieren virtuell im AppSpace und sind für alle Komponenten in allen Compartments grundsätzlich zugänglich. Deshalb liegen die Winkel der asynchronen Kommunikation im obigen Bild im Space.

Implementationstechnisch sieht das jedoch anders aus. Da liegen die Vermittlerdatenstrukturen ebenfalls in Compartments, sozusagen in einem Gürtel um sie herum.

image

Letztlich ist es aber egal, wo genau diese Datenstrukturen, die Container hängen. Sie können nach dem Prinzip "write remote, read local" immer beim asynchronen Service angesiedelt sein (also im Compartment der Pfeilspitze einer Verbindung zwischen zwei Komponenten). Oder sie können auch in einem ganz anderen Compartment liegen:

image

Container werden im AppSpace gegenüber Anwendungscode nicht durch eine Adresse repräsentiert, sondern durch einen AppSpace-globalen Namen.

Der AppSpace macht Komponenten insofern nicht nur durch DI implementationsunabhängig voneinander, sondern auch unabhängig in Bezug auf Ort und Zeit. Durch Container als Vermittler stehen Komponenten nicht mehr direkt in Kontakt, d.h. beide müssen nicht mehr gleichzeitig "anwesend" sein im AppSpace. Und AppSpace-weite Containernamen verbergen den genauen Ort (Compartment, AppDomain, Prozess, Maschine, Netzwerk), an dem eine asynchrone Komponente "lebt".

Nach der grundlegenden Entscheidung, ob Komponenten synchron oder asynchron kommunizieren sollen, kann die Topologie sich bei asynchronen Komponenten sehr weitreichend ändern, d.h. wechselnden Bedürfnissen anpassen, ohne dass Client-Anwendungscode davon betroffen wäre.

Der AppSpace soll also die Vorteile von EDA verallgemeinern (nicht nur Queues, sondern auch andere Datenstrukturen, auf die man lauschen kann) und mit Komponentenorientierung wie Computing in/via the Cloud verschmelzen. Ich sehe ihn als Aggregat vieler existierender Technologien, die er unter einer Abstraktionsdecke versteckt.

Insofern bietet ein AppSpace einen schönen Migrationspfad: Ich kann mit ihm anfangen und erstmal nur Komponenten in einem Compartment verbinden. Dann kann ich in die Asynchronizität klein einsteigen, indem ich mal im selben Compartment eine Komponente asynchron mache. Und dann kann ich die Asynchronizität ausdehnen auf mehr Komponenten. Und dann kann ich diese Komponenten verteilen. Schließlich laufen sie auf vielen Rechnern übers LAN oder gar Internet verteilt. Ohne, dass mein Applikationscode davon etwas merken würde. Denn sobald ich einmal mit der asynchronen, containervermittelten Kommunikation angefangen habe, skaliert die von der cross-thread bis zur cross-firewall Kommunikation. Das Geheimnis liegt darin, dass sie nicht nur nachrichtenorientiert, sondern eben datenstrukturvermittelt ist.

Keine Kommentare: