Utilizzo della compressione GZIP con Spring Boot / MVC / JavaConfig con RESTful


95

Usiamo Spring Boot / MVC con java-config basato su annotazioni per una serie di RESTfulservizi e vogliamo abilitare selettivamente la HTTP GZIPcompressione del flusso su alcune risposte API.

So di poterlo fare manualmente nel mio controller e a byte[] @ResponseBody, tuttavia preferiremmo fare affidamento sull'infrastruttura SpringMVC (filtri / ecc.) E fare in modo che esegua automaticamente la conversione e la compressione JSON (cioè il metodo restituisce un POJO).

Come posso abilitare la compressione GZIP nell'istanza ResponseBody o Tomcat incorporata e in un modo possiamo comprimere selettivamente solo alcune risposte?

Grazie!

PS .: Al momento non abbiamo alcuna configurazione basata su XML.


Dovresti controllare GzipFilter .
approxiblue

2
non utilizzare la compressione HTTP con HTTPS a meno che tu non sappia cosa stai facendo
Neil McGuigan

Risposte:


188

Il resto di queste risposte sono obsolete e / o esageratamente complicate per qualcosa che dovrebbe essere semplice IMO (da quanto tempo gzip è in circolazione? Più lungo di Java ...) Dai documenti:

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

Più vecchio di 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");
  }
}

Nota anche che funzionerà SOLO se stai eseguendo tomcat incorporato:

Se prevedi di eseguire la distribuzione su un tomcat non incorporato, dovrai abilitarlo in server.xml http://tomcat.apache.org/tomcat-9.0-doc/config/http.html#Standard_Implementation

Nota di produzione IRL:

Inoltre, per evitare tutto ciò, considera l'utilizzo di una configurazione proxy / bilanciatore del carico davanti a Tomcat con nginx e / o haproxy o simili poiché gestirà risorse statiche e gzip MOLTO in modo più efficiente e semplice rispetto al modello di threading di Java / Tomcat.

Non vuoi gettare il gatto nella vasca da bagno perché è impegnato a comprimere cose invece di servire le richieste (o più probabilmente a far girare thread / mangiare CPU / heap in attesa che si verifichi l'IO del database durante l'esecuzione della tua fattura AWS che è perché il tradizionale Java / Tomcat potrebbe non essere una buona idea per cominciare a seconda di quello che stai facendo, ma sto divagando ...)

rif: 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


Il tuo approccio per le versioni precedenti alla 1.2.2 non funzionerà poiché Spring Boot non cerca le TomcatConnectorCustomizeristanze nel contesto dell'applicazione; devono essere registrati programmaticamente conTomcatEmbeddedServletContainerFactory
Andy Wilkinson

Grazie per il testa a testa. Ho finito per rinunciare a questo dato che sembra che static / dynamic / tomcat / vs boot fosse ancora un problema. Questo è molto più difficile di quanto dovrebbe essere ... Proxy inverso Nginx FTW!
John Culviner

3
In SpringBoot, le nuove proprietà sono server.compression.enabled = true e server.compression.mime-types = XXX, YYY github.com/spring-projects/spring-boot/wiki/…
blacelle

2
Se per l'avvio primaverile abbiamo più controller di riposo che restituiscono tutti risposte JSON. Possiamo applicare selettivamente lo zip su alcuni controller?

3
Dovresti anche menzionare la dimensione minima della risposta per la compressione (es: 10KB) altrimenti diventa sovraccarico per il server comprimere ogni richiesta (es: 0,5KB). server.compression.min-response-size=10240
UsamaAmjad


12

Questa è fondamentalmente la stessa soluzione fornita da @ andy-wilkinson, ma a partire da Spring Boot 1.0 il metodo personalizza (...) ha un parametro ConfigurableEmbeddedServletContainer .

