Używanie kompresji GZIP z Spring Boot / MVC / JavaConfig z RESTful

97

Używamy Spring Boot / MVC z opartą na adnotacjach java-config dla serii RESTfulusług i chcemy selektywnie włączyć HTTP GZIPkompresję strumienia w niektórych odpowiedziach API.

Wiem, że mogę to zrobić ręcznie w moim kontrolerze i a byte[] @ResponseBody, jednak wolelibyśmy polegać na infrastrukturze SpringMVC (filtry / itp.) I automatycznie wykonywać konwersję i kompresję JSON (tj. Metoda zwraca POJO).

Jak mogę włączyć kompresję GZIP w instancji ResponseBody lub osadzonej Tomcat, aby w sposób selektywny kompresować tylko niektóre odpowiedzi?

Dzięki!

PS .: Obecnie nie mamy żadnej konfiguracji opartej na XML.

user3182614
źródło
Powinieneś sprawdzić GzipFilter .
ok.
2
nie używaj kompresji HTTP z HTTPS, chyba że wiesz, co robisz
Neil McGuigan

Odpowiedzi:

190

Pozostałe odpowiedzi są nieaktualne i / lub przesadnie skomplikowane dla czegoś, co powinno być prostym IMO (jak długo istnieje gzip? Dłużej niż Java ...) Z dokumentacji:

W 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 

W 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

Starsze niż 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");
  }
}

Zwróć również uwagę, że będzie to działać TYLKO, jeśli używasz osadzonego tomcat:

Jeśli planujesz wdrożyć na nie osadzonym tomcat, musisz go włączyć w server.xml http://tomcat.apache.org/tomcat-9.0-doc/config/http.html#Standard_Implementation

Uwaga dotycząca produkcji IRL:

Również, aby uniknąć tego wszystkiego, rozważ użycie konfiguracji proxy / load balancera przed Tomcat z nginx i / lub haproxy lub podobnym, ponieważ będzie on obsługiwał statyczne zasoby i gzip DUŻO wydajniej i łatwiej niż model wątków Java / Tomcat.

Nie chcesz wrzucać kota do wanny, ponieważ zajmuje się on kompresowaniem rzeczy zamiast obsługiwać żądania (lub bardziej prawdopodobne, że kręci wątki / zjada procesor / stertę, czekając na wystąpienie operacji we / wy bazy danych podczas uruchamiania rachunku AWS, który jest dlaczego tradycyjna Java / Tomcat może nie być dobrym pomysłem na początek, w zależności od tego, co robisz, ale dygresję ...)

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
źródło
Twoje podejście do wersji starszych niż 1.2.2 nie zadziała, ponieważ Spring Boot nie szuka TomcatConnectorCustomizerinstancji w kontekście aplikacji; muszą być programowo zarejestrowaneTomcatEmbeddedServletContainerFactory
Andy Wilkinson
Dzięki za ostrzeżenie. Skończyło się na tym, że zrezygnowałem z tego, ponieważ wydaje się, że statyczny / dynamiczny / tomcat / vs boot nadal był problemem. To jest o wiele trudniejsze niż powinno ... FTW zwrotnego proxy Nginx!
John Culviner,
3
W SpringBoot nowe właściwości to server.compression.enabled = true i server.compression.mime-types = XXX, YYY github.com/spring-projects/spring-boot/wiki/ ...
blacelle
2
Jeśli na potrzeby rozruchu wiosennego mamy wiele kontrolerów reszty, wszystkie zwracające odpowiedzi JSON. Czy możemy selektywnie zastosować zip na niektórych kontrolerach?
3
Należy również wspomnieć o minimalnym rozmiarze odpowiedzi dla kompresji (np .: 10 KB), w przeciwnym razie serwer będzie obciążał każde żądanie (np. 0,5 KB). server.compression.min-response-size=10240
UsamaAmjad
13

Najnowsze wersje w application.ymlkonfiguracji:

---

spring:
  profiles: dev

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

---
M. Reza Nasirloo
źródło
Odpowiedni raport o błędzie i poprawka tam, gdzie zostało to zmienione, to github.com/spring-projects/spring-boot/issues/2737
McLovin
12

Jest to w zasadzie to samo rozwiązanie, co @ andy-wilkinson, ale od wersji Spring Boot 1.0 metoda customizacji (...) ma parametr ConfigurableEmbeddedServletContainer .

