GWT Wie kann ich die Größe von Code-Serialisierern für RPC-Aufrufe reduzieren?

8

Ich habe festgestellt, dass mehr als 60% des von GWT in meiner Anwendung generierten JavaScript-Codes für RPC-Serialisierer bestimmt sind. Auch habe ich festgestellt, dass Serialisierer nicht zwischen Service-Schnittstellen geteilt werden, ich meine, wenn ich zum Beispiel AccountDTO-Typ auf 2 RPC-Service-Schnittstellen referenziert habe, bekomme ich 2 Serializer-Klassen statt 1 für den gleichen Typ. Um die Größe des kompilierten Codes zu reduzieren, dachte ich, dass ich vielleicht Deferred Binding verwenden könnte, um alle Serviceschnittstellen zu ersetzen, die ich für eine große Schnittstelle habe. Wenn das möglich wäre, dann wird GWTCompiler vielleicht nur einen AccountDTO Serializer anstelle von 2 produzieren.

Ich bin mir nicht sicher, ob das eine gute Idee ist oder ob es eine bessere Lösung für mein Problem gibt.

Was ich zu implementieren versuchte, war etwa so:

%Vor%

Aber im Moment habe ich den Fehler erhalten:

[FEHLER] Fehler in 'Datei: / C: /Users/Daniel/EclipseWorkspace/ADK/src/com/arballon/gwt/core/client/FinancialService.java'       [FEHLER] Zeile 31: Rebind-Ergebnis 'com.arballon.gwt.core.client.GenericService' konnte nicht gefunden werden

Irgendwelche Gedanken über das Thema werden geschätzt. Grüße

Daniel

    
Daniel Ardison 21.07.2011, 15:32
quelle

5 Antworten

3

Der GWT-RPC-Generierungscode baut mehrere Klassen auf, um seine Arbeit zu erledigen, wie Sie festgestellt haben: a *_FieldSerializer für jeden Typ, der über die Leitung geht, und% *_Proxy -Klasse für den asynchronen RemoteService-Typ. Dieser Proxy-Typ benötigt ein *_TypeSerializer , welches die Wurzel Ihres Problems ist - aus irgendeinem Grund verdrahtet GWT alle Serialisierungs- / Deserialisierungsmethoden in einer String- & gt; js Funktionskarte, wahrscheinlich um schnelle Suchvorgänge zu ermöglichen - aber dieses Setup Code kostet Codezeilen, die im endgültigen Build enthalten sein müssen. Ein optimierterer Ansatz könnte sein, dass jede FieldSerializer eine Registrierungsmethode hat, bei der sie ihre Methoden der statischen Karte des Proxys hinzufügt - dies wird jedoch geplagt, aber die Optimierung von GWT versucht nicht instantiate() , deserialize() und serialize() Methoden, wenn es nicht erscheint, werden sie aufgerufen.

Ihr Problem rührt von vielen Typen her, die serialisiert werden können, und nachdem Sie versucht haben, RemoteService -Typen zu erstellen, die jeweils bestimmte Funktionseinheiten beschreiben, aber viele Modelltypen wiederverwenden. Bewundernswertes Ziel, vor allem, weil es wahrscheinlich Ihren serverseitigen Code schöner aussehen lässt, aber offensichtlich beißt GWT Sie dafür.

Die Lösung, die ich Ihnen auf freenode anbieten wollte (wie niloc132), bestand darin, einen einzelnen großen RemoteService -Typ, den Sie GeneralService nannten, und einen passenden GeneralServiceAsync zu erstellen, wobei alle vorhandenen RPC-Diensttypen erweitert wurden . Mein erster Gedanke war, ein <replace-with> zu verwenden, um dem Generatorsystem mitzuteilen, dass, wenn jeder RemoteService-Typ es durch GeneralService ersetzen soll, dies aber keinen Sinn ergibt - GWT übergibt keine Rebind-Ergebnisse zurück in sich selbst, um weiterhin Nachschauen zu machen. Stattdessen würde ich Folgendes vorschlagen: Wenn Sie einen Service-asynchronen Typ wünschen, gehen Sie folgendermaßen vor:

%Vor%

Das Rebind-Ergebnis von GeneralService implementiert GeneralServiceAsync , was wiederum AccountingServiceAsync zuweisbar ist. Wenn Speicher verwendet wird, haben Sie angegeben, dass Sie über statische Methoden / Felder verfügen, die diese Dienste bereitstellen. Ändern Sie diese Websites so, dass immer eine GeneralServiceAsync -Instanz erstellt wird. Solange Sie GWT.create für keinen RemoteService Subtyp, sondern GeneralService aufrufen, begrenzen Sie die Anzahl von TypeSerializers auf eins.

