Usando compactação GZIP com Spring Boot / MVC / JavaConfig com RESTful

95

Usamos Spring Boot / MVC com java-config baseado em anotação para uma série de RESTfulserviços e queremos habilitar seletivamente a HTTP GZIPcompressão de fluxo em algumas respostas da API.

Eu sei que posso fazer isso manualmente no meu controlador e um byte[] @ResponseBody, no entanto, preferimos contar com a infraestrutura SpringMVC (filtros / etc) e fazer com que ele faça a conversão e compactação JSON automaticamente (ou seja, o método retorna um POJO).

Como posso ativar a compactação GZIP no ResponseBody ou na instância incorporada do Tomcat e, de uma forma, podemos compactar seletivamente apenas algumas respostas?

Obrigado!

PS .: Atualmente não temos nenhuma configuração baseada em XML.

user3182614
fonte
Você deve verificar GzipFilter .
aproximadamente
2
não use compactação HTTP com HTTPS a menos que saiba o que está fazendo
Neil McGuigan

Respostas:

188

O restante dessas respostas está desatualizado e / ou excessivamente complicado para algo que deveria ser simples IMO (há quanto tempo o gzip existe agora? Há mais tempo do que o Java ...) Dos documentos:

Em 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 

Em 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

Anterior a 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");
  }
}

Observe também que isso SÓ funcionará se você estiver executando o tomcat incorporado:

Se você planeja implantar em um tomcat não integrado, deverá habilitá-lo em server.xml http://tomcat.apache.org/tomcat-9.0-doc/config/http.html#Standard_Implementation

Nota de produção IRL:

Além disso, para evitar tudo isso, considere o uso de uma configuração de proxy / balanceador de carga na frente do Tomcat com nginx e / ou haproxy ou similar, uma vez que ele manipulará ativos estáticos e gzip MUITO de forma mais eficiente e fácil do que o modelo de threading Java / Tomcat.

Você não quer jogar o gato na banheira porque ele está ocupado comprimindo coisas em vez de servir solicitações (ou mais provavelmente girando threads / comendo CPU / heap sentado esperando que o banco de dados IO ocorra enquanto aumenta sua conta da AWS, que é porque o Java / Tomcat tradicional pode não ser uma boa ideia para começar, dependendo do que você está fazendo, mas estou divagando ...)

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
fonte
Sua abordagem para versões anteriores a 1.2.2 não funcionará, pois o Spring Boot não procura TomcatConnectorCustomizerinstâncias no contexto do aplicativo; eles devem ser registrados programaticamente comTomcatEmbeddedServletContainerFactory
Andy Wilkinson
Obrigado pelo aviso. Acabei desistindo disso, pois parece que a inicialização estática / dinâmica / tomcat / vs ainda era um problema. Isso é muito mais difícil do que deveria ser ... Nginx reverse proxy FTW!
John Culviner
3
No SpringBoot, as novas propriedades são server.compression.enabled = true e server.compression.mime-types = XXX, YYY github.com/spring-projects/spring-boot/wiki/…
blacelle
2
Se para a inicialização do Spring tivermos vários controladores rest, todos retornando respostas JSON. Podemos aplicar seletivamente o zip em alguns controladores?
3
Você também deve mencionar o tamanho mínimo da resposta para compactação (ex: 10 KB), caso contrário, torna-se uma sobrecarga para o servidor compactar todas as solicitações (ex: 0,5 KB). server.compression.min-response-size=10240
UsamaAmjad de
13

Em versões recentes na application.ymlconfiguração:

---

spring:
  profiles: dev

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

---
M. Reza Nasirloo
fonte
O relatório de bug correspondente e a correção em que isso foi alterado é github.com/spring-projects/spring-boot/issues/2737
McLovin
12

Esta é basicamente a mesma solução fornecida por @ andy-wilkinson, mas a partir do Spring Boot 1.0 o método customize (...) tem um parâmetro ConfigurableEmbeddedServletContainer .

