SpringBoot 启用 GZIP 对响应进行压缩

1659942271661.jpg

SpringBoot Web 应用默认是不启用响应数据的压缩,对大的文本类型的响应数据进行压缩是十分必要的,如 JSON, XML 等应用数据,甚至是 JS, CSS 等。

早先的 Web 应用基本是要配置一个叫做 GzipFilter 之类的东西,然后判断请求的 Accept-Encoding 是否含有 gzip , 再对需要的 Content-Type 响应类型的数据进行压缩。

在使用了 SpringBoot 之后,在碰到有压缩响应的需求的时候,第一件事情应该要想到是否能通过在 application.properties (或 application.yml) 配置就行。于是查阅 SpringBoot 2.7.x 的帮助文档 Spring Boot Reference Document , 搜索关键字 compression ,翻几页就能找到 17.3.6. Enable HTTP Response Compression , 介绍了三个配置项

  1. server.compression.enable=true (默认为 false, 不启用压缩)
  2. server.compression.min-response-size=2048 (默认至少 2K 字节以及以上大小的响应数据才被压缩, 要在网络带宽与 CPU 消耗上找到一个平衡)
  3. server.compression.mim-types=text/html,text/xml,text/plain,text/css,text/javascript,application/json,application/xml (默认压缩的响应类型)

再往下找到 .A.11.Server Properties , 还有一个相关的选项

  • server.compression.exclude-user-agents= (默认为空,逗号分隔的 user-agent, 针对什么 user-agent 不启用压缩,可能给测试用的)

如果是 SpringBoot 1.2.x 的话,启用压缩的方法是不一样的,详情同样是参考官方的文档 64.18 Enable HTTP response compression

这里我们再回到当前的 SpringBoot 2.7.x 版本,其实只要是 SpringBoot 1.3+ 的版本,唯一要做的就是配置

<pre class="hljs vbscript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 0.75em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"> server.compression.enable=true
</pre>

其他三个选项根据基本满足我们的日常要求了,或者按需稍加调节。

下面进行一些实战烟训,默认未配置 server.compression.enable 时,即默认为 false, 不启用响应压缩,写一个 controller 方法

<pre class="hljs less" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 0.75em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto;">@GetMapping(value = "/hello")
public String hello(@RequestParam int length) {
return StringUtils.repeat("0", length);
}</pre>

|

@ GetMapping ( value = "/hello" )

public String hello ( @ RequestParam int length ) {

 return StringUtils . repeat ( "0" , length ) ;

}

|

curl 测试

<pre class="prettyprint hljs ruby" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"> bash-3.2curl -I http://localhost:8080/hello?length=2047 HTTP/1.1 200 Content-Type: text/plain;charset=UTF-8 Content-Length: 2047 Date: Tue, 30 Aug 2022 03:01:53 GMT bash-3.2 curl -I http://localhost:8080/hello?length=2048 HTTP/1.1 200 Content-Type: text/plain;charset=UTF-8 Content-Length: 2048 Date: Tue, 30 Aug 2022 03:01:56 GMT bash-3.2$ curl -I -H "Accept-Encoding:gzip" http://localhost:8080/hello?length=2049 HTTP/1.1 200 Content-Type: text/plain;charset=UTF-8 Content-Length: 2049 Date: Tue, 30 Aug 2022 03:02:20 GMT
</pre>

怎么都不会对响应进行压缩,现在我们在 application.properties 中加上

<pre class="hljs vbscript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 0.75em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto;">server.compression.enabled = true</pre>

|

server . compression . enabled = true

|

重新测试

<pre class="prettyprint hljs ruby" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"> bash-3.2curl -I -H "Accept-Encoding:gzip" http://localhost:8080/hello?length=2047 HTTP/1.1 200 Content-Type: text/plain;charset=UTF-8 Content-Length: 2047 Date: Tue, 30 Aug 2022 13:22:14 GMT bash-3.2 curl -I http://localhost:8080/hello?length=2048 HTTP/1.1 200 vary: accept-encoding Content-Type: text/plain;charset=UTF-8 Content-Length: 2048 Date: Tue, 30 Aug 2022 13:22:16 GMT bash-3.2$ curl -I -H "Accept-Encoding:gzip" http://localhost:8080/hello?length=2048 HTTP/1.1 200 vary: accept-encoding Content-Encoding: gzip Content-Type: text/plain;charset=UTF-8 Transfer-Encoding: chunked Date: Tue, 30 Aug 2022 13:22:18 GMT
</pre>

响应长度为 2048 及以上会采用压缩,并且这时不管有没有 Accept-Encoding:gzip 都会加上 vary: accept-encoding 用以区分不同的响应数据,像 Varnish 就要考虑 Accept-Encoding 作为 Key 的一部分缓存是否压缩的数据。

关于 server.compression.mime-types

前面提过它的默认值是 text/html,text/xml,text/plain,text/css,text/javascript,application/json,application/xml, 即只对这些 Content-Type 类型的数据进行压缩,不该压缩的类型注意不要重复压缩,如 image/jpg, application/octet-stream 等.

text/plain 对 Content-Type:text/plain;charset=UTF-8 同样是适用的