Inna sprawa, że to warto wspomnieć, że Tomcat kompresuje tylko treści rodzajów text/html, text/xmla text/plaindomyślnie. Poniżej znajduje się przykład, który obsługuje również kompresję 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
źródło
Próbowałem dodać to do mojej konfiguracji Java i stwierdziłem, że kompresja w ogóle nie działa. Używam Spring Boot z Tomcat jako osadzonym kontenerem i zastanawiałem się, czy są jakieś dodatkowe rzeczy, które muszę ustawić poza tą konfiguracją?
Michael Coxon
2
Spróbuj zweryfikować, określając Accept-Encoding: gzip,deflatenagłówek, jeśli używasz curl:curl -i -H 'Accept-Encoding: gzip,deflate' http://url.to.your.server
matsev
9

Spring Boot 1.4 Użyj tego dla wszystkich kompresji JavaScript HTML Json.

server.compression.enabled: true
server.compression.mime-types: application/json,application/xml,text/html,text/xml,text/plain,text/css,application/javascript
Ronny Shibley
źródło
Jak weryfikujemy tę kompresję?
Bhargav
@Bhargav Zobacz nagłówek odpowiedzi swojej odpowiedzi API. Powinna ona zawierać nagłówek: Content-Encoding:gzip
Sumit Jha
6

Włączanie GZip w Tomcat nie działa w moim projekcie Spring Boot. Użyłem CompressingFilter znalezionego tutaj .

@Bean
public Filter compressingFilter() {
    CompressingFilter compressingFilter = new CompressingFilter();
    return compressingFilter;
}
user1127860
źródło
@ user1127860 tnx to działa, ale mimo wszystko do dalszej konfiguracji tego filtru? Używam rozruchu sprężynowego i nie mogę dodać parametrów inicjalizacji, zgodnie z instrukcją w pliku web.xml
wiosna
5

Aby włączyć kompresję GZIP, należy zmodyfikować konfigurację osadzonej instancji Tomcat. Aby to zrobić, deklarujesz EmbeddedServletContainerCustomizerkomponent bean w konfiguracji Java, a następnie rejestrujesz plikTomcatConnectorCustomizer go w nim.

Na przykład:

@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);
                }
            });
        }
    };
}

Więcej informacji na temat różnych dostępnych opcji konfiguracji kompresji można znaleźć w dokumentacji Tomcat .

Mówisz, że chcesz selektywnie włączyć kompresję. W zależności od kryteriów wyboru powyższe podejście może być wystarczające. Umożliwia sterowanie kompresją przez agenta użytkownika żądania, rozmiar odpowiedzi i typ MIME odpowiedzi.

Jeśli to nie spełnia twoich potrzeb, uważam, że będziesz musiał wykonać kompresję w kontrolerze i zwrócić odpowiedź byte [] z nagłówkiem kodowania treści gzip.

Andy Wilkinson
źródło
1
Jaka jest różnica między Twoją odpowiedzią na temat opcji umieszczenia ustawienia na application.properties? server.compression.enabled = true server.compression.mime-types = application / json, application / xml, text / html, text / xml, text / plain, application / javascript, text / css
lukass77
Ta odpowiedź została napisana przed udostępnieniem konfiguracji kompresji opartej na właściwościach. Są równoważne, ale podejście oparte na właściwościach jest łatwiejsze, więc polecam ich użycie.
Andy Wilkinson
chcę tylko udostępnić, że w moim przypadku tomcat znajduje się za modułem równoważenia obciążenia, który pobiera https i przekazuje żądanie do tomcat jako http., kiedy używam odpowiedzi rozwiązania application.properties, nie jest to gzip, ale kiedy używam programowego rozwiązania konfiguracyjnego na łączniku, otrzymuję odpowiedź gzip z żądaniem https LB
lukass77
kolejne pytanie w przypadku, gdy używam rozwiązania application.properties .. i definiuję więcej 2 złączy na portach 8081 i 8082 .. czy kompresja dotyczy wszystkich konektorów czy tylko złącza 8080?
lukass77
Wiem, że kompilacja jest stosowana tylko na porcie 8080, nawet jeśli otworzysz więcej złączy .. myślę, że błąd powinien być otwarty na tym, aby uruchomić sprężynowy rozruch ?? .., więc jedynym działającym rozwiązaniem dla mnie była konfiguracja programowa dla każdego złącza, nie application.properties
lukass77
0

Miałem ten sam problem w moim projekcie Spring Boot + Spring Data podczas wywoływania pliku @RepositoryRestResource.

Problem polega na zwróceniu typu MIME; który jest application/hal+json. Dodanie go do server.compression.mime-typesnieruchomości rozwiązało ten problem za mnie.

Mam nadzieję, że to pomoże komuś innemu!

Serginho
źródło