# Datenabfrage mit Timeout (alternative zu JMS)



## reNur (22. Mai 2012)

Hallo,

vllt eine etwas seltsame Frage - aber ich probiers trotzdem:
In einer Anwendung wird JMS für folgendes Szenario verwendet:
Client stellen Anfragen an den Server. Der Server verteilt diese an verschi. Applikationen (per JMS angebunden) und bekommt dann die berechneten Rückgabewerte.
Im Moment ist es so, dass die Rückgabedaten einfach in einer JMS-Queue bleiben und vom Server abgefragt werden. Für das Empfangen wird ein SLSB aufgerufen, das sich mit der JMS-Queue verbindet und mit receive( timeout ) auf die entsprechende Antwort wartet.

So, das große Problem ist natürlich: Pro Client, der die Rückgabedaten erwartet, wird neben einer SLSB-Instanz eine JMS-Session belegt, was ja nicht unbedingt Sinn der Sache ist. Der ganze Vorgang wird relativ schnell abgewickelt, performancetechnisch lohnt es sich also nicht, die Daten persistent zu machen.

Meine Frage nun: Welchen Ansatz könnte ich noch verfolgen, wenn ich ein Verhalten wie in JMS haben will - also mit einer receive Methode und einem zu definierenden Timeout auf ein Ergebnis "warten".


----------



## FArt (25. Mai 2012)

Ein SLSB pollt auf Messages? Wer kam denn auf diese Idee? Dafür sollt man ein Message Driven Bean verwenden. Das könnte auch schon die Lösung für dein Problem sein.

Evtl. wäre noch ein Ansatz über asynchrone Beans denkbar (ab EJB 3.1).


----------



## reNur (25. Mai 2012)

Hmm... ich persönlich bin auch ein Fan von Message Driven Beans, aber nicht jeder ist davon überzeugt  In der HornetQ-Dokumentation wird beispielsweise explizit davon abgeraten und erwähnt, dass man MDB nur verwenden soll, wenn man sie unbedingt braucht.

Wie auch immer: Wie würde ich denn einen Consumer, der auf Daten wartet, mit Hilfe eines MDB hinbekommen? Der Knackpunkt ist ja das "warten".... hier bietet sich JMS an, weil es einen einfachen Methodenaufruf dafür gibt (consumer.receive(timeout)).

Ein MDB fragt die Queue, in die die Antwort geschickt werden soll, natürlich kontinuierlich ab - aber wenn ich nun an einer Message interessiert bin, die in der Queue liegt, und darauf eine vorgegebene Zeit warten will - dann hilft mir das MDB ja relativ wenig, oder? Dazu müsste ich ja eine Art call back Mechanismus einbauen, der dann vom MDB aufgerufen wird, wenn die Antwort eintrifft.
Oder hab ich nur nicht verstanden, was du meinst?


----------



## FArt (29. Mai 2012)

reNur hat gesagt.:


> In der HornetQ-Dokumentation wird beispielsweise explizit davon abgeraten und erwähnt, dass man MDB nur verwenden soll, wenn man sie unbedingt braucht.


Bitte genauer. Wo steht das und warum wird von MDBs abgeraten bzw. wann braucht man sie unbedingt? Ich kann mir nicht vorstellen, dass es um eine pauschale Ablehnung geht, noch dazu ohne passende Alternativen aufzuzeigen.

"Fan" von MDBs hin oder her: ein sauberes Design sollte immer im Vordergrund stehen. Mit seltsamen Konstrukten aus Gründen der "Optimierung" fährt man mit großer Wahrscheinlichkeit gegen die Wand.


----------



## reNur (15. Jun 2012)

Hallo,

sorry für die späte Rückmeldung.
Also, von MDB wird generell in der HornetQ-Doku im Kapitel Performance Tuning abgeraten:


> Don't use Message-Driven Beans for the sake of it. As soon as you start using MDBs you
> are greatly increasing the codepath for each message received compared to a straightforward
> message consumer, since a lot of extra application server code is executed.



Wie auch immer, MDBs würden mit ja auch nicht weiter helfen. Nochmal das Szenario:
Ich schicke eine JMS Nachricht ab und erwarte kurzfristig das Ergebnis der Berechnung, die genau durch diese Nachricht angestossen wird. Ich warte nun also an der Queue mit einem receive() (z.B. über die CorrleationID) bis die erwartete Nachricht eintrifft.
Bis das passiert, ist natürlich erstmal das SLSB "belegt" (was mir im Moment egal ist - man kann eben nur gleichzeitig auf x Ergebnisse warten).

