# Getrennte Rechner für RMI-Server/RMI-Registry, bzw. Alternativen?



## kopfsalat (12. Feb 2010)

Hallo!

Ich möchte eine verteilte Anwendung schreiben, bei der insbesondere eine Komponente namens _Controller_ und eine namens _Worker_ auf verschiedenen Rechnern laufen sollen. Üblicherweise sendet der _Controller_ and den _Worker_ Befehle, die dann von diesem ausgeführt werden. Manchmal jedoch möchte auch der _Worker_ eine Information unaufgefordert an den _Controller_ senden.

Mein Plan war bisher, Java RMI hierfür zu nutzen. Dazu wollte ich auf einem Rechner den _Controller_ laufen lassen, auf einem weiteren den _Worker_, und auf einem der beiden oder einem dritten eine RMI Registry. Idee war, dass nun _Controller_ und _Worker_ sich bei der Registry anmelden und dann jeweils wechselseitig ein Remote-Objekt des anderen holen, um diesem dann Nachricht zuzusenden.

*Problem*: Es scheint nicht möglich zu sein, ein Remote-Objekt bei der Registry anzumelden, wenn dieses nicht auf demselben Rechner wie die Registry implementiert ist (zumindest gelingt mir das nicht).
*Frage 1*: Stimmt das? Oder kann man das Problem irgendwie umgehen?

*Alternative*: Nun gäbe es natürlich die Möglichkeit, auf dem _Controller_- und dem _Worker_-Rechner jeweils zusätzlich eine eigene RMI Registry zu starten, so dass die beiden Komponenten die Implementierung des jeweils anderen bei seiner Registry erfragen können. Das finde ich aber doof, da dann auch wechselseitig die IPs bekannt gemacht werden müssen. Stattdessen wäre vielleicht folgendes möglich (?):

Der _Controller_ (RMI-Client) registriert sich anfangs als _Listener_-beim _Worker_ (RMI-Server), indem er diesem eine Art Posteingangs-Objekt (den Listener) übersendet. Fortan kann nun auch der _Worker_ jederzeit Listener-Funktionen des _Controller_s aufrufen. Mit dieser Alternative versuche ich also, eine bidirektionale Kommunikation herzustellen, obwohl nur eine einzige RMI Registry genutzt wird.

*Frage 2*: Ist das so möglich? Ich erahne dort folgendes Problem (welches vielleicht aber gar nicht existiert). Wenn der _Controller_ das Listener-Objekt an der _Worker_ gesendet hat und aus diesem Remote-Call zurückgekehrt ist, wird das Listener-Objekt dann vielleicht nach einiger Zeit ungültig? Oder darf es problemlos im _Worker_ gespeichert werden und jederzeit später auch für Rückaufrufe zum _Controller_ verwendet werden?

Zur Verdeutlichung hier eine Rohstruktur dieser bidirektionalen Kommunikation:

[Java]
// RMI-Server
class Worker {
    public doSomething() {
        // Aufgerufen vom RMI-Client, implementiert auf Seiten des Workers (RMI-Server)
    }

    public registerListener(Listener listener) {
        // aufgerufen vom RMI-Client via Remote.
        // übergibt insbesondere einen Listener, der auf der RMI-Client-Seite implementiert ist,
        // und ab und an von hier aus (also vom RMI-Server aus) aufgerufen wird.
    }
}
[/Java]

[Java]
// RMI-Client
class Controller {
    Listener listener;
    (...)
}
[/Java]

[Java]
// Hilfsklasse, implementiert auf RMI-Client-Seite
class Listener {
    public somethingHappened(String s) {
        // Aufgerufen vom RMI-Server, implementiert auf Seiten des Controllers (RMI-Client)
    }
}
[/Java]

Also: Weiß jemand, ob es hierbei ein Problem gibt? Oder gibt es vielleicht noch eine elegantere Lösung?

Würde mich sehr über Hilfe freuen!
Danke,
kopfsalat


----------



## FArt (12. Feb 2010)

Bidirektionale, asynchrone Kommunikation? JMS nehmen und wohl fühlen...


----------



## kopfsalat (12. Feb 2010)

