http压缩相关类Compression.java
package org.springframework.boot.web.server; import org.springframework.util.unit.DataSize; /** * Simple server-independent abstraction for compression configuration. * * @author Ivan Sopov * @author Andy Wilkinson * @author Stephane Nicoll * @since 2.0.0 */ public class Compression { private boolean enabled = false; private String[] mimeTypes = new String[] { "text/html", "text/xml", "text/plain", "text/css", "text/javascript", "application/javascript", "application/json", "application/xml" }; private String[] excludedUserAgents = null; private DataSize minResponseSize = DataSize.ofKilobytes(2); /** * Return whether response compression is enabled. * @return {@code true} if response compression is enabled */ public boolean getEnabled() { return this.enabled; } public void setEnabled(boolean enabled) { this.enabled = enabled; } /** * Return the MIME types that should be compressed. * @return the MIME types that should be compressed */ public String[] getMimeTypes() { return this.mimeTypes; } public void setMimeTypes(String[] mimeTypes) { this.mimeTypes = mimeTypes; } public String[] getExcludedUserAgents() { return this.excludedUserAgents; } public void setExcludedUserAgents(String[] excludedUserAgents) { this.excludedUserAgents = excludedUserAgents; } /** * Return the minimum "Content-Length" value that is required for compression to be * performed. * @return the minimum content size in bytes that is required for compression */ public DataSize getMinResponseSize() { return this.minResponseSize; } public void setMinResponseSize(DataSize minSize) { this.minResponseSize = minSize; } }
通过以上代码了解
1、从Spring 2.x开始就支持http响应流压缩
2、默认不开启压缩
3、默认压缩阈值2K,当响应流字节达到2K才压缩
4、支持过滤特定类型的User-Agent
5、默认压缩文件类型
"text/html", "text/xml", "text/plain", "text/css", "text/javascript",
"application/javascript", "application/json", "application/xml"
解决方案
在application.properties或者application.yml配置相关属性
server.compression.enabled=true
server.compression.min-response-size=1024 #响应流达到1024字节再压缩
application.yml
server:
compression:
enabled: true
min-response-size: 1024
参考:https://frontbackend.com/spring/how-to-enable-gzip-compression-in-spring-boot
关于排除自定义的User-Agent
笔者追踪源代码,找到了相关代码片段
Tomcat
org.springframework.boot.web.embedded.tomcat.CompressionConnectorCustomizer
package org.springframework.boot.web.embedded.tomcat; import org.apache.catalina.connector.Connector; import org.apache.coyote.ProtocolHandler; import org.apache.coyote.UpgradeProtocol; import org.apache.coyote.http11.AbstractHttp11Protocol; import org.apache.coyote.http2.Http2Protocol; import org.springframework.boot.web.server.Compression; import org.springframework.util.StringUtils; /** * {@link TomcatConnectorCustomizer} that configures compression support on the given * Connector. * * @author Brian Clozel */ class CompressionConnectorCustomizer implements TomcatConnectorCustomizer { private final Compression compression; CompressionConnectorCustomizer(Compression compression) { this.compression = compression; } @Override public void customize(Connector connector) { if (this.compression != null && this.compression.getEnabled()) { ProtocolHandler handler = connector.getProtocolHandler(); if (handler instanceof AbstractHttp11Protocol) { customize((AbstractHttp11Protocol<?>) handler); } for (UpgradeProtocol upgradeProtocol : connector.findUpgradeProtocols()) { if (upgradeProtocol instanceof Http2Protocol) { customize((Http2Protocol) upgradeProtocol); } } } } private void customize(Http2Protocol protocol) { Compression compression = this.compression; protocol.setCompression("on"); protocol.setCompressionMinSize(getMinResponseSize(compression)); protocol.setCompressibleMimeType(getMimeTypes(compression)); if (this.compression.getExcludedUserAgents() != null) { protocol.setNoCompressionUserAgents(getExcludedUserAgents()); } } private void customize(AbstractHttp11Protocol<?> protocol) { Compression compression = this.compression; protocol.setCompression("on"); protocol.setCompressionMinSize(getMinResponseSize(compression)); protocol.setCompressibleMimeType(getMimeTypes(compression)); if (this.compression.getExcludedUserAgents() != null) { protocol.setNoCompressionUserAgents(getExcludedUserAgents()); } } private int getMinResponseSize(Compression compression) { return (int) compression.getMinResponseSize().toBytes(); } private String getMimeTypes(Compression compression) { return StringUtils.arrayToCommaDelimitedString(compression.getMimeTypes()); } private String getExcludedUserAgents() { return StringUtils.arrayToCommaDelimitedString(this.compression.getExcludedUserAgents()); } }
tomcat的做法是拿到excludedUserAgent然后根据逗号进行分割数组,相关代码如下
/** * Convert a {@code String} array into a comma delimited {@code String} * (i.e., CSV). * <p>Useful for {@code toString()} implementations. * @param arr the array to display (potentially {@code null} or empty) * @return the delimited {@code String} */ public static String arrayToCommaDelimitedString(@Nullable Object[] arr) { return arrayToDelimitedString(arr, ","); }
跟随代码一路往下看,找到(说明支持正则表达式匹配)
public void setNoCompressionUserAgents(String noCompressionUserAgents) { if (noCompressionUserAgents == null || noCompressionUserAgents.length() == 0) { this.noCompressionUserAgents = null; } else { this.noCompressionUserAgents = Pattern.compile(noCompressionUserAgents); } }
undertow
笔者用的是undertow,相关代码
org.springframework.boot.web.embedded.undertow.CompressionHttpHandlerFactory
相关代码
private static Predicate[] getCompressionPredicates(Compression compression) { List<Predicate> predicates = new ArrayList<>(); predicates.add(new MaxSizePredicate((int) compression.getMinResponseSize().toBytes())); predicates.add(new CompressibleMimeTypePredicate(compression.getMimeTypes())); if (compression.getExcludedUserAgents() != null) { for (String agent : compression.getExcludedUserAgents()) { RequestHeaderAttribute agentHeader = new RequestHeaderAttribute(new HttpString(HttpHeaders.USER_AGENT)); predicates.add(Predicates.not(Predicates.regex(agentHeader, agent))); } } return predicates.toArray(new Predicate[0]); }
undertow这里不拐弯抹角,直接就能看到,支持正则表达式