Was viel schwerer wiegt: Es ist gleichzeitig eine JMS Session "verbraten". Davon hab ich in meinem Fall (JBoss7.1 mit HornetQ) gerade mal 20 Stück - d.h. falls ich auch 20 Nachrichten gleichzeitig warte, kann ich aus dem gesamten System keine Messages mehr verschicken/empfangen, da alle Sessions belegt sind.

Eine MDB könnte ich zwar auf die Queue legen . aber das hilft mir ja nicht weiter, da ich das Ergebniss dann irgendwie weiterleiten muss, wenn die MDB eins empängt.

Ich bräuchte nun also irgendwie die Möglichkeit, auf ein "Ergebnis" (also eine JMS Message oder eben auf die entsprechend transportieren Daten) zu "warten", ohne dafür eine JMS Session zu verschwenden.
Und natürlich, ohne manuell irgendwie Thread.sleep() zu benutzen  (wobei das in EJB ja eh verboten ist).


----------



## FArt (15. Jun 2012)

Für einen sinnvolle Lösung bräuchte man zumindes die wichtigsten funktionalen und nicht funktionalen Anforderungen an deine Implementierung. Kennst du die bzw. kannst du die hier mitteilen?

Ich verstehe den Sinn immer noch nicht ganz. Wenn die Antwort "kurzfristig" kommt, warum dann über JMS und nicht synchron?

Wenn es asynchron sein soll, dann kann man das über asynchrone Beancalls (wie oben bereits erwähnt) realisieren, das ganze inklusive Rückantwort über Future-Objekte.

Die Aussage über die MDBs steht im Absatz "Performance Tuning", es steht nicht "generell" dabei sondern "for its sake" (was für alles gelten sollte, nicht nur für MDBs) und sie stammt, so weit ich weiß, von Tim Fox, was auch bei der Einordnung der Aussage hilfreich sein kann ;-)

Über Performance sollte man sich Gedanken machen, wenn es an einer Stelle Probleme gibt und dann sollte man (nachdem man weiß was dafür verantwortlich ist) diese Stelle verbessern. Erst dann würde ich ein sauberes Design aufgeben. So lange würde ich MDBs (wo sie passen) immer den Vortritt vor abenteuerlichen Implementierungen geben. Schließlich haben EJBs (und MDBs) auch Vorteile gegenüber einfachen Implementierungen: sie laufen in einem EJB-Kontext (bzgl. Transaktionen, Security, Caching, Pooling, Konfiguration, ...)

Man kann natürlich die Implementierung auch in POJOs realisieren und dann in einen Service (evtl. MBean) oder so tüten. Bei dir kommen die Anfragen aber wohl von Remote-Clients (über EJBs). Vielleicht sollten diese Anfragen von Haus aus über JMS gehen und die Antwort wird dann auch als Message übermittelt.

Wie gesagt, dazu kann ich nur sinnvolle Aussagen treffen, wenn mir die Anforderungen bekannt sind.


----------



## reNur (15. Jun 2012)

Danke für die Antwort.

Es gibt mehrere Gründe, wieso das ganze asynchron ablaufen sollte. Es soll z.B. später auf jeden Fall mit Clustering, Failover etc. experimentiert werden - das dürfte bei JMS wesentlich leichter zu bewältigen sein (als z.B. mit RMI etc.).
Ausserdem sehen viele Use-Cases auch so aus, dass die Requets abgeschickt werden und in der Zwischenzeit, während die Berechnungen von den externen Systemen durchgeführt werden, noch gewisse andere Dinge erledigt werden können - es sollte also kein blockierender Aufruf sein.
Zu guter Letzt ist die XA-fähigkeit von JMS auch ein großer Pluspunkt - denn verteilte Transaktionen sollten so gut wie möglich unterstützt werden.

Das Problem kann ich auch nicht mit asynchronen Beancalls lösen - oder ich bin zu blöd dafür und verstehe nicht, wie es gehen sollte .

Ich kann zwar z.B. eine Methode receive() erstellen, die asynchron abläuft und mir als Future-Objekt die Antwort zurückliefert.

Wenn ich nun irgendwo in meinem Code auf die Antwort warten will - dann kann ich doch nur wieder prüfen, ob das Future-Objekt schon vorliegt oder nicht. Ich kann nicht - wie bei einer JMS Queue - eine bestimmte Zeit auf das Ergebnis warten.


----------



## FArt (18. Jun 2012)

