felberbauer_0

GWT: Zusammenfassen von Requests mit der RequestFactory

Oft wird zum Befüllen von User Interfaces eine Vielzahl verschiedener Daten vom Server benötigt. Auch beim Persistieren von Benutzereingaben kann es notwendig sein, mehrere Requests an den Server zu senden. Die Kommunikation zwischen Client und Server ist in GWT zwar asynchron, jedoch führt das Absenden eines jeden Requests in einem eigenen HTTP-Request zu einem merklichen Overhead. In diesem Artikel widmen wir uns der Vermeidung dieses Overheads und zeigen, wie sich eine beliebige Anzahl an Requests innerhalb desselben HTTP-Requests mithilfe der RequestFactory zusammenfassen lassen. 

Nehmen wir ein User Interface mit einigen Comboboxen als Beispiel, so wird deutlich, dass zum Befüllen dieser Comboboxen erst Daten vom Server geladen werden müssen und diese Daten zumeist auf verschiedene Entities verteilt sind. Je nach Komplexität der Maske kann sich dabei schnell eine größere Anzahl an Requests auf verschiedenen RequestContexts ergeben. Feuert man jeden dieser Requests nun einzeln ab, wird jedes Mal ein eigener HTTP-Request  erstellt und der Server antwortet wiederum mit einem HTTP-Response.

requestFactory.entityOneRequestContext().findSomething().fire(new Receiver<EntityOneProxy>() {...});
requestFactory.entityOneRequestContext().findMore().fire(new Receiver<List<EntityOneProxy>>() { ... });
requestFactory.entityTwoRequestContext().findFoo().fire(new Receiver<EntityTwoProxy>() { ... });
...

Der damit verbundene Aufwand lässt sich nun reduzieren, indem man die RequestFactory dazu bringt, mehrere GWT-Requests gebündelt in einem HTTP-Request zu übertragen.  

Requests am selben RequestContext zusammenfassen

Neben der Methode fire() bietet die Klasse Receiver<T> auch die Methode to() an, mit deren Hilfe zunächst nur der Receiver des Methodenaufrufs übergeben wird. Für mehrere Requests auf demselben RequestContext wiederholen wir diesen Schritt. Sind alle Requests auf dem RequestContext vorbereitet, können wir diese anschließend absenden.

EntityOneRequestContext context = requestFactory.entityOneRequestContext();  context.findSomething().to(new Receiver<EntityOneProxy>() { ... }); context.findMore().to(new Receiver<List<EntityOneProxy>>() { ... }); ... //Alle Requests im selben HTTP-Request absenden context.fire();

Jeder Request hat immer noch seinen eigenen Receiver, die Übertragung findet aber innerhalb desselben HTTP-Request statt.

Requests verschiedener RequestContexts zusammenfassen

Auf ähnliche Weise lassen sich auch mehrere RequestContexts verknüpfen, sodass beliebige Requests gesammelt übertragen werden können.
Dazu bedienen wir uns der Methode append() der Klasse RequestContext. Diese Methode nimmt einen RequestContext als Parameter, setzt den State auf den eigenen State und gibt den RequestContext im Anschluss wieder zurück.

EntityOneRequestContext contextOne = requestFactory.entityOneRequestContext(); 
EntityTwoRequestContext contextTwo = contextOne.append(requestFactory.entityTwoRequestContext());

Hier ist zu beachten, dass mit dem von append() zurückgegeben RequestContext gearbeitet werden muss und nicht mit dem Argument von append().
Nachdem wieder alle Requests auf den verknüpften RequestContexts vorbereitet wurden, wird auf einem dieser RequestContexts wieder fire()aufgerufen. Dabei ist es egal, auf welchem dies geschieht, es werden immer alle Requests der verbundenen RequestContexts abgesendet. 

contextOne.findSomething().to(new Receiver<EntityOneProxy>() { ... });
contextOne.findMore().to(new Receiver<List<EntityOneProxy>>() { ... });
contextTwo.findSomething().to(new Receiver<EntityTwoProxy>() { ... });
...
//Alle Requests aller verknuepfter RequestContext abfeuern
contextOne.fire(); //gleichbedeutend mit contextTwo.fire();
Verarbeitung der Requests am Server

Sendet man mehrere Requests unabhängig voneinander zum Server, so werden diese multithreaded abgearbeitet. Durch das Zusammenfassen der Requests werden alle gleichzeitig übertragen und in Folge auch vom selben Thread verarbeitet. Dadurch gehen zwar die Vorteile des Multithreading verloren, die Vorteile des geringeren Übertragungsaufwandes und der damit einhergehende Geschwindigkeitsgewinn überwiegen jedoch. Auch beim Zusammenfassen mehrerer Requests empfiehlt sich die Verwendung des in diesem Artikel beschriebenen CountdownLatch, um fortzufahren zu können sobald alle Requests erledigt sind. 

Welche Konzepte verwendest du für eine effiziente Client-Server Kommunikation in GWT? Welche Features der RequestFactory hast du sonst noch im Einsatz? Wir freuen uns auf deine Kommentare. 

Florian Felberbauer
(Software Developer)