Verwenden der GZIP-Komprimierung mit Spring Boot / MVC / JavaConfig mit RESTful

95

Wir verwenden Spring Boot / MVC mit annotationsbasierter Java-Konfiguration für eine Reihe von RESTfulDiensten und möchten die HTTP GZIPStream-Komprimierung für einige API-Antworten selektiv aktivieren .

Ich weiß, dass ich dies manuell in meinem Controller und einem Controller byte[] @ResponseBodyausführen kann. Wir würden uns jedoch lieber auf die SpringMVC-Infrastruktur (Filter / usw.) verlassen und diese automatisch die JSON-Konvertierung und -Komprimierung durchführen lassen (dh die Methode gibt ein POJO zurück).

Wie kann ich die GZIP-Komprimierung in der ResponseBody- oder eingebetteten Tomcat-Instanz aktivieren und so können wir nur einige Antworten selektiv komprimieren?

Vielen Dank!

PS.: Wir haben derzeit keine XML-basierte Konfiguration.

user3182614
quelle
Sie sollten GzipFilter auschecken .
ungefähr
2
Verwenden Sie keine HTTP-Komprimierung mit HTTPS, es sei denn, Sie wissen, was Sie tun
Neil McGuigan

Antworten:

188

Der Rest dieser Antworten ist veraltet und / oder übertrieben kompliziert für etwas, das einfach sein sollte IMO (wie lange gibt es gzip schon? Länger als Java ...) Aus den Dokumenten:

In application.properties 1.3+

# 🗜️🗜️🗜️
server.compression.enabled=true
# opt in to content types
server.compression.mime-types=application/json,application/xml,text/html,text/xml,text/plain,application/javascript,text/css
# not worth the CPU cycles at some point, probably
server.compression.min-response-size=10240 

In application.properties 1.2.2 - <1.3

server.tomcat.compression=on
server.tomcat.compressableMimeTypes=application/json,application/xml,text/html,text/xml,text/plain,application/javascript,text/css

Älter als 1.2.2:

@Component
public class TomcatCustomizer implements TomcatConnectorCustomizer {

  @Override
  public void customize(Connector connector) {
    connector.setProperty("compression", "on");
    // Add json and xml mime types, as they're not in the mimetype list by default
    connector.setProperty("compressableMimeType", "text/html,text/xml,text/plain,application/json,application/xml");
  }
}

Beachten Sie auch, dass dies NUR funktioniert, wenn Sie Embedded Tomcat ausführen:

Wenn Sie die Bereitstellung auf einem nicht eingebetteten Tomcat planen, müssen Sie diese in server.xml http://tomcat.apache.org/tomcat-9.0-doc/config/http.html#Standard_Implementation aktivieren

IRL-Produktionshinweis:

Um all dies zu vermeiden, sollten Sie ein Proxy / Load Balancer-Setup vor Tomcat mit nginx und / oder haproxy oder ähnlichem verwenden, da es statische Assets und gzip VIEL effizienter und einfacher handhabt als das Threading-Modell von Java / Tomcat.

Sie möchten die Katze nicht in die Badewanne werfen, weil sie damit beschäftigt ist, Dinge zu komprimieren, anstatt Anforderungen zu bedienen (oder eher Threads zu spinnen / CPU / Heap zu essen, während Sie Ihre AWS-Rechnung ausführen) warum traditionelles Java / Tomcat vielleicht keine gute Idee ist, je nachdem, was Sie tun, aber ich schweife ab ...)

Refs: https://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/html/howto.html#how-to-enable-http-response-compression

https://github.com/spring-projects/spring-boot/issues/2031