Nur für das Argument "Clustering" ist JMS keine Lösung. EJBs arbeiten in einem Cluster genau so gut und natürlich auch transaktional. Das gilt auch für MDBs. Das ganze natürlich (wenn richtig konfiguriert) mit Failover, LoadBalancing, und alles was man sich in einem tollen Cluster nur so vorstellen kann. Mit HAJNDI natürlich völlig transparent.

Bei JBoss und bei vielen anderen Applicationservern ist das Transportprotokoll der Kommunikation völlig egal und in der Regel transparent. Ich bin mir nicht sicher, wie es bei JBoss 7 ist, aber RMI ist schon lange nicht mehr das Protokoll der Wahl (siehe z.B. JBoss Remoting).

Ein Future-Objekt kannst du regelmäßig befragen, ob der Task fertig ist oder mit get auf die Antwort warten. Für get gibt es auch ein Timeout. Sonst kannst du eine Timeout selber bestimmen und mit canel den Task löschen.

Es kann sehr gute Argumente für JMS geben. Du hast bei deinen Anforderungen kein einziges (konkret) genannt. Bestenfalls das immer gerne genommene Wort "Failover" (ohne genauer zu definieren was das für die Applikation in eurem Fall bedeuten soll) deutet darauf hin, dass JMS sinnvoll verwendet werden könnte, wenn die Messages transaktional und persistent versendet werden. Mit eurer oben beschriebenen EJB-Wrapper-Variante würdes du aber jeden Vorteil kaputt machen, im Gegenteil, das wäre nicht sinnvoll clusterfähig. Nutzt die Clusterfähigkeiten des Applicationservers und versucht nicht selber etwas zu realisieren, was in der Spec und im Standard enthalten ist!

Ihr solltet euch mal einene EE Experten als Berater ins Haus holen. Hier werden evtl. fundamentale Designentscheidungen anhand von mangelndem Wissen getroffen. Das kann ins Auge gehen.

Oder habt ihr konkrete funkitionale und nicht funktionale Anforderungen, die du hier posten kannst?

Mein Tipp:
* Wenn ihr über Clustering nachdenkt, müsst ihr nicht über Mikroptimierung bzgl. Containercode nachdenken, denn ein weitere Knoten mit MDBs, der auch Messages konsumiert kann diese Einbußen leicht abfangen
* Wenn ihr über eine große Clusterlösung nachdenkt, dann haltet euch an Standards. Nur so könnt ihr den Cluster sinnvoll konfigurieren und vor allem auch warten und erweitern
* Wenn "Cluster" bei euch bisher nur ein Buzzword ist und keine konkreten Anforderungen gestellt sind (vollständig, nicht wischi-waschi), dann haltet euch am EE Standard fest und einem Applicationserver... fertig... . Wenn ihr Anforderungen habt, könnt ihr weiter denken. Vorher ist das lediglich Zeitverschwendung
* Wenn das von euch noch keiner bemerkt hat (oder der Geschäftsleitung sagen konne oder wollte), dann holt euch einen Berater, der etwas ähnliches erzählen wird, mächtig teuer ist aber euch viel Geld sparen wird. Außerdem glaubt die Geschäftsleitung dem in der Regel fast alles ;-)


----------



## reNur (26. Jun 2012)

So, sorry komm erst jetzt dazu, wieder zu posten.

Also, um JMS bzw. allgemein um die Verwendung eines Massage-basierten Lösung komm ich bei den Szenarien nicht vorbei. Das liegt an bestimmten Use-Cases, die auch mit abgebildet werden sollen:
- Bei bestimmten Berechnungen interessiert mich das Ergebnis schlicht nicht, weil diese so lange dauern. Es gibt also Applikationen, die verschiedene Funktionen anbieten, von denen ein paar praktisch nur asynchron ausgeführt werden können/sollen. Für diese Berechnungen ist es sehr vorteilhaft, dass ich die Anfragen auch persistent in der Queue speichern kann.
- Es kann durchaus sein, dass eine Instanz einer Applikation während der Ausführung "merkt", dass sie eine Berechnung nicht durchführen kann. Bei einer JMS-Lösung kann ich die Anfrage in Messageform einfach nicht bestätigen oder sie wieder zurück in die Queue legen, ohne dass ich mir da groß Gedanken drüber machen muss. Die nächste Instanz übernimmt dann.
- Es handelt sich um Legacy-Applikationen, die mit einem Java-Wrapper gekapselt sind und die auch nicht direkt als EJBs ausgeführt werden können, da auch Aufrufe an eine C-Library durchgeführt werden. Die Applikation muss also auf jeden Fall ausserhalb des EJB-Servers laufen.
- Es werden auf absehbare Zeit wohl auch Applikationen dazu kommen, die man über STOMP ansprechen muss.

