关于http响应内容压缩的一点小积累。

时间:2022-07-20 16:39:24

1、在tomcat的server.xml配置文件中,添加上背景颜色为绿色的配置,服务器就会自动压缩

  <Connector port="80" maxHttpHeaderSize="8192"
                maxThreads="150" minSpareThreads="25" maxSpareThreads="75"
                enableLookups="false" redirectPort="8443" acceptCount="100"
                connectionTimeout="20000" disableUploadTimeout="true" URIEncoding="utf-8" compression="on" compressionMinSize="2048" noCompressionUserAgents="gozilla, traviata" compressableMimeType="text/html,text/xml,text/javascript,text/css,text/plain"  />

详细讲解:

1) compression="on" 打开压缩功能
2) compressionMinSize="2048" 启用压缩的输出内容大小,这里面默认设置为2KB
3) noCompressionUserAgents="gozilla, traviata" 对于以下的浏览器,不启用压缩 
4) compressableMimeType="text/html,text/xml" 压缩类型(默认为text/html,text/xml,text/plain)

2、如果希望支持bzip2压缩,我的环境是apache cfx框架,自定义了一个跟GZIPOutInterceptor相似的拦截器,然后配置在接口的发布配置文件里。

   详细介绍如下:

BZIPOutInterceptor代码如下:

import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.TreeMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
 

import org.apache.cxf.common.i18n.BundleUtils;
import org.apache.cxf.common.logging.LogUtils;
import org.apache.cxf.helpers.CastUtils;
import org.apache.cxf.helpers.HttpHeaderHelper;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.interceptor.MessageSenderInterceptor;
import org.apache.cxf.io.AbstractThresholdOutputStream;
import org.apache.cxf.message.Exchange;
import org.apache.cxf.message.Message;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import org.apache.tools.bzip2.CBZip2OutputStream;
 
/**
 * CXF interceptor that compresses outgoing messages using bzip and sets the
 * HTTP Content-Encoding header appropriately. An instance of this class should
 * be added as an out interceptor on clients that need to talk to a service that
 * accepts gzip-encoded requests or on a service that wants to be able to return
 * compressed responses. In server mode, the interceptor only compresses
 * responses if the client indicated (via an Accept-Encoding header on the
 * request) that it can understand them. To handle gzip-encoded input messages,
 * see {@link GZIPInInterceptor}. This interceptor supports a compression
 * {@link #threshold} (default 1kB) - messages smaller than this threshold will
 * not be compressed. To force compression of all messages, set the threshold to
 * 0. This class was originally based on one of the CXF samples
 * (configuration_interceptor).
 */
public class BZIPOutInterceptor extends AbstractPhaseInterceptor<Message> {
 
    /**
     * Enum giving the possible values for whether we should gzip a particular
     * message.
     */
    public static enum UseBzip {
        NO, YES, FORCE
    }
     
    /**
     * regular expression that matches any encoding with a
     * q-value of 0 (or 0.0, 0.00, etc.).
     */
    public static final Pattern ZERO_Q = Pattern.compile(";\\s*q=0(?:\\.0+)?$");
     
    /**
     * regular expression which can split encodings
     */
    public static final Pattern ENCODINGS = Pattern.compile("[,\\s]*,\\s*");
 
    /**
     * Key under which we store the original output stream on the message, for
     * use by the ending interceptor.
     */
    public static final String ORIGINAL_OUTPUT_STREAM_KEY = BZIPOutInterceptor.class.getName()
                                                            + ".originalOutputStream";
 
    /**
     * Key under which we store an indication of whether compression is
     * permitted or required, for use by the ending interceptor.
     */
    public static final String USE_BZIP_KEY = BZIPOutInterceptor.class.getName() + ".useBzip";
 
    /**
     * Key under which we store the name which should be used for the
     * content-encoding of the outgoing message. Typically "gzip" but may be
     * "x-gzip" if we are processing a response message and this is the name
     * given by the client in Accept-Encoding.
     */
    public static final String BZIP_ENCODING_KEY = BZIPOutInterceptor.class.getName() + ".bzipEncoding";
     
    public static final String SOAP_JMS_CONTENTENCODING = "SOAPJMS_contentEncoding";
 
    private static final ResourceBundle BUNDLE = BundleUtils.getBundle(BZIPOutInterceptor.class);
    private static final Logger LOG = LogUtils.getL7dLogger(BZIPOutInterceptor.class);
 
 
    /**
     * Compression threshold in bytes - messages smaller than this will not be
     * compressed.
     */
    private int threshold = 1024;
    private boolean force;
 
