前言
springboot支持gzip压缩,并且支持自动压缩,即响应内容长度较短时不压缩,保证响应速度,响应内容长度较长时压缩,减轻带宽压力,并且配置起来十分简单
http响应的gzip压缩
当服务端对响应做了gzip压缩后,header里会添加:Content-Encoding: gzip用于表明返回结果做了gzip压缩。
如果客户端不希望该次请求会被压缩,则可以修改请求的header中的Accept-Encoding,去掉gzip即可
springboot配置
1、application.properties配置
server.compression.enabled=true
添加该配置后,springboot就开启了压缩。
springboot压缩还有以下配置:
server:
port: 2222
compression: #开启gzip压缩,返回内容大于2k的才会进行压缩
enabled: true
mime-types: application/javascript,text/css,application/json,application/xml,text/html,text/xml,text/plain
min-response-size: 2048
2、添加Content-Length
一般的教程到上一步就结束了,事实上上一步完成后确实已经开启了自动压缩,但是在测试时我发现,如果Controller返回的是普通的字符串,确实会有效果,如:
@RequestMapping(value = “/”, method = RequestMethod.GET)
@ResponseBody
public String test(){
return “test”;
}
当返回的字符串超过2KB时就会压缩,不超过则直接返回。
但是,当返回的是一个自定义的对象时,自动压缩不起效果,如:
@RequestMapping(value = “/”, method = RequestMethod.GET)
@ResponseBody
public Result test(){
return new Result(“test”);
}
server.compression.min-response-size不起作用,不管这个对象的大小,默认全部做gzip压缩。
经过排查是因为响应的header中没有Content-Length
当返回的是字符串,即Content-Type: text/plain 时,会设置Content-Length
而当返回的是对象,即Content-Type: application/json时,不会设置Content-Length,服务端无法判断长度,并且是通过Transfer-Encoding: chunked的方式发送给客户端,因此一定会做压缩。
因此只需要全局加上一个Content-Length即可:
@Configuration
public class FilterConfig {
@Bean
public FilterRegistrationBean filterRegistrationBean() {
FilterRegistrationBean filterBean = new FilterRegistrationBean();
filterBean.setFilter(new AddContentLengthFilter());
filterBean.setUrlPatterns(Arrays.asList("*"));
return filterBean;
}
}
class AddContentLengthFilter extends OncePerRequestFilter {
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
ContentCachingResponseWrapper cacheResponseWrapper;
if (!(response instanceof ContentCachingResponseWrapper)) {
cacheResponseWrapper = new ContentCachingResponseWrapper(response);
} else {
cacheResponseWrapper = (ContentCachingResponseWrapper) response;
}
filterChain.doFilter(request, cacheResponseWrapper);
cacheResponseWrapper.copyBodyToResponse();
}
}
这里添加了一个Filter,在这个Filter中,使用了ContentCachingResponseWrapper包装response
ContentCachingResponseWrapper会缓存所有写给OutputStream的数据,并且因为缓存了内容,所以可以获取Content-Length并帮忙设置。
测试
这里不细讲测试细节,可以用wireshark抓包,看看http响应的header中是否有Content-Encoding: gzip ,也可以看看具体的响应大小
总结
springboot的自动压缩看起来很容易设置,事实上自己在解决时也找了好久的资料。