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