Danke schonmal für den Hinweis! JMS kannte ich noch gar nicht, aber es kommt durchaus als weitere Alternative in Betracht. Allerdings möchte ich zumindest für die Richtung _Controller_ -> _Worker_ lieber eine synchrone Kommunikation: Das Leben wird mir derzeit sehr dadurch vereinfacht, dass der Remote Call bis zur Fertigstellung blockiert und auch direkt einen Rückgabewert hat. Mittels JMS müsste ich hier wohl nach jedem Nachrichtenversand an den _Worker_ den _Controller_ in eine Warteschleife übergehen und seine Eingangsqueue nach der Antwort pollen lassen. Etwaige Übertragungsfehler (da etwa der _Worker_ abgeschmiert ist) müsste ich mittels Time-Outs abfangen. Ist also um einiges komplizierter.

Ergo: Mir wäre eine synchrone bidirektionale Kommunikation lieber, wie eben per RMI 'in beiden Richtungen'.


----------



## FArt (12. Feb 2010)

Das Verhalten wäre mit reinem JMS nicht so viel anders. Aktives pollen ist nicht nötig (ok, zugegeben, das wird implizit natürlich schon gemacht). Du registrierst einen Listener auf eine Queue (oder Topic bei einer Art Broadcast) und dessen Callback (onMessage) wird aufgerufen, sobald etwas da ist.
Ausserdem kannst du bei JMS leicht das verwendete Protokoll austauschen, das für die Übertragung verwendet wird. Das macht dich viel flexibler (angenommen Controller und Worker sind über komplexe Infrastrukturen (Router, Firewalls, DMZ, Internet, ...) verbunden... Du kannst Messags priorisieren, filtern, transaktional versenden, persistente Messages verwenden (die gehen auch nicht verloren, wenn mal der Controller down ist, ..., uvm). 

Mein Tipp: für dein Vorhaben nur auf JMS setzen...


----------



## kopfsalat (15. Feb 2010)

Ich habe mir nun JMS (Apache ActiveMQ) genauer zu Gemüte geführt. Tatsächlich beabsichtige ich nun, JMS anstelle RMI zu nutzen, obwohl synchrone Kommunikation deutlich umständlicher zu realisieren ist - einige Tests zeigten aber, das bei RMI und JMS letzlich der einfache Nachrichtenversand etwa gleich schnell funktioniert. Grund dafür ist wohl auch, dass man bei JMS nicht tatsächlich auf Pollen (im Sinne von aktivem Warten) angewiesen ist, sondern dass mittels Listenern und Monitoring direkt auf einkommende Nachrichten reagiert werden kann. Auf diese Weise habe ich mir nun "Agenten" geschrieben, die auch eine Funktion send(...) anbieten, die solange wartet, bis die entsprechende Antwort (oder ein TimeOut) eintritt. Damit wird das ganze dann ab dieser Ebene genauso komfortabel wie mit RMI.

Schön ist auch die Möglichkeit zur Authentifizierung, die man sonst bei reinem RMI erst noch selbst einbauen müsste. Der ausschlaggebende Grund war aber die Möglichkeit, an die JMS-Schnittstelle leichter andere Architekturen andocken zu können, was für mein Projekt durchaus interessant werden kann.

Eine Sache muss ich aber noch testen, und habe diesbzgl. ein paar besorgniserregende Fragen im Netz gefunden: Kann ich einen Client auch über einen HTTP-Proxy (im Firmennetz) mit einem JMS-Broker (per ActiveMQ) verbinden? Tests kann ich erst nächste Woche dazu machen, ich würde es zunächst einfach mittels den Proxy-Klassen oder dem Vorgehen von hier: Java HTTP Proxy Settings  Information for Technology versuchen. Mit RMI war das kein Problem - und eigentlich sollte das hier ja auch funktionieren... Hast Du, oder jemand anderes, da vielleicht Erfahrung mit ?

Vielen Dank nochmal für den JMS-Tip!


----------



## FArt (15. Feb 2010)

Ich habe mit ActiveMQ noch nicht gearbeitet. 
Ich gehe davon aus, dass die Transportschicht auch dort von JMS getrennt konfiguriert werden kann. Somit muss ActiveMQ nur so konfiguriert werden, dass HTTP als Transport verwendet wird. Dazu arbeitet in der Regel ein Servlet auf der Serverseite. Damit ist es auch kein Thema einen HTTP Proxy zu verwenden.


----------