Un'altra cosa che è degno di nota è che Tomcat comprime solo i tipi di contenuto di text/html, text/xmle text/plainper impostazione predefinita. Di seguito è riportato un esempio che supporta anche la compressione di 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);
                        }
                    }
            );
        }
    };
}

Ho provato ad aggiungerlo alla mia configurazione Java e ho scoperto che la compressione non sembrava funzionare affatto. Sto usando Spring Boot con Tomcat come contenitore incorporato e mi chiedevo se ci fossero altre cose che dovevo impostare oltre a questa configurazione?
Michael Coxon

2
Prova a verificare specificando l' Accept-Encoding: gzip,deflateintestazione, se stai usando curl:curl -i -H 'Accept-Encoding: gzip,deflate' http://url.to.your.server
matsev

9

Spring Boot 1.4 Usa questo per Javascript HTML Json tutte le compressioni.

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

Come verifichiamo questa compressione?
Bhargav

@Bhargav Vedi l'intestazione della risposta della tua risposta API. Dovrebbe contenere l'intestazione: Content-Encoding:gzip
Sumit Jha


6

L'abilitazione di GZip in Tomcat non ha funzionato nel mio progetto Spring Boot. Ho usato CompressingFilter trovato qui .

@Bean
public Filter compressingFilter() {
    CompressingFilter compressingFilter = new CompressingFilter();
    return compressingFilter;
}

@ user1127860 tnx funziona ma comunque per configurare ulteriormente questo filtro? Uso lo spring boot e non riesco ad aggiungere i parametri di inizializzazione come dice il manuale in web.xml
Primavera

5

Per abilitare la compressione GZIP, è necessario modificare la configurazione dell'istanza Tomcat incorporata. Per fare ciò, dichiari un EmbeddedServletContainerCustomizerbean nella tua configurazione Java e quindi registri un TomcatConnectorCustomizercon esso.

Per esempio:

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

Consultare la documentazione di Tomcat per ulteriori dettagli sulle varie opzioni di configurazione della compressione disponibili.

Dici di voler abilitare selettivamente la compressione. A seconda dei criteri di selezione, l'approccio di cui sopra potrebbe essere sufficiente. Ti consente di controllare la compressione da parte dello user-agent della richiesta, la dimensione della risposta e il tipo MIME della risposta.

Se questo non soddisfa le tue esigenze, credo che dovrai eseguire la compressione nel tuo controller e restituire una risposta byte [] con un'intestazione di codifica del contenuto gzip.


1
qual è la differenza tra la tua risposta all'opzione di mettere l'impostazione su 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

Questa risposta è stata scritta prima che fosse disponibile la configurazione della compressione basata sulle proprietà. Sono equivalenti, ma l'approccio basato sulle proprietà è più semplice, quindi consiglierei di usarlo.
Andy Wilkinson

voglio solo condividere che nel mio caso tomcat è dietro un bilanciatore del carico che ottiene https e forword la richiesta a tomcat come http., quando uso la risposta della soluzione application.properties non è gzip ma quando uso la soluzione di configurazione programmatica sul connettore ottengo risposta gzip con richiesta https LB
lukass77

un'altra domanda nel caso in cui utilizzo la soluzione application.properties .. e definisco più 2 connettori sulle porte 8081 e 8082 .. la compressione si applica a tutti i connettori o solo al connettore 8080?
lukass77

lo verifico, la compersione si applica solo alla porta 8080, anche se apri più connettori .., penso che il bug dovrebbe essere aperto su questo per l'avvio a molla ?? .., quindi l'unica soluzione di lavoro per me era la configurazione programmatica per ciascun connettore, not application.properties
lukass77

0

Ho avuto lo stesso problema nel mio progetto Spring Boot + Spring Data quando ho invocato un file @RepositoryRestResource.

Il problema è il tipo MIME restituito; che è application/hal+json. L'aggiunta alla server.compression.mime-typesproprietà ha risolto questo problema per me.

Spero che questo aiuti a qualcun altro!

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.