Als Nebenbemerkung sind die RemoteServiceProxy Subtypen zustandslos. Wenn Sie sicherstellen, dass Sie nur eine Instanz erstellen, ist es zwar einfacher, konsistent zu bauen, aber es wird kein Laufzeitspeicher oder Zeit gespart, da sie fast sicher in statischen Methoden kompiliert werden . Die *_TypeSerializer -Klassen haben jedoch einen Zustand, aber es gibt nur jeweils eine Instanz. Wenn Sie also alle Ihre RemoteService s zusammenfassen, wird möglicherweise sehr wenig Arbeitsspeicher gespart.

    
Colin Alworth 24.07.2011, 21:14
quelle
2

Nun, nach ein paar Rundreisen haben wir endlich eine Lösung für unser Problem gefunden, mit dem ich teilen möchte, falls es anderen helfen könnte. Zuerst muss ich die Hilfe von Colin Alworth erwähnen, ohne diese Unterstützung wäre diese Lösung überhaupt nicht möglich. Ich muss auch erwähnen, dass ich nicht wirklich stolz auf die endgültige Lösung bin, aber es funktioniert für uns und im Moment ist es das Beste, was wir haben.

Was wir letztendlich getan haben, war, wie Colin im letzten Beitrag bemerkt hat, das GWT.create von jeder unserer Service-Schnittstellen zu ersetzen, um stattdessen die GenericBigService-Schnittstelle zu erstellen.

Also unser erster Patch geht so:

1) Create GenericBigService-Schnittstelle, die alle Service-Schnittstellen erweitert, die wir haben (zur Zeit 52 Schnittstellen), und auch seinen Async-Bruder erstellen. Wir haben das durch ein Phytom-Skript gemacht.

So sieht unser GenericBigInterface folgendermaßen aus:

%Vor%

2) Wir haben eine interne innere statische Klasse in jeder Service-Schnittstelle, um die Async-Instanz zu installieren, in der wir GWT.create ersetzen, um das GenericBigInterface zu erstellen.

Eine unserer Service-Schnittstellen sieht so aus:

%Vor%

wir müssen den serServiceEntyPoint-Aufruf durchführen, um unsere web.xml unverändert zu erhalten.

Wenn wir das zum ersten Mal kompilieren, kompiliert es ok, aber es funktioniert nicht, weil der Serveraufruf zur Laufzeit eine Ausnahme auslöst:

%Vor%

, das nicht von FinancialPeriodBalanceCategoryService

implementiert wird

Nun, das war absolut richtig, wir rufen den Dienst mit einer Schnittstelle an, die er nicht implementiert, und hier kommt der hässliche Teil herein. Wir konnten im Moment keine bessere Lösung finden, die wir codieren können, und die, die wir beschlossen haben, lautet:

Wir ersetzen RPC.java durch unsere eigene Kopie und ersetzen den Code wie folgt:

in der decodeRequest-Methode, die wir gemacht haben:

%Vor%

Der Vorteil dabei war:

1) Wir brauchten 1 Stunde und 20 Minuten, um für 20 Permutationen nur 20 Minuten zu machen.

2) In devMode startet alles schneller. Das Startup bleibt mehr oder weniger das gleiche, aber die Ausführung nach dem Start geht wirklich gut.

3) Die Reduzierung der Kompilierungsgröße war ein anderes, nicht geringes, interessantes Ergebnis, wir reduzieren das übrig gebliebene Segment von 6Mb auf 1,2Mb, wir reduzieren die gesamte Kompilation der JS Größe in ca. 50% bis 60%.

Wir sind wirklich glücklich mit GWT-RPC und wollen es nicht lassen, aber typeSerializers war wirklich ein Problem, hauptsächlich wegen der Größe des JS, die daraus resultiert. Mit dieser Lösung, ich weiß, ist nicht sehr elegant, aber es funktioniert, und es funktioniert Rost. Danke nochmal Colin für deine Hilfe!

Grüße Daniel

    
Daniel Ardison 05.09.2011 15:02
quelle
1

GWt generiert für jeden GWT-RPC-Dienst einen Proxy, einen TypeSerializer. Und für jedes Objekt, das möglicherweise über GWT übergeben werden kann, gibt es eine FieldSerializer-Klasse. Und es kann nur einen FieldSerializer pro Klasse geben. Es gibt also keine Möglichkeit, zwei FieldSerializer für ein AccountDTO zu haben.

