19.1.2 | Kommunikation |
Eine Client-Server-Anwendung besteht aus mindestens zwei Komponenten, die Daten untereinander austauschen. In Abbildung 19.1 sind diese zwei Teile die Client-Anwendung und die Server-Implementierung.Zwischen diesen beiden Anwendungsteilen besteht eine logische Verbindung. Bei der konventionellen Implementierung von Netzwerkanwendungen wird Lowlevel-Socket-Programmierung eingesetzt, und der Datenaustausch zwischen Client und Server wird über ein anwendungsspezifisches Kommunikationsprotokoll durchgeführt. Die Entwicklung eines eigenen Protokolls für den Datenaustausch zwischen zwei Anwendungsteilen hat jedoch folgende Nachteile:
- Die Protokollentwicklung ist meist sehr zeitaufwändig und fehleranfällig.
- Protokolle sind häufig nicht wiederverwendbar. Entwickelt man eine neue Anwendung mit anderen Anforderungen, muss wiederum ein neues Kommunikationsprotokoll entworfen werden.
Mit Hilfe von Brokern kann man Client-Server-Anwendungen entwickeln, ohne auf der Ebene von Sockets und Protokollen zu programmieren. Der Entwickler sollte die Kommunikation nicht aus Sicht eines Netzwerkprogrammierers sehen, der primitive Nachrichten und Datenrahmen über das Netz schickt, sondern in Form von logischen Operationen. Wenn man in einer lokalen Anwendung eine Operation ausführt, werden diese Operationen in Java normalerweise in Form von Methoden bereitgestellt. Was wäre also einfacher, als in der Anwendung genau diese Methoden zur Verfügung zu haben, die bestimmte Operationen im Server ausführen, statt auf dem Client. Hierdurch wäre eine optimale Integration in die Client-Anwendung gegeben: Lokale Operationen werden in der Client-Anwendung eben auch durch Methoden-Aufrufe realisiert. Wenn nun durch einen Methodenaufruf eine Operation auf dem Server ausgeführt wird, muss man in der Anwendung keine Unterscheidung zwischen lokalen Operationen und Operationen auf dem Server vornehmen: Die Verteilung auf mehrere Rechner wäre also transparent.
Genau das ist das Prinzip, das hinter einer Broker-Architektur steckt: Zunächst wird eine Menge von Methoden-Aufrufen definiert, die eine logische Server-Schnittstelle darstellen. Anschließend werden die Operationen in diesen Funktionen implementiert. Der Broker ermöglicht es nun, dass die Funktionen auf dem Client aufgerufen, die Operationen jedoch auf dem Server ausgeführt werden. Hierzu werden Client-Side Proxy (auch »Stub« genannt) und Server-Side Proxy (»Skeleton«) benötigt. Diese beiden Teile werden von einem Werkzeug generiert, das bei einer Broker-Implementierung üblicherweise mitgeliefert wird (z. B. RMI-Compiler bei RMI oder IDL-Compiler bei CORBA). Der Client-Side Proxy wird in den Client, der Server-Side Proxy in den Server eingebunden. Die Proxy-Objekte stellen die Verbindung zwischen den Client- und Server-Teilen der Anwendung und dem Broker her. Somit läuft die physische Kommunikation vom Client in den Client-Side Proxy über den Broker zum Server-Side Proxy und schließlich zur Server-seitigen Objekt-Implementierung. Dort wird die gewünschte Operation schließlich ausgeführt, bevor eventuell ein Rückgabewert an den Aufrufer zurückgeliefert wird. Die Proxys sind für die Bereitstellung der zuvor definierten Server-Schnittstelle zuständig, der Broker selbst übernimmt nur die Übertragung der Daten und Kommunikationsaufgaben.
Die Verwendung von Server-Side Proxys ist bei dynamischen Sprachen wie z. B. Java nicht unbedingt notwendig, da dort Methoden auch dynamisch aufgerufen werden können (siehe die Klasse Class).
Dadurch können die Aufgaben eines Server-Side Proxy von einer generischen Klasse übernommen werden, die vom Broker Methoden-Aufrufe entgegennimmt und diese dynamisch an die Implementierung des Server-Objektes weiterleitet. Ob nun tatsächlich ein Server-Side Proxy benutzt wird, hängt von der Broker-Implementierung ab. Bei der RMI-Implementierung des JDK 1.1 werden z. B. noch Server-Side Proxys benutzt, ab der Java-2-Plattform Version 1.2, sind sie jedoch nicht mehr erforderlich.
Der Client-Side Proxy verfügt über dieselben Methoden wie die Server-Schnittstelle. In der Implementierung werden jedoch nicht bereits die gewünschten Operationen ausgeführt, sondern es werden Daten über das Netzwerk geschickt. Bei Aufruf einer Methode des Client-Side Proxy werden Informationen über die aufgerufene Methode und die Aufrufparameter über den Broker an den Server übermittelt. Das Verpacken des Methoden-Aufrufs in eine serielle Form, die über Netzwerke übertragen werden kann, nennte man »Marshalling«. Der Server nimmt den Aufruf entgegen, im Server-Side Proxy wird nun der Methoden-Aufruf rekonstruiert, und schließlich wird die Methode der Objekt-Implementierung auf dem Server aufgerufen. Die Rekonstruktion des Methoden-Aufrufs aus den Daten, die über das Netz geschickt werden, nennt man »Unmarshalling«. In Abbildung 19.2 ist dieses Prinzip nochmals detaillierter dargestellt.Die Kommunikation zwischen Client und Server wird durch ein generisches Protokoll durchgeführt, das in der Lage ist, Informationen über Methoden-Aufrufe zu übertragen. Was letztendlich über das Netzwerk geschickt wird, ist die Aufrufspezifikation. Aus logischer Sicht besteht sie aus folgenden Bestandteilen:
Mit einem solchen Protokoll können beliebige Methoden-Aufrufe über das Netzwerk übermittelt werden. Die anwendungsspezifischen Komponenten sind hierbei die Proxy-Objekte. Proxy-Objekte sind abhängig von den Methoden, die der Server zur Verfügung stellen soll. Sie führen das Marshalling bzw. das Unmarshalling durch. Ändert sich die Server-Schnittstelle, müssen auch neue Proxy-Objekte generiert werden, da neue Methoden im Server auch in den Proxy-Objekten vorhanden sein müssen. Da die Proxy-Objekte jedoch von einem beim Broker mitgelieferten Werkzeug generiert werden können, besteht der Entwicklungsaufwand lediglich in der Definition der Server-Schnittstelle. Programmiert werden muss nur die Objekt-Implementierung auf Serverseite selbst sowie die Aufrufe der Server-Methoden auf dem Client. Die Kommunikation wird vollständig über das Broker-interne Protokoll durchgeführt. Eines der bekanntesten Protokolle für die Übertragung von Methoden-Aufrufen ist das Internet Inter ORB Protocol (IIOP), das von CORBA benutzt wird. Seit
- der Name der Methode, die aufgerufen wird,
- die Parameter, die an die Methode übergeben werden.
dem J2SDK 1.4 kann es jedoch auch von RMI verwendet werden. Als Alternative steht bei RMI das native RMI Wire Protocol zur Übertragung von Methoden-Aufrufen zur Verfügung.
Die serverseitige Objekt-Implementierung wird in einer eigenen Laufzeitumgebung in Java ausgeführt, die z. B. die Aufgabe hat, auf eingehende Anfragen zu warten, und diese an die Objekt-Implementierung weiterleitet. Bei einigen Architekturen kann die Laufzeitumgebung noch weitere Dienste wie z. B. die Durchführung von Transaktionen, Authentifizierung oder die Aktivierung von Objekten übernehmen. Bei der Benutzung umfangreicher Server-Objekten werden auf Serverseite viele Ressourcen benötigt. Theoretisch müssen jedoch nur die Objekte in den Speicher geladen werden, die gerade benötigt werden. Deshalb kann die Laufzeitumgebung Objekte aus dem Speicher auslagern, die seit längerer Zeit nicht genutzt wurden. Wenn erneut auf das Objekt zugegriffen wird, muss die Laufzeitumgebung das Objekt erneut aktivieren und in den Speicher einlagern.