    public BZIPOutInterceptor() {
        super(Phase.PREPARE_SEND);
        addAfter(MessageSenderInterceptor.class.getName());
    }
    public BZIPOutInterceptor(int threshold) {
        super(Phase.PREPARE_SEND);
        addAfter(MessageSenderInterceptor.class.getName());
        this.threshold = threshold;
    }
 
    public void setThreshold(int threshold) {
        this.threshold = threshold;
    }
 
    public int getThreshold() {
        return threshold;
    }
 
    public void handleMessage(Message message) throws Fault {
        UseBzip use = bzipPermitted(message);
        
        if (use != UseBzip.NO) {
            // remember the original output stream, we will write compressed
            // data to this later
            OutputStream os = message.getContent(OutputStream.class);
            
            if (os == null) {
                return;
            }
            
            message.put(ORIGINAL_OUTPUT_STREAM_KEY, os);
            message.put(USE_BZIP_KEY, use);
            
            // new stream to cache the message
            
            BZipThresholdOutputStream cs 
                = new BZipThresholdOutputStream(threshold,
                                                os,
                                                use == UseBzip.FORCE,
                                                message);
            message.setContent(OutputStream.class, cs);
        }
    }
    
    /**
     * Checks whether we can, cannot or must use gzip compression on this output
     * message. Gzip is always permitted if the message is a client request. If
     * the message is a server response we check the Accept-Encoding header of
     * the corresponding request message - with no Accept-Encoding we assume
     * that gzip is not permitted. For the full gory details, see <a
     * href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.3">section
     * 14.3 of RFC 2616</a> (HTTP 1.1).
     * 
     * @param message the outgoing message.
     * @return whether to attempt gzip compression for this message.
     * @throws Fault if the Accept-Encoding header does not allow any encoding
     *                 that we can support (identity, gzip or x-gzip).
     */
    private UseBzip bzipPermitted(Message message) throws Fault {
        UseBzip permitted = UseBzip.NO;
        if (isRequestor(message)) {
            LOG.fine("Requestor role, so bzip enabled");
            Object o = message.getContextualProperty(USE_BZIP_KEY);
            if (o instanceof UseBzip) {
                permitted = (UseBzip)o;
            } else if (o instanceof String) {
                permitted = UseBzip.valueOf((String)o);
            } else {
                permitted = force ? UseBzip.YES : UseBzip.NO;
            }
            message.put(BZIP_ENCODING_KEY, "bzip");
            addHeader(message, "Accept-Encoding", "bzip;q=1.0, identity; q=0.5, *;q=0"); 
        } else {
            LOG.fine("Response role, checking accept-encoding");
            Exchange exchange = message.getExchange();
            Message request = exchange.getInMessage();
            Map<String, List<String>> requestHeaders = CastUtils.cast((Map<?, ?>)request
                .get(Message.PROTOCOL_HEADERS));
            if (requestHeaders != null) {
                List<String> acceptEncodingHeader = CastUtils.cast(HttpHeaderHelper
                    .getHeader(requestHeaders, HttpHeaderHelper.ACCEPT_ENCODING));
                List<String> jmsEncodingHeader = CastUtils.cast(requestHeaders.get(SOAP_JMS_CONTENTENCODING));
                if (jmsEncodingHeader != null && jmsEncodingHeader.contains("bzip")) {
                    permitted = UseBzip.YES;
                    message.put(BZIP_ENCODING_KEY, "bzip");
                }
                if (acceptEncodingHeader != null) {
                    if (LOG.isLoggable(Level.FINE)) {
                        LOG.fine("Accept-Encoding header: " + acceptEncodingHeader);
                    }
                    // Accept-Encoding is a comma separated list of entries, so
                    // we split it into its component parts and build two
                    // lists, one with all the "q=0" encodings and the other
                    // with the rest (no q, or q=<non-zero>).
                    List<String> zeros = new ArrayList<String>(3);
                    List<String> nonZeros = new ArrayList<String>(3);
 
                    for (String headerLine : acceptEncodingHeader) {
                        String[] encodings = ENCODINGS.split(headerLine.trim());
 
                        for (String enc : encodings) {
                            Matcher m = ZERO_Q.matcher(enc);
                            if (m.find()) {
                                zeros.add(enc.substring(0, m.start()));
                            } else if (enc.indexOf(';') >= 0) {
                                nonZeros.add(enc.substring(0, enc.indexOf(';')));
                            } else {
                                nonZeros.add(enc);
                            }
                        }
                    }
 
                    // identity encoding is permitted if (a) it is not
                    // specifically disabled by an identity;q=0 and (b) if
                    // there is a *;q=0 then there is also an explicit
                    // identity[;q=<non-zero>]
                    //
                    // [x-]gzip is permitted if (a) there is an explicit
                    // [x-]gzip[;q=<non-zero>], or (b) there is a
                    // *[;q=<non-zero>] and no [x-]gzip;q=0 to disable it.
                    boolean identityEnabled = !zeros.contains("identity")
                                              && (!zeros.contains("*") || nonZeros.contains("identity"));
                    boolean bzipEnabled = nonZeros.contains("bzip")
                                          || (nonZeros.contains("*") && !zeros.contains("bzip"));
 
                    if (identityEnabled && !bzipEnabled) {
                        permitted = UseBzip.NO;
                    } else if (identityEnabled && bzipEnabled) {
                        permitted = UseBzip.YES;
                        message.put(BZIP_ENCODING_KEY, "bzip");
                    } else if (!identityEnabled && bzipEnabled) {
                        permitted = UseBzip.FORCE;
                        message.put(BZIP_ENCODING_KEY, "bzip");
                    } else {
                        throw new Fault(new org.apache.cxf.common.i18n.Message("NO_SUPPORTED_ENCODING",
                                                                               BUNDLE));
                    }
                } else {
                    LOG.fine("No accept-encoding header");
                }
            }
        }
 
        if (LOG.isLoggable(Level.FINE)) {
            LOG.fine("bzip permitted: " + permitted);
        }
        return permitted;
    }
     
