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這里不拐彎抹角,直接就能看到,支持正則表達式