zoukankan      html  css  js  c++  java
  • 关于http响应内容压缩的一点小积累。

    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压缩了。

  • 相关阅读:
    笨方法学python中执行argv提示ValueError: not enough values to unpack (expected 4, got 1)
    VMware workstation安装
    Redis bigkey分析
    MySQL drop table 影响及过程
    MySQL 大表硬连接删除
    ES elasticsearch 各种查询
    ES elasticsearch 各种聚合
    ES elasticsearch 聚合统计
    ES elasticsearch 实现 count单字段,分组取前多少位,以地理位置中心进行统计
    MySQL行溢出、varchar最多能存多少字符
  • 原文地址:https://www.cnblogs.com/ld-swust/p/5199797.html
Copyright © 2011-2022 走看看