    static class BZipThresholdOutputStream extends AbstractThresholdOutputStream {
        Message message;
         
        public BZipThresholdOutputStream(int t, OutputStream orig,
                                         boolean force, Message msg) {
            super(t);
            super.wrappedStream = orig;
            message = msg;
            if (force) {
                setupBZip();
            }
        }
         
        private void setupBZip() {
             
        }
 
        @Override
        public void thresholdNotReached() {
            //nothing
            LOG.fine("Message is smaller than compression threshold, not compressing.");
        }
 
        @Override
        public void thresholdReached() throws IOException {
            LOG.fine("Compressing message.");
            // Set the Content-Encoding HTTP header
            String enc = (String)message.get(BZIP_ENCODING_KEY);
            addHeader(message, "Content-Encoding", enc);
            // if this is a response message, add the Vary header
            if (!Boolean.TRUE.equals(message.get(Message.REQUESTOR_ROLE))) {
                addHeader(message, "Vary", "Accept-Encoding");
            } 
            // bzip the result
            CBZip2OutputStream bZip2Output = new CBZip2OutputStream(wrappedStream);
            wrappedStream = bZip2Output;
        }
    }
     
    /**
     * Adds a value to a header. If the given header name is not currently
     * set in the message, an entry is created with the given single value.
     * If the header is already set, the value is appended to the first
     * element of the list, following a comma.
     * 
     * @param message the message
     * @param name the header to set
     * @param value the value to add
     */
    private static void addHeader(Message message, String name, String value) {
        Map<String, List<String>> protocolHeaders = CastUtils.cast((Map<?, ?>)message
            .get(Message.PROTOCOL_HEADERS));
        if (protocolHeaders == null) {
            protocolHeaders = new TreeMap<String, List<String>>(String.CASE_INSENSITIVE_ORDER);
            message.put(Message.PROTOCOL_HEADERS, protocolHeaders);
        }
        List<String> header = CastUtils.cast((List<?>)protocolHeaders.get(name));
        if (header == null) {
            header = new ArrayList<String>();
            protocolHeaders.put(name, header);
        }
        if (header.size() == 0) {
            header.add(value);
        } else {
            header.set(0, header.get(0) + "," + value);
        }
    }
    public void setForce(boolean force) {
        this.force = force;
    }    
 
}

配置文件:

    <jaxrs:server id="" address="/">
      <jaxrs:outInterceptors>
           <bean class="BZIPOutInterceptor"></bean>
       </jaxrs:outInterceptors>
        <jaxrs:serviceBeans>
            <ref bean="" />
        </jaxrs:serviceBeans>
        <jaxrs:extensionMappings>
            <entry key="xml" value="application/xml" />
        </jaxrs:extensionMappings>
        <jaxrs:languageMappings>
            <entry key="en" value="en-gb" />
        </jaxrs:languageMappings>
    </jaxrs:server>

这样便能够实现如果请求头里Accept-Encoding 包含bzip压缩,服务器就自动进行bzip2压缩了。