Outra coisa que vale a pena mencionar é que o Tomcat compacta apenas os tipos de conteúdo de text/html, text/xmle text/plainpor padrão. Abaixo está um exemplo que suporta compressão de 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
fonte
Tentei adicionar isso à minha configuração Java e descobri que a compactação não parecia estar funcionando. Estou usando o Spring Boot com Tomcat como o contêiner integrado e gostaria de saber se havia alguma coisa adicional que eu precisava definir além desta configuração?
Michael Coxon
2
Tente verificar especificando o Accept-Encoding: gzip,deflatecabeçalho, se você estiver usando curl:curl -i -H 'Accept-Encoding: gzip,deflate' http://url.to.your.server
matsev
9

Spring Boot 1.4 Use para todas as compressões 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
fonte
Como verificamos essa compressão?
Bhargav
@Bhargav Veja o cabeçalho de resposta de sua resposta da API. Ele deve conter o cabeçalho: Content-Encoding:gzip
Sumit Jha
6

A etiquetagem do GZip no Tomcat não funcionou no meu Spring Boot Project. Eu usei o CompressingFilter encontrado aqui .

@Bean
public Filter compressingFilter() {
    CompressingFilter compressingFilter = new CompressingFilter();
    return compressingFilter;
}
user1127860
fonte
@ user1127860 tnx isso funciona, mas quer configurar este filtro mais adiante? Eu uso o spring boot e não consigo adicionar parâmetros de inicialização como o manual diz em web.xml
Spring
5

Para habilitar a compactação GZIP, você precisa modificar a configuração da instância incorporada do Tomcat. Para fazer isso, você declara um EmbeddedServletContainerCustomizerbean em sua configuração Java e, a seguir, registra um TomcatConnectorCustomizercom ele.

Por exemplo:

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

Consulte a documentação do Tomcat para obter mais detalhes sobre as várias opções de configuração de compactação disponíveis.

Você diz que deseja ativar seletivamente a compactação. Dependendo dos seus critérios de seleção, a abordagem acima pode ser suficiente. Ele permite que você controle a compactação pelo agente do usuário da solicitação, o tamanho da resposta e o tipo MIME da resposta.

Se isso não atender às suas necessidades, acredito que você terá que realizar a compactação em seu controlador e retornar uma resposta byte [] com um cabeçalho de codificação de conteúdo gzip.

Andy Wilkinson
fonte
1
qual é a diferença entre sua resposta à opção de colocar a configuração em 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
Esta resposta foi escrita antes que a configuração de compactação baseada em propriedades estivesse disponível. Eles são equivalentes, mas a abordagem baseada em propriedades é mais fácil, então eu recomendaria usá-la.
Andy Wilkinson
só quero compartilhar que, no meu caso, o tomcat está atrás de um balanceador de carga que obtém https e forword a solicitação para tomcat como http., quando eu uso a resposta da solução application.properties não é gzip, mas quando eu uso a solução de configuração programática no conector, recebo Resposta gzip com solicitação https LB
lukass77
outra questão caso eu use a solução application.properties .. e defino mais 2 conectores nas portas 8081 e 8082 .. a compressão se aplica a todos os conectores ou apenas ao conector 8080?
lukass77
eu verfiy isto, compersion é aplicável somente na porta 8080, mesmo se você abrir mais conectores .., eu acho que bug deveria ser aberto neste para boot de mola ?? .., então só a solução de trabalho para mim era configuração programática para cada conector, não application.properties
lukass77
0

Tive o mesmo problema em meu projeto Spring Boot + Spring Data ao chamar a @RepositoryRestResource.

O problema é o tipo MIME retornado; qual é application/hal+json. Adicioná-lo à server.compression.mime-typespropriedade resolveu esse problema para mim.

Espero que isso ajude outra pessoa!

Serginho
fonte