23. November 2012 | 4 min lesezeit

Kein Tor verpassen – mit WebSockets!

Schon während der Fertigstellung unseres Digitalkickers zur JAX 2012 (siehe unsere früheren Blogeinträge) war uns vollkommen klar, dass ein bestimmtes Feature definitiv umgesetzt werden muss. Leider fehlte uns vor einem halben Jahr die Zeit dazu.

Bisher hatten wir die ständigen Aktualisierungen (bspw. des Spielstandes) über ein Zeitintervall-getriggertes Polling realisiert. Das sollte sich nun fundamental ändern. Schließlich weiß der Server am besten über Spielstandsänderungen Bescheid. Also wieso sollte er nicht selber aktiv dem Frontend Bescheid geben, wenn ein Tor gefallen ist? Mit der WebSocket-API im HTML 5 Umfeld bietet sich hier eine elegante Lösung für eben dieses Problem an. Zudem war es so möglich erheblich kürzere Reaktionszeiten auf Spielereignisse bei unserem Kicker zu erreichen, da allein schon das wiederholte Auf- und Abbauen einer Verbindung zum Server entfiel.

Die Umsetzung dieses Features hatten wir uns also nun zur diesjährigen W-JAX vorgenommen. Das gab uns die Möglichkeit eine ausreichende und im Nachhinein betrachtet auch nötige Evaluierungsphase vor der eigentlichen Implementierung durchführen zu können.

Gut, also WebSockets. Aber womit?

Dies war die erste große Fragestellung mit der wir uns konfrontiert sahen. Leider gibt es im Java Enterprise Umfeld noch keinerlei aktiven Standard für die serverseitige Implementierung von WebSockets. Die Grundlagen dazu werden derzeit im JSR 356 gelegt, der sich, während dieser Artikel verfasst wird, im Status „Early Draft Review“ befindet. Aus diesem Grund bringen derzeit viele Java Server ihre eigenen – natürlich unterschiedlichen – Implementierungen mit. Da der von uns ursprünglich verwendete JBoss AS 7 hier nur eine Variante anbot, die sich in einem sehr frühen Entwicklungsstadium befand, sahen wir uns leider gezwungen nach Alternativen Ausschau zu halten. In die nähere Auswahl hatten wir folgende Server/Produkte genommen:

  • GlassFish 3
  • Jetty 8
  • Kaazing Websocket Gateway

Bei letzterem handelt es sich um ein, wie der Name schon sagt, Gateway, welches auf Basis des WebSocket Protokolls einen Transportkanal bereitstellt, an den im Prinzip beliebige Anwendungen (wie auch unsere Webapp im JBoss) zur Kommunikation angedockt werden können. Dieses sehr mächtige und flexible Produkt haben wir allerdings verworfen, da wir eine möglichst kompakte All-In-One Lösung präferierten. Diese hätte darüber hinaus den Vorteil, dass wir, sollten WebSockets einmal im Java Enterprise Umfeld zum aktiven Standard geworden sein, uns nicht genötigt sehen würden, diesen zusätzlichen Service wieder „auszubauen“.

Verblieben also noch als Application Server der GlassFish 3 und als reiner Servlet Container der Jetty 8. Genauere Evaluierungen hatten hier gezeigt, dass Jetty eine sehr schön gekapselte Umsetzung der WebSocket Technologie in Form von spezialisierten Servlets mitbrachte. GlassFish hingegen setzte hier auf seine, etwas „gröbere“, Grizzly Library. Den letztendlichen Ausschlag gab allerdings, dass eine Portierung im Fall eines kompletten Application Servers wahrscheinlich leichter fallen würde. Und so entschieden wir uns für den GlassFish.

Details der Umsetzung

Die erste Frage die sich nun einigen vielleicht stellt ist: „Warum WebSockets? Hätten Server-Sent Events (SSEs) nicht auch gelangt?“ Das stimmt natürlich. Bei der Anzeige von Spielständen findet nur eine Kommunikation vom Server zum Client statt. SSEs wären dafür ausreichend gewesen. Jedoch waren bereits die Implementierungsmöglichkeiten für WebSockets überaus spärlich dokumentiert. Desweiteren wollten wir uns ebenfalls die Möglichkeit offen lassen auch eine entgegengesetzte Kommunikation zu ermöglichen, selbst wenn wir sie vorerst noch nicht benötigen sollten.

Aber kommen wir nun zu den technischen Details. Eine Nutzung von WebSockets aus dem JavaScript des Frontends heraus ist relativ einfach möglich. Folgendes Code-Fragment zeigt, wie man eine Verbindung zum Server öffnet und eine Callback-Methode bereitstellt, die im Fall einer empfangenen Nachricht aufgerufen wird.

