Verteiltes Rechnen Verteilte Dienste mit Java Remote Method Invocation (RMI). Prof. Dr. Nikolaus Wulff Verteiltes Rechnen • Anwendungen im WWW sind meist als Client-Server Architekturen realisiert. • Ein Client stellt Anfragen an einen entfernten Server. – Per HTTP meistens als „primitive GET-Request“ mit einem HTML Dokument als Rückgabewert. – Programmierung „auf dem Draht“ mit primitiven Datenstrukturen per Java (Server)Sockets. • Java bietet zusätzlich die Möglichkeit höherwertige Dienste als echte Java Objekte anzusprechen. – HTTP basierte Web-Services via XML/SOAP und WSDL. – Remote Method Invocation (RMI) mit Java generierten Proxy und Adapter Klassen für Client und Server. © Prof. Dr. Nikolaus Wulff Höhere Programmierkonzepte 2 Verteilung und Nebenläufigkeit • Neben des grundsätzlichen Vorteils, dass Client und Server auf physisch getrennten Maschinen laufen, bietet eine solche Architektur die Möglichkeit der Parallelisierung und Lastverteilung. • Der Preis hierfür ist der Geschwindigkeitsverlust durch die Netzzugriffe und eine anfängliche Lernkurve zum Erlernen dieser Technologie(n). • RMI beinhaltet alle wesentlichen Bestandteile, die auch in den weiterführenden J2EE Architekturen – wie EJB oder Web-Services – Verwendung finden. © Prof. Dr. Nikolaus Wulff Höhere Programmierkonzepte 3 Architekturblaupause • Der Service wird als (Java) Schnittstelle beschrieben. • Serverseitig wird dieser Dienst implementiert. • Der Client bekommt mit Hilfe eines Stellvertreterobjekts eine Referenz auf eine Implementierung der Schnittstelle. • Ein Naming-Service bietet eine Art „Telefonbuch“ (Yellow Pages) für registrierte Dienste an. • Serverseitig registrieren sich die Dienste beim Naming-Service, clientseitig werden sie gesucht. • Client- und serverseitige Hilfsklassen werden passend zur Schnittstellenbeschreibung mit RMIC generiert. © Prof. Dr. Nikolaus Wulff Höhere Programmierkonzepte 4 HelloWorld Pseudo Code • Lokaler Methodenaufruf: dauert ~ 1 - 4 NanoSec HelloInterface h = new HelloImpl(); h.sayHello("HelloWorld"); • Remote Methodenaufruf: dauert ~ 5 - 50 MilliSec !!! HelloInterface h = HelloFactory.lookup("myServer","myObject"); h.sayHello("HelloWorld"); © Prof. Dr. Nikolaus Wulff Höhere Programmierkonzepte 5 RMI Architektur Remote Interface Client Application RMI Stub Class Remote Object Skeleton Class Remote Reference Layer Transport Layer (JRMP) © Prof. Dr. Nikolaus Wulff Höhere Programmierkonzepte 6 HelloWorld mit RMI RMI Framework java.rmi.* generated by RMIC Developer defined © Prof. Dr. Nikolaus Wulff Höhere Programmierkonzepte 7 RMIC Generierung • Seit JDK1.2 werden keine Quelltexte für die Stubund Proxy Klassen mehr auf der Platte oder im Archive gespeichert. • Mit den RMIC Parameter -v1.1 und -v.1.2 lässt sich steuern für welches RMI Protokoll generiert wird. • Werden beim Aufruf von RMIC die Parameter -keepgenerated übergeben, so ist es möglich den Quelltext dieser Klassen zu studieren. – Seit 1.2 wird die java reflection API eingesetzt. – -v1.1 generiert @deprecated Code. © Prof. Dr. Nikolaus Wulff Höhere Programmierkonzepte 8 Implementierung des Service public class HelloImpl extends UnicastRemoteObject implements HelloInterface { /** * Sole constructor. * @throws RemoteException during remote IO binding */ protected HelloImpl() throws RemoteException { super(); } © /* (non-Javadoc) * @see de.lab4inf.rmi.HelloInterface#sayHello(java.lang.St */ @Override public String sayHello(String msg) throws RemoteException { String ret = format("[%s %s] msg: %s",this, Thread.currentThread(),msg); System.out.printf("sayHello %s\n",ret); return ret; } Prof. Dr. Nikolaus Wulff Höhere Programmierkonzepte 9 Gelbe Seiten • Im Laufe der Zeit haben sich die Anforderungen an die Registrierung von RMI Objekten verändert. Es gibt inzwischen: • Die java.rmi.registry.Registry • Den java.rmi.Naming Service • Und für Enterprise Anwendungen den JNDI – Verzeichnisdienst das Java Naming and Directory Interface, das in J2EE Anwendungen verwandt wird. • Der Zweck bleibt gleich: Registriere ein Objekt unter einer URL (bind) und mache es auffindbar im Netz per (lookup)... © Prof. Dr. Nikolaus Wulff Höhere Programmierkonzepte 10 HelloClient mit RMI public static void main(java.lang.String[] args) throws Exception { String url = “//localhost/hello“; // 1. Referenz auf den Server per Naming Service System.out.println("Suche Server "+url); Remote ref = Naming.lookup(url); // 2. Cast auf den gewünschten Typ HelloInterface hello = (HelloInterface) ref; System.out.println("Fand "+hello); // 3. Remote Call ausführen long time = System.currentTimeMillis(); hello.sayHello("Hello vom HelloClient "+time); System.out.println("done ..."); } © Prof. Dr. Nikolaus Wulff Höhere Programmierkonzepte 11 Service Registrierung public class HelloImpl extends UnicastRemoteObject implements HelloInterface { // ... public static void main(java.lang.String[] args) throws Exception { String url = “//localhost/hello“; // 1. Server Instanz erzeugen System.out.println("Erzeuge Server"); HelloInterface hello = new HelloImpl(); // 2. Server Instanz beim Naming Service bekannt machen System.out.println("Registriere Server "+hello); Naming.rebind(url,hello); System.out.println("Server "+url+" ready... “); } © Prof. Dr. Nikolaus Wulff Höhere Programmierkonzepte 12 Testlauf Wichtig: Vor dem Start des Servers muss die rmiregistry mit entsprechendem Classpath gestartet sein, sonst schlägt die Registrierung der Serverinstanz fehl. $ java de.lab4inf.rmi.HelloImpl verteilt Erzeuge Server multi-threaded Registriere Server HelloImpl [UnicastServerRef [liveRef: [endpoint: [127.0.1.1:48796](local),objID: [3ffe7dc9:134eaef05ce:-7fff, 6082581128767713112]]]] Server rmi://localhost/hello ready... sayHello [HelloImpl[UnicastServerRef [liveRef: [endpoint:[127.0.1.1:48796](local),objID: [3ffe7dc9:134eaef05ce:-7fff, 6082581128767713112]]]] Thread[RMI TCP Connection(2)-127.0.0.1,5,RMI Runtime]] msg: Hello vom HelloClient 1326791465704 © Prof. Dr. Nikolaus Wulff Höhere Programmierkonzepte 13 Generierter Stub public final class HelloImpl_Stub extends java.rmi.server.RemoteStub implements de.lab4inf.rmi.HelloInterface, java.rmi.Remote { private static final long serialVersionUID = 2; private static java.lang.reflect.Method $method_sayHello_0; de.lab4inf.rmi-Packagename entfernt ... static { try { $method_sayHello_0 = HelloInterface.class.getMethod("sayHello", new java.lang.Class[] {java.lang.String.class}); } catch (java.lang.NoSuchMethodException e) { throw new java.lang.NoSuchMethodError("stub class initializ } } • Der generierte Code verwendet die Reflection API, um die Methodenzeiger zu finden/setzen.... © Prof. Dr. Nikolaus Wulff Höhere Programmierkonzepte 14 Delegation an die Implementierung // methods from remote interfaces // implementation of sayHello(String) public java.lang.String sayHello(java.lang.String $param_String_1) throws java.rmi.RemoteException { try { Object $result = ref.invoke(this, $method_sayHello_0, new java.lang.Object[] {$param_String_1}, 8370655165776887524L); return ((java.lang.String) $result); } catch (java.lang.RuntimeException e) { throw e; } catch (java.rmi.RemoteException e) { throw e; } catch (java.lang.Exception e) { throw new java.rmi.UnexpectedException("undeclared checked exceptio } } • Und per Reflection wird die implementierende sayHello-Methode gerufen.... © Prof. Dr. Nikolaus Wulff Höhere Programmierkonzepte 15 RMI wozu...? • Mit RMI ist es möglich objektorientiert Dienste verteilt anzubieten. • Z.B. einen Script-Parser, den Differentiator oder …, so dass rechenintensive Arbeiten auf entfernten leistungsstarken Maschinen ausgeführt werden. • Wird dies zusätzlich mit dem „Divide and Conquere“ Ansatz durchgeführt, so ist es möglich paralleles Rechnen auf verteilten Maschinen durchzuführen. • Dies lohnt sich immer dann, wenn der Zeitverlust durch die Verteilung der Daten per Netz-IO geringer ist als der Performanzvorteil der Parallelisierung. © Prof. Dr. Nikolaus Wulff Höhere Programmierkonzepte 16 RMI Pragmatik • Es ist wichtig bei verteilten Anwendungen die richtige Granularität der Aufrufe zu finden. • Viele „kleine, verteilte“ Getter/Setter-Methoden bremsen eine verteilte Anwendung komplett aus! • Daher „Datenobjekte“ als verteilbare Container für RMI Argumente verwenden. Diese sind dann meist serialisierbar, werden „in einem Rutsch übertragen“ und für Enterprise-Anwendungnen zumeist generiert. • Entsprechende Werkzeuge, wie z.B. XDoclet, Spring und Hibernate unterstützen dies und es gibt zahlreiche Architekturblaupausen für verteilte Anwendungen. © Prof. Dr. Nikolaus Wulff Höhere Programmierkonzepte 17 Zusammenfassung • Java bietet mit den RMI Paketen eine Möglichkeit verteilte objektorientierte Anwendungen mit einfachen Mitteln zu entwickeln. • Gegen eine Remote-Schnittstelle werden automatisch Client-Proxies und Server-Adapter generiert. • Ein Naming-Service liefert „Gelbe Seiten“. • Lastverteilung und höherwertige Dienste wie z.B. Autorisierung, Sicherheit, Transaktionen etc. fehlen. • Diese Dienste werden mit der Java Enterprise Edition (J2EE) zur Verfügung gestellt. © Prof. Dr. Nikolaus Wulff Höhere Programmierkonzepte 18