Die abgelehnte Bindungsregel, die Sie verwenden möchten, funktioniert nicht. Zum Beispiel hast du so etwas: MyServiceAsync-Synchronisierung = GWT.create (MyService.class);

Verzögerte Bindungsregeln ändern es in:

MyServiceAsync sync = neu MyServiceAsync_Proxy ();

Ihre Regeln werden tatsächlich so etwas tun:

MyServiceAsync sync = new MyGenericService (); // nicht gültig, da MyGenericService eine Schnittstelle ist

So wird Ihre Lösung nicht funktionieren.

Da Sie sagen, dass 60% Ihres generierten Anwendungscodes RPC-bezogene Daten sind, vermute ich, dass Sie ein Problem mit der RPC-Typexplosion haben.

Überprüfen Sie, ob GWT während der Kompilierung keine Warnungen auslöst oder Stubs für RPC TypeSerializers generiert, höchstwahrscheinlich haben Sie eine sehr häufige Schnittstelle im Dienst.

    
jusio 21.07.2011 17:04
quelle
1

Wenn Sie eine bessere Lösung wünschen, verwenden Sie ein Befehlsmuster. Auf diese Weise benötigen Sie nur einen GWT-Dienst, der einen Befehlssubtyp akzeptiert und einen Ergebnistyp zurückgibt (Sie können ihn typsicher machen, indem Sie Generika verwenden).

Das Schöne ist, dass Sie nur eine Methode in einem gwt-Servlet deklarieren müssen und von dort aus können Sie zu jedem anderen serverseitigen Dienst weiterleiten.

Das Befehlsmuster kann Ihnen auch einen großen zusätzlichen Nutzen bringen, da Sie einen zentralen Kontrollpunkt haben, um Sicherheitsüberprüfungen durchzuführen, oder Ihnen erlaubt, Anfragen transparent zu stapeln

Ihre Exposition gegenüber GWT wird somit auf der Serverseite viel kleiner.

    
David Nouls 14.02.2013 13:46
quelle
0

Soweit ich verstehe, soll die GWT-Codegenerierung konkrete Implementierungen einer Schnittstelle liefern. Diese Implementierung wird dann in Javascript für bestimmte Permutationen umgewandelt.

Auf der anderen Seite ersetzt Ihre Probe eine Schnittstelle durch die andere. Wenn Sie es aus den Augen des GWT-Compilers sehen, werden Sie vielleicht das Problem mit dieser Konfiguration sehen.

Angenommen, Sie sind der GWT-Compiler und Sie sehen folgende Zeile im clientseitigen Code, den Sie in JavaScript konvertieren

%Vor%

Sie müssen also herausfinden, was in Zeile 2 passieren soll. Insbesondere müssen Sie wissen, wo Sie die Implementierung von accountingServiceAsync.recordTransaction () finden. Sie suchen also in Ihrer gesamten Konfiguration nach einer Regel, die angibt, welche Implementierungsklasse für AccountingService (nicht Async) verwendet werden soll. Aber leider findest du keine. Aber dann merkt man, dass AccountingService auch ein RemoteService ist. Sie tauchen also wieder in Ihre Konfiguration ein. Und, aha, da ist es, eine Regel, die besagt, dass Sie mit ServiceInterfaceProxyGenerator RemoteService-Implementierungen generieren können . Sie übergeben gerne die Aufgabe, ServiceInterfaceProxyGenerator eine Implementierung von AccountingService bereitzustellen.

Angenommen, anstelle dieses Happy Ends wird in Ihrer Konfiguration angegeben, dass AccountingService durch GenericService, und du sagst: "Hey cool, bring es an". Aber dann finden Sie heraus, dass GenericService auch eine Schnittstelle ist. Offensichtlich werden Sie ausgeschaltet und sagen: "Nun, was werde ich mit einer anderen Schnittstelle tun, alles, was ich brauchte, war eine Implementierung von AccountingService". An diesem Punkt würden Sie sogar mit dem Programmierer kommen wollen, indem Sie einen kryptischen Fehler auf ihn werfen.

Also, all das erklärt, warum Ihre Lösung (theoretisch) nicht funktioniert. Was Ihr eigentliches Anliegen von aufgeblähtem Javascript betrifft, bin ich erstaunt, dass dieses Problem sogar existiert angesichts des Aufwands, den GWT-Leute bei der Optimierung des kompilierten JavaScript einsetzen. Wie haben Sie Ihre kompilierte Ausgabe für die Duplizierung getestet?

    
Tahir Akhtar 21.07.2011 16:58
quelle

Tags und Links