John Culviner
quelle
Ihr Ansatz für Versionen älter als 1.2.2 funktioniert nicht, da Spring Boot nicht nach TomcatConnectorCustomizerInstanzen im Anwendungskontext sucht . Sie müssen programmatisch registriert werden beiTomcatEmbeddedServletContainerFactory
Andy Wilkinson
Danke für die Warnung. Am Ende gab ich dies auf, da es scheint, dass statisch / dynamisch / tomcat / vs boot immer noch ein Problem war. Dies ist viel schwieriger als es sein sollte ... Nginx Reverse Proxy FTW!
John Culviner
3
In SpringBoot sind die neuen Eigenschaften server.compression.enabled = true und server.compression.mime-types = XXX, YYY github.com/spring-projects/spring-boot/wiki/…
blacelle
2
Wenn wir für den Frühlingsstart mehrere Rest-Controller haben, die alle JSON-Antworten zurückgeben. Können wir die Zip-Datei selektiv auf einige Controller anwenden?
3
Sie sollten auch die minimale Antwortgröße für die Komprimierung angeben (z. B. 10 KB), da der Server sonst jede (z. B. 0,5 KB) Anforderung komprimieren muss. server.compression.min-response-size=10240
UsamaAmjad
13

In den letzten Versionen in der application.ymlKonfiguration:

---

spring:
  profiles: dev

server:
  compression:
    enabled: true
    mime-types: text/html,text/css,application/javascript,application/json

---
M. Reza Nasirloo
quelle
Der entsprechende Fehlerbericht und Fix, wo dies geändert wurde, ist github.com/spring-projects/spring-boot/issues/2737
McLovin
12

Dies ist im Grunde die gleiche Lösung wie bei @ andy-wilkinson, aber ab Spring Boot 1.0 verfügt die Methode customize (...) über den Parameter ConfigurableEmbeddedServletContainer .

Eine andere erwähnenswerte Sache ist, dass Tomcat nur Inhaltstypen von komprimiert text/html , text/xmlund text/plainstandardmäßig aktiviert . Unten sehen Sie ein Beispiel, das auch die Komprimierung von unterstützt application/json:

@Bean
public EmbeddedServletContainerCustomizer servletContainerCustomizer() {
    return new EmbeddedServletContainerCustomizer() {
        @Override
        public void customize(ConfigurableEmbeddedServletContainer servletContainer) {
            ((TomcatEmbeddedServletContainerFactory) servletContainer).addConnectorCustomizers(
                    new TomcatConnectorCustomizer() {
                        @Override
                        public void customize(Connector connector) {
                            AbstractHttp11Protocol httpProtocol = (AbstractHttp11Protocol) connector.getProtocolHandler();
                            httpProtocol.setCompression("on");
                            httpProtocol.setCompressionMinSize(256);
                            String mimeTypes = httpProtocol.getCompressableMimeTypes();
                            String mimeTypesWithJson = mimeTypes + "," + MediaType.APPLICATION_JSON_VALUE;
                            httpProtocol.setCompressableMimeTypes(mimeTypesWithJson);
                        }
                    }
            );
        }
    };
}
matsev
quelle
Ich habe versucht, dies in meine Java-Konfiguration aufzunehmen, und festgestellt, dass die Komprimierung anscheinend überhaupt nicht funktioniert. Ich verwende Spring Boot mit Tomcat als eingebettetem Container und habe mich gefragt, ob ich außer dieser Konfiguration noch weitere Einstellungen vornehmen muss.
Michael Coxon
2
Versuchen Sie zu überprüfen, indem Sie den Accept-Encoding: gzip,deflateHeader curl -i -H 'Accept-Encoding: gzip,deflate' http://url.to.your.server
angeben
9

Spring Boot 1.4 Verwenden Sie dies für Javascript HTML Json alle Komprimierungen.

server.compression.enabled: true
server.compression.mime-types: application/json,application/xml,text/html,text/xml,text/plain,text/css,application/javascript
Ronny Shibley
quelle
Wie überprüfen wir diese Komprimierung?
Bhargav
@Bhargav Siehe den Antwortheader Ihrer API-Antwort. Es sollte den Header enthalten: Content-Encoding:gzip
Sumit Jha
6