Das WebSocket-Objekt bietet noch weitere Methoden und Event-Handler. Aber für den Rahmen dieses Blogeintrages soll uns das vorerst genügen.

Die serverseitige Implementierung dagegen ist schon etwas komplexer. Wie schon angedeutet, ist die Kapselung der Funktionalität im GlassFish nicht so gut gelungen wie im Jetty, allerdings haben wir so die Möglichkeit bei Bedarf sehr tief in die programmatischen Details hinabzusteigen. Es stellt sich allerdings die Frage, wann dies wirklich jemals nötig sein sollte. Das folgende Code-Fragment zeigt (in vereinfachter Form), wie wir die serverseitige Implementierung vorgenommen haben. Es wird grundsätzlich eine Unterteilung in WebSocketApplications (die WebSockets bereitstellen und unter einer URL erreichbar sind) und den eigentlichen WebSockets vorgenommen.

Im Fall unserer Anwendung verwenden wir die von uns hinzugefügte Methode „notifyOpenSockets“, um dem Frontend bspw. einen geänderten Spielstand mitzuteilen. Insgesamt existieren in unserer Anwendung zwei unterschiedliche WebSocket Applications. Eine, wie bereits erwähnt, für die Aktualisierung der Spielstände und die zweite, um über Änderungen an der im WJAX-Release neu hinzugekommenen Warteliste zu informieren. Beide Applications sind über unterschiedliche URLs erreichbar.

Die Methode „isApplicationRequest“ dient dazu festzustellen, ob eine WebSocket Application für einen eingehenden Request verantwortlich ist. Sollte dies der Fall sein, muss die Methode ‚true‘ zurückgeben, anderenfalls ‚false‘. Der ein oder andere Leser mag sich an dieser Stelle über den etwas seltsam anmutenden Vergleich des Requests wundern. Wie wir während der Implementierung feststellen mussten, befinden wir uns an dieser Stelle leider außerhalb des Servlet Kontextes. Dies führt dazu, dass viele elegantere Abgleich-Methoden mit einer ClassCastException quittiert wurden und wir auf diese sehr rudimentäre Vorgehensweise zurückkamen.

Fazit

Abschließend kann man sagen, dass die Verwendung von WebSockets einen absoluten Mehrwert für unsere Anwendung darstellt, da nun, neben dem bemerkbaren Geschwindigkeitsvorteil, auch noch der Informationsfluß dem intuitiven Verständnis angepasst wurde.

Eine Erfahrung, die wir aus dieser Arbeit mitnehmen ist, dass man – gerade im Java Umfeld – sich bei der Verwendung dieser Technologie genaue Gedanken über seine evtl. bereits bestehende Anwendung machen sollte. Desweiteren sollte man sich genau überlegen, was man erreichen will, da man aufgrund (noch) fehlender Standardisierung, früher oder später auf eine Umsetzung festgelegt ist.

Was bleibt ist eine überaus wertvolle Erfahrung mit einer neuen und sehr spannenden Technologie. WebSockets werden (hoffentlich) bald eine größere Unterstützung und Verbreitung finden, da sie eine nicht zu unterschätzende Erleichterung im Bereich schnell reagierender Webanwendungen sind.


2 Kommentare

  • Stephan Müller

    26. Oktober 2015

    Hallo Michael, WebSocket ist ja mit Java EE 7 (2013) standardisiert worden, so dass es hier erstmal keine Neuerungen gibt und im Rahmen von Java EE 8 auch keine (allenfalls nur Kleinigkeiten) geben wird. Für gewisse Anforderungen (auch abseits des allgegenwärtigen Chat-Beispiels) ist WebSocket auf jeden Fall ein spannender Ansatz. Server-Side-Events beispielsweise bieten die Möglichkeit Informationen vom Server zum Client zu pushen - und damit eine schöne Möglichkeit sich ändernde Informationen und Zustände an einen Client zu übertragen. Bei weiteren Fragen wenden dich doch direkt an mich. Gruß Stephan

  • Michael

    26. Oktober 2015

    Hi, super Artikel. gibt es seit Erscheinen des Artikels Neuerungen bei der Implementierung von Websockets in Java? Würdet ihr diesen Weg aktuell auch noch gehen? Danke für die Rückmeldung. Gruß Michael

Kontakt

OPEN KNOWLEDGE GmbH
Poststraße 1
26122 Oldenburg