Seit vielen Jahren setze ich in meinen Anwendungen Frameworks ein, die mir eine remote Kommunikation mit Systemen gestatten, die in einem unabhängigen Adressraum, gegebenenfalls sogar auf einem anderen Server laufen.
In diesen Jahren habe ich dabei nacheinander unterschiedliche Technologien verwendet: SUNs RPC (Remote Procedure Call), das Java RMI, NeXTs Distributed Objekt Protokoll, Corba IIOP im EJB Container, Corba mit C Bindings und Hessian.
Alle diese Frameworks versuchten, die wahre Natur der Remote-Kommunikation vor den Anwendungsprogrammierern abzuschirmen. In der Tat wird in vielen Fällen eine wunderbar heile Welt vorgegaukelt in der über ein Java-Interface der ferne Service beschrieben und abgerufen wird. Die Kommunikation über Prozess- und Systemgrenzen wird so zu einem ganz normalen, tagtäglich genutzten Funktions- und Methodenaufruf.
Doch beim näheren Hinsehen wird deutlich, dass diese Schnittstellen nicht nur das Problem haben, dass sporadisch RemoteExceptions
auftreten können.
-
Latenzen: Eine lokale Kommunikation ist in der Regel mit einer zu vernachlässigen Latenz von wenigen CPU-Zyklen verbunden. Sobald ich jedoch meinen Prozesskontext verlasse, können diese Verzögerungen rapide ansteigen und schnell auch einige Sekunden umfassen.
-
Verfügbarkeit: Ein lokaler Service im eigenen Adressraum steht der Anwendung praktisch immer zur Verfügung. Kommuniziere ich jedoch mit einer anderen Anwendung, gibt es bestimmt 1000 Gründe, warum diese gerade eben jetzt nicht genutzt werden kann.
-
CPU Bedarf: Bei jeder remote Kommunikation muss ich meine Datenelemente in irgend einer geeigneten Form serialisieren und als Bytestrom über das Netzwerk versenden. Das ist einfach für skalare Datentypen, kann aber schon bei zeigerbasierten Strukturen, Objekt- oder Datennetzen, zu einem Glücksspiel werden.
-
Call by Value: Jede Information, die über das Netz versendet wird, muss sich notwendigerweise von der ursprünglichen Quelle lösen. Ein gesendetes Objekt wird daher als Kopie der Werte zum Kommunikationspartner übertragen und als zusätzliche unabhängige Kopie wieder den Aufrufer erreichen. Dort muss das aufrufende Programm den Zustand des Objekts wieder mit dem ursprünglichen Element assoziieren. Keine einfache Aufgabe.
-
Versionierung: Häufig kommunizieren Systeme miteinander, die aufgrund abweichender Releasestände unterschiedliche Softwareversionen enthalten. Natürlich handelt es sich hier um ein Thema, dass insbesondere fachlich durch ein Portfoliomanagement adressiert werden muss und schlussendlich über entsprechend formulierte Anforderungen und Zwischenreleases den Softwareentwicklern mitzuteilen ist. Aber in vielen Fällen kann kein Einfluss auf die Releasepolitik eines genutzten Softwaresystems genommen werden. Flickr wird keine Rücksicht auf uns nehmen, wenn wir gerade mal eine API-Änderung nicht nachvollziehen können.
-
Komplexe Kommunikationsendpunkte: Wir leben in einer Welt, in der immer mehr Information und Funktion auf datenverarbeitende Systeme verlagert wird. Ein nur mäßig erfolgreicher Dienst wird dabei schon so stark genutzt, dass ein einzelnes Hostsystem häufig nicht in der Lage ist, dieses Volumen zu verarbeiten. Damit muss auch bei der remote Kommunikation mit komplexeren Kommunikationstechniken gearbeitet werden, um die Anforderungen hinsichtlich Skalierung und Verfügbarkeit erfüllen zu können. Das geht weit über die ansonsten genutzte einfache (war sie das jemals ?) TCP/IP basierte Kommunikation hinaus.
-
Synchrone Kommunikation: Die Kommunikation in einem Anwendungssystem ist von der Natur her erst einmal synchron. Ich rufe eine Funktion auf und mein Prozessor ist dann mit der Bearbeitung dieser Funktion beschäftigt. Nach dem RTS geht es dann in meinem Programmcode weiter. Bei einer remote Kommunikation sind aber mindestens zwei Netzwerk-Pakete an diesem Aufruf beteiligt: die Anfrage muss gesendet werden und die Antwort muss mich wieder erreichen. Es existieren also zwei mögliche Fehlerquellen bei einem remote Aufruf, die ich in meinen Programmen berücksichtigen muss. In der Regel wird mein Programm aber nie erfahren, wo das Problem aufgetreten ist, wenn es nicht explizit den Stand der Verarbeitung in fernen System überprüft.
Diese Einschränkungen zeigen deutlich, dass das schlichte Verstecken der remote Kommunikation durch clevere Frameworks und Technologien zwar die Verwendung dieser Kommunikationsformen erleichtert, die wahre Natur und die damit verbundenen Probleme letztlich aber nicht vor den Entwicklern verbergen kann.
Daher ist für mich der Zeitpunkt gekommen, grundsätzlich über die Art der remote Kommunikation nachzudenken, da sie verstärkt die Basis für komplexe Anwendungssysteme darstellen muss.
Meine aktuellen Überlegungen, sie sind bei Weitem noch nicht zu einem Abschluss gelangt, basieren auf den nachfolgenden Annahmen:
-
Für die Kommunikation muss ein Dokument ähnliche Datenrepräsentation gewählt werden, die von einem nicht-strikten Parser verarbeitet werden kann. Versionsunterschiede zwischen Anwendungsprogramm und genutzten Service müssen erkennbar sein, sollten aber die Interoperabilität nicht verhindern.
-
Das bislang verwendete synchrone Kommunikationsmodell ist verzichtbar und kann durch alternative Lösungen ersetzt werden.
-
Für die Funktionsaufrufe muss im Produktionsbetrieb in den Leitweg eingegriffen werden, um eine Skalierbarkeit, Versionierung oder auch Laststeuerung zu erreichen.
Aus diesem Grund experimentiere ich derzeit mit einem Testsystem, in dem für die remote Kommunikation JSON Dokumente zwischen den Clients und Servern ausgetauscht werden. Dabei wird der Datenaustausch zwischen den Kommunikationspartnern über eine Messaging Infrastruktur auf der Basis von RabbitMQ bereitgestellt. Durch Eingriff in die Bindings und den Einsatz zwischengeschalteter Routing-Prozesse kann ich dabei jederzeit die Verarbeitung beobachten und gegebenenfalls kritische Inhalte inspizieren; bei Bedarf kann jederzeit in die Verarbeitung eingegriffen werden in dem die Anfragen neu ausgesteuert werden.
Die bislang gesammelten Erfahrungen zeigen mir, dass es sich um eine interessante Lösung handelt die weiter verfolgt werden sollte. In einigen Punkten ergeben sich Gemeinsamkeiten mit den Grundsätzen der REST Architektur, ohne allerdings auf den prozeduralen Aspekt eines Funktionsaufrufs zu verzichten. Es ist auf jeden Fall eine Entwicklungsrichtung, die ich weiter verfolgen werde.