Das Beschriften von GZip in Tomcat hat in meinem Spring Boot-Projekt nicht funktioniert. Ich habe CompressingFilter verwendet, das hier zu finden ist .

@Bean
public Filter compressingFilter() {
    CompressingFilter compressingFilter = new CompressingFilter();
    return compressingFilter;
}
user1127860
quelle
@ user1127860 tnx das funktioniert aber trotzdem um diesen filter weiter zu konfigurieren? Ich benutze Spring Boot und kann nicht scheinen, Init-Parameter hinzuzufügen, wie im Handbuch in web.xml
Spring
5

Um die GZIP-Komprimierung zu aktivieren, müssen Sie die Konfiguration der eingebetteten Tomcat-Instanz ändern. Dazu deklarieren Sie eine EmbeddedServletContainerCustomizerBean in Ihrer Java-Konfiguration und registrieren eine TomcatConnectorCustomizerbei dieser.

Beispielsweise:

@Bean
public EmbeddedServletContainerCustomizer servletContainerCustomizer() {
    return new EmbeddedServletContainerCustomizer() {
        @Override
        public void customize(ConfigurableEmbeddedServletContainerFactory factory) {
            ((TomcatEmbeddedServletContainerFactory) factory).addConnectorCustomizers(new TomcatConnectorCustomizer() {
                @Override
                public void customize(Connector connector) {
                    AbstractHttp11Protocol httpProtocol = (AbstractHttp11Protocol) connector.getProtocolHandler();
                    httpProtocol.setCompression("on");
                    httpProtocol.setCompressionMinSize(64);
                }
            });
        }
    };
}

Weitere Informationen zu den verschiedenen verfügbaren Komprimierungskonfigurationsoptionen finden Sie in der Tomcat-Dokumentation .

Sie sagen, dass Sie die Komprimierung selektiv aktivieren möchten. Abhängig von Ihren Auswahlkriterien kann der oben beschriebene Ansatz ausreichend sein. Sie können die Komprimierung über den Benutzeragenten der Anforderung, die Größe der Antwort und den MIME-Typ der Antwort steuern.

Wenn dies nicht Ihren Anforderungen entspricht, müssen Sie meines Erachtens die Komprimierung in Ihrem Controller durchführen und eine Byte [] -Antwort mit einem gzip-Inhaltscodierungsheader zurückgeben.

Andy Wilkinson
quelle
1
Was ist der Unterschied zwischen Ihrer Antwort auf die Option, die Einstellung auf application.properties zu setzen? server.compression.enabled = true server.compression.mime-types = application / json, application / xml, text / html, text / xml, text / plain, application / javascript, text / css
lukass77
Diese Antwort wurde geschrieben, bevor eine eigenschaftsbasierte Komprimierungskonfiguration verfügbar war. Sie sind äquivalent, aber der auf Eigenschaften basierende Ansatz ist einfacher, daher würde ich die Verwendung empfehlen.
Andy Wilkinson
Ich möchte nur mitteilen, dass Tomcat in meinem Fall hinter einem Load Balancer steht, der https erhält und die Anfrage an Tomcat als http gzip Antwort mit https Anfrage LB
lukass77
Eine weitere Frage für den Fall, dass ich die application.properties-Lösung verwende und mehr als 2 Anschlüsse an den 8081- und 8082-Ports definiere. Wird die Komprimierung auf alle Konnektoren oder nur auf den 8080-Anschluss angewendet?
lukass77
Ich verifiziere dies, Compersion wird nur auf Port 8080 angewendet, selbst wenn Sie mehr Konnektoren öffnen. nicht application.properties
lukass77
0

Ich hatte das gleiche Problem in meinem Spring Boot + Spring Data-Projekt beim Aufrufen von a @RepositoryRestResource.

Das Problem ist der zurückgegebene MIME-Typ. das ist application/hal+json. Das Hinzufügen zur server.compression.mime-typesEigenschaft löste dieses Problem für mich.

Hoffe das hilft jemand anderem!

Serginho
quelle