Korrelations-ID mit Javascript Client und Grails REST Backend

Bei mehrfachen asynchronen Funktionsaufrufen ist nicht klar, welche Antwort zu welchem Aufruf gehört. Insbesondere wenn sich Aufrufe überholen, treten kuriose Effekte auf. Um dieses Verhalten zu verhindern, gibt es in den Enterprise Integration Patterns den Correlation Identifier. Dazu wird bei jedem Aufruf eine eindeutige Kennung (Correlation ID) mitgegeben, die der Angefragte in der Antwort wieder mit zurückschickt. So kann man auf Client-Seite die Antwort zu dem dazugehörigem Aufruf zuordnen.

Natürlich macht es keinen Sinn, die Correlation ID bei jedem Aufruf manuell mitzuschicken. Der Code würde dann viele Redundanzen enthalten und wäre weder gut les- und zudem schlecht wart- und erweiterbar. Vielmehr handelt es sich um einen Querschnittsbelang (cross cutting concern) und der sollte mit aspektorientierter Programmierung gelöst werden. Bei Webanwendungen (z. B. REST-Services) bieten sich in der Java-Welt Servlet-Filter an. In Grails ist die Umsetzung relativ einfach:

class RestFilters {
	def filters = {
		returnCorrelationId(controller: '*Rest*', action: '*') {
			before = { Map model ->
				response.setHeader('correlationId', request.getHeader('correlationId'))
			}
		}
	}
}

Die Klasse RestFilters legt man unter grails-app/config ab. Grundsätzlich können dort mehrere Filter definiert werden, wir betrachten hier aber nur den Filter “returnCorrelationId”. Der soll bei allen REST-Controller-Zugriffen (controller: ‘Rest‘, action: ‘*’) aufgerufen werden. Grundsätzlich kann man je Filter vor und/oder nach dem umschlossenen Controller Code ausführen. In diesem Beispiel muss man diese Aktion vor dem eigentlichen Controller-Aufruf durchführen, da hinterher der Response zwar noch verändert werden kann, die Änderung aber keine Auswirkung mehr hat (siehe GRAILS-10806). Die eigentliche Logik ist relativ schlank, man holt aus dem Servlet-Request aus einem bestimmten Header die Correlation-ID und packt sie wiederum in eine Header-Variable des Response.

Damit wird auf der Serverseite bei jedem Aufruf eines REST-Controllers die Correlation-ID zentral aus dem Request gelesen und im Response zurückgeschickt. Auf der Clientseite könnte eine Umsetzung dann folgendermaßen aussehen. Wir verwenden hier JavaScript mit jQuery:

        correlationId = new Date().getTime();
        jQuery.ajax("rest/persons?search=" + event.node.value + "&max=20", {headers:{correlationId:correlationId}})
        [...]

In diesem Beispiel wird als Correlation ID einfach ein Zeitstempel verwendet, was in diesem Szenario vollkommen ausreichend ist. Der Zweck dieser Implementierung ist das Zuordnen von Requests innerhalb einer Autovervollständigungskomponente. Da durch das Tippen in relativ kurzer Zeit ständig neue Request abgeschickt werden, überholen sich diese Anfragen zum Teil. So kommt beispielsweise ein Response zu einem Request später zurück, als ein Response zu einem später abgeschickten Request. Das verfälscht natürlich die Suchergebnisse, weil die vorgeschlagenen Begriffe nicht mehr zur Suche passen. Durch das Mitsenden der Correlation-ID kann man sich den zuletzt abgesetzten Request merken und alle nicht dazugehörigen Antworten einfach ignorieren.

Natürlich ist ein Zeitstempel in anderen Szenarios nicht ausreichend. Da sollte man dann über die Erzeugung einer weltweit eindeutigen ID nachdenken. Für Javascript gibt es die Bibliothek UUID-JS, die Erzeugung sieht in etwa folgendermaßen aus:

        var UUIDjs = require('uuid-js');
        var correlationId = UUIDjs.create().toString();
Short URL for this post: http://wp.me/p4nxik-2cM
This entry was posted in Groovy and Grails, Other languages for the Java VM, SOA / Webservices and tagged , , , , , . Bookmark the permalink.

Leave a Reply