不支持通配符配置,如不能用 text/* 来涵盖所有以 text/ 开头的类型,像 test/html, test/xml, text/plain 等。必须一个个罗列出来

server.compression.mime-types 中的配置是区分大小写的,如

<pre class="hljs vbscript" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 0.75em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto;">server.compression.mime-types=TEXT/PLAIN</pre>

|

server . compression . mime - types = TEXT / PLAIN

|

对 Content-Type:text/plain 是不启作用的

<pre class="prettyprint hljs ruby" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"> bash-3.2$ curl -I -H "Accept-Encoding:gzip" http://localhost:8080/hello?length=2049 HTTP/1.1 200 Content-Type: text/plain;charset=UTF-8 Content-Length: 2049 Date: Tue, 30 Aug 2022 14:20:21 GMT
</pre>

如果我们把 API 的 Content-Type 也设置为 TEXT/PLAIN 就能被压缩了

<pre class="hljs dart" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 0.75em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto;">@GetMapping(value = "/hello")
public ResponseEntity<String> hello(HttpServletResponse response, @RequestParam int length) {
MultiValueMap<String, String> headers = new LinkedMultiValueMap<>();
headers.add("Content-Type", "TEXT/PLAIN");
return new ResponseEntity<>(StringUtils.repeat("0", length), headers, HttpStatus.OK);
}</pre>

|

@ GetMapping ( value = "/hello" )

public ResponseEntity < String > hello ( HttpServletResponse response , @ RequestParam int length ) {

 MultiValueMap < String , String > headers = new LinkedMultiValueMap <> ( ) ;

 headers . add ( "Content-Type" , "TEXT/PLAIN" ) ;

 return new ResponseEntity <> ( StringUtils . repeat ( "0" , length ) , headers ,    HttpStatus . OK ) ;

}

|

<pre class="prettyprint hljs vim" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 1.5em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: start; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"> bash-3.2$ curl -I -H "Accept-Encoding:gzip" http://localhost:8080/hello?length=2049 HTTP/1.1 200 vary: accept-encoding Content-Encoding: gzip Content-Type: TEXT/PLAIN Transfer-Encoding: chunked Date: Tue, 30 Aug 2022 14:22:20 GMT
</pre>

注意在 Spring Web controller 方法中,对于标准的 Content-Type 是无法通过 @GetMapping 注解的 produces 和 HttpServletResponse 来改变的

<pre class="hljs less" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 0.75em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto;">@GetMapping(value = "/hello", produces = "TEXT/PLAIN")
public String hello(HttpServletResponse response, @RequestParam int length) {
response.setHeader("Content-Type", "TEXT/PLAIN");
return StringUtils.repeat("0", length);
}</pre>

|

@ GetMapping ( value = "/hello" , produces = "TEXT/PLAIN" )

public String hello ( HttpServletResponse response , @ RequestParam int length ) {

 response . setHeader ( "Content-Type" , "TEXT/PLAIN" ) ;

 return StringUtils . repeat ( "0" , length ) ;

}

|

以上代码最终的 Content-Type 仍然为 text/plain;charset=UTF-8

其他相关的内容

SpringBoot 1.2.2 - <1.3 之间启用压缩的配置

<pre class="hljs vbnet" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 0.75em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto;">server.tomcat.compression=on
server.tomcat.compressableMimeTypes=application/json,application/xml,text/html,text/xml,text/plain,application/javascript,text/css</pre>

|

server . tomcat . compression = on

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

|

SpringBoot 1.2.2 之前,在使用 Tomcat 作为内嵌应用服务器时,通过 TomcatConnectorCustomizer

<pre class="hljs java" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 0.75em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto;">@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");
}
}</pre>

|

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

}

}

|

Tomcat 本身可配置 server.xml 中的 connector 自动实现对响应数据的压缩,在 Apache Tomcat 10 Configuration Reference - The HTTP Connector 一章中查找 compression 就能找到下面这几个属性

  • compression: off|on|force (默认为 off)
  • compressibleMimeType: text/html,text/xml,text/plain,text/css,text/javascript,application/javascript,application/json,application/xml
  • compressionMinSize: 2048
  • noCompressionUserAgents: 默认为空,可使用正则表达式

HTTP/2 connector 也继承了以上几个属性配置

Apache HTTP Server 的压缩模块

如果部署时在应用服务器(如 Tomcat) 前端配置了 Apache HTTP Server 的话,可以由 Apache 完成对数据的压缩,要使用到的模块是 mod_defalte

比如在 Debian 系的 OS 中 a2enmod deflate, 或在 httpd.conf 中用 LoadModule deflate_module modules/mod_deflate.so 启用。然后在 http.conf 或是应用的 .htaccess 文件中

<pre class="hljs vbnet" style="padding: 0.5em; font-family: Menlo, Monaco, Consolas, "Courier New", monospace; color: rgb(68, 68, 68); border-radius: 4px; display: block; margin: 0px 0px 0.75em; font-size: 14px; line-height: 1.5em; word-break: break-all; word-wrap: break-word; white-space: pre; background-color: rgb(246, 246, 246); border: none; overflow-x: auto;">AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE text/html
......</pre>

|

AddOutputFilterByType DEFLATE text / plain

AddOutputFilterByType DEFLATE text / html

. . . . . .

|

逐项加入要支持压缩的响应类型

具体使用方式请参照 Apache Module mod_deflate 的文档。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容