1. 简介
在本文中,我们将探讨如何通过 RestTemplate
发送包含压缩数据的 HTTP 请求。
同时,我们也会介绍如何配置 Spring Web 应用,使其能够正确处理客户端发来的压缩请求体。
这在高吞吐、大数据量的微服务通信中非常实用——能显著减少网络传输开销。但⚠️要注意:不是所有服务器都支持接收压缩请求体,盲目发送会导致 400 或解析失败。
2. 发送压缩请求
要实现请求体压缩,核心思路是:
✅ 在 RestTemplate
发出请求前,拦截并压缩 body
✅ 添加正确的 Content-Encoding
和 Accept-Encoding
头部
2.1 压缩工具方法
先写一个简单的 GZIP 压缩工具方法:
public static byte[] compress(byte[] body) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (GZIPOutputStream gzipOutputStream = new GZIPOutputStream(baos)) {
gzipOutputStream.write(body);
}
return baos.toByteArray();
}
这个方法接收原始字节数组,返回 GZIP 压缩后的字节流。
2.2 自定义拦截器
接下来实现 ClientHttpRequestInterceptor
,用于在请求发出前修改内容:
public class CompressingClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {
@Override
public ClientHttpResponse intercept(HttpRequest req, byte[] body, ClientHttpRequestExecution exec)
throws IOException {
HttpHeaders httpHeaders = req.getHeaders();
httpHeaders.add(HttpHeaders.CONTENT_ENCODING, "gzip");
httpHeaders.add(HttpHeaders.ACCEPT_ENCODING, "gzip");
return exec.execute(req, compress(body));
}
}
关键点说明:
- ✅
Content-Encoding: gzip
:告诉服务端,请求体是 GZIP 压缩过的 - ✅
Accept-Encoding: gzip
:表示客户端也接受压缩响应(可选,视需求而定) - ⚠️ 必须调用
compress(body)
对原始 body 进行压缩后再传递
2.3 注册到 RestTemplate
最后将拦截器注册到 RestTemplate
实例中:
@Bean
public RestTemplate getRestTemplate() {
RestTemplate restTemplate = new RestTemplate();
restTemplate.getInterceptors().add(new CompressingClientHttpRequestInterceptor());
return restTemplate;
}
这样,所有通过该 RestTemplate
发出的请求都会自动压缩 body 并带上对应 header。
💡 小贴士:如果你使用的是
WebClient
(响应式栈),可以用ExchangeFilterFunction
实现类似功能,原理一致。
3. 服务端处理压缩请求
光客户端压缩还不够 ❌。默认情况下,Spring Boot 内嵌的 Tomcat、Netty 等服务器不会自动解压请求体,直接接收会报错或解析为空。
目前只有 Jetty 和 Undertow 支持自动解压 GZIP 请求体。
所以你必须满足两个条件:
- 使用 Jetty 或 Undertow 作为 Web 服务器
- 正确配置 handler 启用请求解压
3.1 配置 Jetty 服务器
首先在 pom.xml
中排除默认的 Tomcat,引入 Jetty:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
然后配置 GzipHandler
并启用 inflateBufferSize
:
@Bean
public JettyServletWebServerFactory jettyServletWebServerFactory() {
JettyServletWebServerFactory factory = new JettyServletWebServerFactory();
factory.addServerCustomizers(server -> {
GzipHandler gzipHandler = new GzipHandler();
gzipHandler.setInflateBufferSize(1); // ⚠️ 必须大于0才能启用解压
gzipHandler.setHandler(server.getHandler());
HandlerCollection handlerCollection = new HandlerCollection(gzipHandler);
server.setHandler(handlerCollection);
});
return factory;
}
⚠️ 注意:setInflateBufferSize(1)
是关键!
Jetty 默认不开启请求体解压,必须显式设置该值 > 0 才会激活 inflate 功能。
3.2 配置 Undertow 服务器
同样,先切换为 Undertow:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
然后添加 RequestEncodingHandler
来处理 GZIP 编码:
@Bean
public UndertowServletWebServerFactory undertowServletWebServerFactory() {
UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory();
factory.addDeploymentInfoCustomizers((deploymentInfo) -> {
deploymentInfo.addInitialHandlerChainWrapper(handler -> new RequestEncodingHandler(handler)
.addEncoding("gzip", GzipStreamSourceConduit.WRAPPER));
});
return factory;
}
这样 Undertow 就会在请求进入 Servlet 前自动解压 GZIP 请求体,你的 Controller 可以像处理普通请求一样读取参数。
4. 总结
本文完整演示了 Spring 生态下实现请求压缩的两端配置:
角色 | 关键点 |
---|---|
客户端 | 使用 ClientHttpRequestInterceptor 压缩 body + 添加 Content-Encoding |
服务端 | 必须使用 Jetty/Undertow,并配置对应 Handler 启用解压 |
✅ 推荐场景:
- 内部微服务之间通信(可控环境)
- 数据上报、日志收集等大 payload 场景
❌ 不推荐场景:
- 面向第三方或浏览器的公开接口(兼容性差)
- 小数据量请求(压缩反而增加 CPU 开销)
📌 最后提醒:永远不要向不支持压缩的服务器发送压缩请求,否则只会换来一个 400 Bad Request 或静默失败。建议通过配置项控制是否开启压缩,便于灰度和回滚。