Ganz davon abgesehen: Ich habe noch nirgendwo gelesen, dass es verboten/nicht spec-konform ist, aus einem EJB heraus eine Queue zu pollen. Im JavaEE Tutorial heisst es auch:



> Because a blocking synchronous receive ties up server resources, it is not a good programming practice to use such a receive call in an enterprise bean. Instead, use a timed synchronous receive, or use a message-driven bean to receive messages asynchronously. For details about blocking and timed synchronous receives, see Writing the Clients for the Synchronous Receive Example.



Bedeutet für mich, dass es ok ist, auf eine Nachricht mit einem receive(timeout) zu warten - was ja im Moment auch passiert (ich bin mir nicht mehr 100% sicher, ob ich so etwas schonmal in einem Beispiel gesehen habe, ich meine aber schon).
Ich werde mir das mit den Futures in EJB 3.1 nochmal anschauen - ich hab bis jetzt schlicht übersehen, dass es auch eine get(timeout) Methode gibt .
Bleibt aber nach wie vor die Frage, wie ich den Schritt dahin schaffe. Ich habe dann eine MDB, die alle Ergebnisse aus der ReplyQueue empfängt. Diese muss ich ja nun irgendwie "weiterleiten", damit ich darauf warten kann...


----------



## FArt (26. Jun 2012)

Nein, das Zitat ist völlig richtig.. man sollte es nicht machen, weil man damit natürlich Funktionalität von JMS aushebelt. Wie beschrieben: jeder wartende EJB Call bindet EJB Ressourcen (bei MDBs ist das eben nicht so). Außerdem ist die Anzahl der parallel laufenden EJBs eines Typs begrenzt (Containerkonfiguration), was bedeutet, dass bei Last Calls abgewiesen werden können, nur weil andere gerade warten... sehr unschön.

Wie ich schon am Anfang sagte: ich ziehe ein sauberes Design vor, dazu gehört auch, dass ich solche Stellen vermeiden würde wenn möglich, auch wenn sie "nur" bad practice sind und nicht durch die Spec verboten werden. 

Ok ist alles, was laut Spec nicht explizit verboten ist und dessen Auswirkungen du kennst und berücksichtigst.


----------



## reNur (27. Jun 2012)

Ich komm trotzdem nicht weiter.. stehe ich auf dem Schlauch?

Ich habe also Anfragen (z.B. ein WebService Request(Daten)), die per JMS an die Applikation geschickt werden. Diese berechnet irgendetwas und schick die Daten an den Server zurück.
Dort lauscht ein MDB auf die Reply-Queue und erhält also die berechneten Daten. So, aber wie gehe ich nun vor, wenn der Client die berechneten Daten auslesen will? Also z.B. eine EJB-Methode receive(Request, timeout). Wie soll ich denn da ein Future-Object in einem asynchronen Call einsetzen? Hmmm....


----------



## FArt (28. Jun 2012)

Das asynchrone Bean sollte evtl. einer Alternative zu JMS sein. Man kann es aber natürlich auch nach deinen Anforderungen kombinieren.
Insgesamt kommen mir die Anforderungen ein wenig seltsam vor, vermutlich weil mir einfach noch zu viele Informationen fehlen.

Anscheinend soll der Client (Webservice => Client = Browser?) mehrere parallele Aktionen mit einem Call anstoßen und am Ende aller Aktionen ein Ergebnis erhalten.

Der Client kann blockierend warten (evtl. mit Timeout) oder auf ein Ergebnis pollen oder über einen Callback benachrichtigt werden. 

Auf dem Server können die parallel laufenden Aktionen über JMS oder andere Mittel verteilt werden, das ist egal. Auf jeden Fall brauchst du noch eine Art Barriere zur synchronisation um das Ergebnis verarbeiten zu können.

Wenn du nicht während der ganzen asynchron laufenden Arbeit Ressourcen blockieren möchtest (Webservicecall, Beancall, ...) solltest du über Polling oder Callbacks nachdenken.

Ein Remote-Beancall auf ein asynchrones Bean könnte sowohl Polling als auch Callback über das Future Objekt realisieren. Es gibt aber auch noch viele andere Möglichkeiten.


----------

