• 使用gzip优化web应用(filter实现)


    相关知识:

      gzip是http协议中使用的一种加密算法,客户端向web服务器端发出了请求后,通常情况下服务器端会将页面文件和其他资源,返回到客户端,客户端加载后渲染呈现,这种情况文件一般都比较大,如果开启Gzip ,那么服务器端响应后,会将页面,JS,CSS等文本文件或者其他文件通过高压缩算法将其压缩,然后传输到客户端,由客户端的浏览器负责解压缩与呈现。通常能节省40%以上的流量(一般都有60%左右),一些PHP,JSP文件也能够进行压缩。

    1.实现:  

    Tomcat 开启Gzip :

    1.找到Tomcat 目录下的conf下的server.xml,并找到如下信息

    Connector port="8080" maxHttpHeaderSize="8192" maxThreads="150" minSpareThreads="25" maxSpareThreads="75" enableLookups="false" redirectPort="8443" acceptCount="100" connectionTimeout="20000" disableUploadTimeout="true"

    将它改成如下的形式(其实在上面代码的下面已经有了,将他们打开而已。):

    <!-- Define a non-SSL HTTP/1.1 Connector on port 8080 --> <Connector port="8080" maxHttpHeaderSize="8192" maxThreads="150" minSpareThreads="25" maxSpareThreads="75" enableLookups="false" redirectPort="8443" acceptCount="100" connectionTimeout="20000" disableUploadTimeout="true" compression="on" compressionMinSize="2048" noCompressionUserAgents="gozilla, traviata" compressableMimeType="text/html,text/xml" >

    这样,就能够对html和xml进行压缩了,如果要压缩css 和 js,那么需要将

    compressableMimeType=”text/html,text/xml”加入css和js:

    <Connector port="8080" ......... compressableMimeType="text/html,text/xml,text/css,text/javascript" >

    你甚至可以压缩图片:

    compressableMimeType=”text/html,text/xml”加入css和js:

    <Connector port="8080" ......... compressableMimeType="text/html,text/xml,text/css,text/javascript,image/gif,image/jpg" >

    开启后重启Tomcat ,通过浏览器查看headers信息就能看到是否开启(firebug中有),如果开启了,那么transfer-encoding就会是Gzip,否则就是chunked。

    2.在代码级别完成web应用的gzip压缩的开启:

    1.Wrapper  用来包装HttpServletResponse 对象

    package com.shop.gzip;
    
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.io.OutputStreamWriter;
    import java.io.PrintWriter;
    
    import javax.servlet.ServletOutputStream;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpServletResponseWrapper;
    
    public class Wrapper extends HttpServletResponseWrapper {
        public static final int OT_NONE = 0, OT_WRITER = 1, OT_STREAM = 2;
        private int outputType = OT_NONE;
        private ServletOutputStream output = null;
        private PrintWriter writer = null;
        private ByteArrayOutputStream buffer = null;
    
        public Wrapper(HttpServletResponse resp) throws IOException {
            super(resp);
            buffer = new ByteArrayOutputStream();
        }
    
        public PrintWriter getWriter() throws IOException {
            if (outputType == OT_STREAM)
                throw new IllegalStateException();
            else if (outputType == OT_WRITER)
                return writer;
            else {
                outputType = OT_WRITER;
                writer = new PrintWriter(new OutputStreamWriter(buffer,
                        getCharacterEncoding()));
                return writer;
            }
        }
    
        public ServletOutputStream getOutputStream() throws IOException {
            if (outputType == OT_WRITER)
                throw new IllegalStateException();
            else if (outputType == OT_STREAM)
                return output;
            else {
                outputType = OT_STREAM;
                output = new WrappedOutputStream(buffer);
                return output;
            }
        }
    
        public void flushBuffer() throws IOException {
            if (outputType == OT_WRITER)
                writer.flush();
            if (outputType == OT_STREAM)
                output.flush();
        }
    
        public void reset() {
            outputType = OT_NONE;
            buffer.reset();
        }
    
        public byte[] getResponseData() throws IOException {
            flushBuffer();
            return buffer.toByteArray();
    
        }
    
        class WrappedOutputStream extends ServletOutputStream {
            private ByteArrayOutputStream buffer;
    
            public WrappedOutputStream(ByteArrayOutputStream buffer) {
                this.buffer = buffer;
            }
    
            public void write(int b) throws IOException {
                buffer.write(b);
            }
    
            public byte[] toByteArray() {
                return buffer.toByteArray();
            }
        }
    } 

    2.过滤器

    package com.shop.gzip;
    
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.util.zip.GZIPOutputStream;
    
    import javax.servlet.Filter;
    import javax.servlet.FilterChain;
    import javax.servlet.FilterConfig;
    import javax.servlet.ServletException;
    import javax.servlet.ServletOutputStream;
    import javax.servlet.ServletRequest;
    import javax.servlet.ServletResponse;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    public class GZipFilter implements Filter {
    
        public void destroy() {
        }
          /**
           * 判断浏览器是否支持GZIP
           * @param request
           * @return
           */
          private static boolean isGZipEncoding(HttpServletRequest request){
            boolean flag=false;
            String encoding=request.getHeader("Accept-Encoding");
            if(encoding.indexOf("gzip")!=-1){
              flag=true;
            }
            return flag;
          }
          
        public void doFilter(ServletRequest request, ServletResponse response,
                FilterChain chain) throws IOException, ServletException {
            HttpServletResponse resp = (HttpServletResponse) response;
            HttpServletRequest req=(HttpServletRequest)request;
            if(isGZipEncoding(req)){
                Wrapper wrapper = new Wrapper(resp);
                chain.doFilter(request, wrapper);
                byte[] gzipData = gzip(wrapper.getResponseData());
                resp.addHeader("Content-Encoding", "gzip");
                resp.setContentLength(gzipData.length);
                ServletOutputStream output = response.getOutputStream();
                output.write(gzipData);
                output.flush();
            } else {
                chain.doFilter(request, response);
            }        
    
        }
    
        public void init(FilterConfig arg0) throws ServletException {
    
        }
    
        private byte[] gzip(byte[] data) {
            ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(10240);
            GZIPOutputStream output = null;
            try {
                output = new GZIPOutputStream(byteOutput);
                output.write(data);
            } catch (IOException e) {
            } finally {
                try {
                    output.close();
                } catch (IOException e) {
                }
            }
            return byteOutput.toByteArray();
        }
    
    }

    3.在web.xml中配置 GZipFilter,当我们访问应用中以.do结尾的资源的使用,服务器端就开启http gzip压缩,将压缩后的信息通过http 协议传递给浏览器.

    <filter>  
    
      <filter-name>ecsideExport</filter-name>  
    
      <filter-class>com.web.servlet.GZipFilter</filter-class>  
    
     </filter>  
    
    <filter-mapping>  
    
                 <filter-name>ecsideExport</filter-name>  
    
                <url-pattern>*.do</url-pattern>  
    
    </filter-mapping>  


    番外:使用Jayson Falkner的过滤器

    Jayson Falkner 在他的Two Servlet Filters Every Web Application Should Have(http://www.onjava.com/pub/a/onjava/2003/11/19/filters.html?page=1 ) 里面介绍了2个提高Java Web Application 效能的Servlet。这里记一下其中的GZIPFilter。
    GZIPFilter可以用gzip的方式压缩HTTP Response的内容,从而在Server端加快了响应回复的速度(gzip压缩过程需耗用CPU资源,却大幅减轻了网卡的处理负担),在Client端缩短了页面刷新时间,在网络内减少了数据流量。另一方面,因为采用的Filter的方式,无需改动应用现有的代码就可引入该功能。


    以上Filter的source code在这里(http://www.onjava.com/onjava/2003/11/19/examples/jspbook.zip )下载。当然也可以在这里(http://www.onjava.com/onjava/2003/11/19/examples/jspbook.jar )下载现成的JAR。

    作者定义了3个类来实现这个压缩功能:GZIPFilter, GZIPResponseWrapper, GZIPResponseStream。类图大致如下:


    这3个类要做的,就是在Server将响应内容装入HTTP Response之前,先对内容进行GZIP压缩。其中GZIPFilter实现了javax.servlet.Filter接口,在Java Web Container处理HTTP Response的过程中便可以挂载使用。另外2个类(GZIPResponseWrapper,GZIPResponseStream)基本上是辅助类,真正的压缩动作是在GZIPResponseStream中由java.util.zip.GZIPOutputStream来完成的,除此还有其它的一些方法,感觉似乎并没有全部用到。这部分还需要另外再研究。

    要布署这个Filter也很简单。只要将JAR放至应用的library目录,并且在应用的布署配置文件web.xml中加入

    GZIPFilter

    com.jspbook.GZIPFilter

    GZIPFilter

    /*.jsp

    然后启动Server就可以了。

    GZIPFilter压缩HTTP Response内容的作用非常明显。作者在source code的包里面另外放了TestGZIP.jsp,这是用来显示GZIPFilter的压缩效果的JSP。你可以找一支产生大量内容的目标JSP来测验一下,就像下面这支show_response_compressed.jsp:

    <%@ page import="java.io.*" %>
    
    <%@ page import="java.util.zip.*" %>
    
     
    
    
    <%
    
    String title = "Show Compressed Response";
    
    int size = 100000;
    
     
    
    
    out.println("<HTML>/n" +
    
    "<HEAD><TITLE>" + title + "</TITLE></HEAD>/n" +
    
    "<BODY BGCOLOR=/"#FDF5E6/">/n" +
    
    "<H1 ALIGN=/"CENTER/">" + title + " SIZE="+size+"</H1>/n");
    
     
    
    
    String line = "Blah, blah, blah, blah, blah. " +
    
    "Yadda, yadda, yadda, yadda.";
    
    for(int i=0; i<size; i++) {
    
    out.println(line);
    
    }
    
    out.println("</BODY></HTML>");
    
     
    
    
    %>

    运行show_response_compressed.jsp可以产生类似如下截图中的页面。

     

     

     

    运行TestGZIP.jsp,在URL中填入show_response_compressed.jsp 所在的地址,提交后即可得到如下结果。

     


     

    可以看到,未经压缩的HTTP Response数据量达到了2,950,086 bytes (2.9MB),而压缩后的数据量仅有8,687 bytes(8 KB),压缩比高达99.7%!

     


    因为是在一台机器上作的验证,所以在响应时间的改善方面感觉不是很明显。同样的,如果是在Intranet环境内,则这种效果也不会很明显。如果是在Internet上测试,改善的效果应该会比较明显。

    附上源码

    /*
     * Copyright 2003 Jayson Falkner (jayson@jspinsider.com)
     * This code is from "Servlets and JavaServer pages; the J2EE Web Tier",
     * http://www.jspbook.com. You may freely use the code both commercially
     * and non-commercially. If you like the code, please pick up a copy of
     * the book and help support the authors, development of more free code,
     * and the JSP/Servlet/J2EE community.
     */
    package cn.javass.common.web.filter;
    
    /*使用gzip优化web应用(filter实现)*/
    import javax.servlet.*;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import java.io.IOException;
    
    public class GZIPFilter implements Filter {
    
        private static Logger log = LoggerFactory.getLogger(GZIPFilter.class);
    
        public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
            if (req instanceof HttpServletRequest) {
                HttpServletRequest request = (HttpServletRequest) req;
                HttpServletResponse response = (HttpServletResponse) res;
                String ae = request.getHeader("accept-encoding");
                if (ae != null && ae.indexOf("gzip") != -1) {
                    log.debug("GZIP supported, compressing.");
                    GZIPResponseWrapper wrappedResponse = new GZIPResponseWrapper(response);
                    chain.doFilter(req, wrappedResponse);
                    wrappedResponse.finishResponse();
                    return;
                }
                chain.doFilter(req, res);
            }
        }
    
        public void init(FilterConfig filterConfig) {
            // noop
        }
    
        public void destroy() {
            // noop
        }
    }
    /*
     * Copyright 2003 Jayson Falkner (jayson@jspinsider.com)
     * This code is from "Servlets and JavaServer pages; the J2EE Web Tier",
     * http://www.jspbook.com. You may freely use the code both commercially
     * and non-commercially. If you like the code, please pick up a copy of
     * the book and help support the authors, development of more free code,
     * and the JSP/Servlet/J2EE community.
     */
    package cn.javass.common.web.filter;
    
    import javax.servlet.ServletOutputStream;
    import javax.servlet.http.HttpServletResponse;
    import java.io.ByteArrayOutputStream;
    import java.io.IOException;
    import java.util.zip.GZIPOutputStream;
    
    public class GZIPResponseStream extends ServletOutputStream {
    
        protected ByteArrayOutputStream baos = null;
        protected GZIPOutputStream gzipstream = null;
        protected boolean closed = false;
        protected HttpServletResponse response = null;
        protected ServletOutputStream output = null;
    
        public GZIPResponseStream(HttpServletResponse response) throws IOException {
            super();
            closed = false;
            this.response = response;
            this.output = response.getOutputStream();
            baos = new ByteArrayOutputStream();
            gzipstream = new GZIPOutputStream(baos);
        }
    
        @Override
        public void close() throws IOException {
            if (closed) {
                throw new IOException("This output stream has already been closed");
            }
            gzipstream.finish();
    
            byte[] bytes = baos.toByteArray();
    
            //   response.addHeader("Content-Length", Integer.toString(bytes.length));
            response.addHeader("Content-Encoding", "gzip");
            output.write(bytes);
            output.flush();
            output.close();
            closed = true;
        }
    
        @Override
        public void flush() throws IOException {
            if (closed) {
                throw new IOException("Cannot flush a closed output stream");
            }
            gzipstream.flush();
        }
    
        @Override
        public void write(int b) throws IOException {
            if (closed) {
                throw new IOException("Cannot write to a closed output stream");
            }
            gzipstream.write((byte) b);
        }
    
        @Override
        public void write(byte b[]) throws IOException {
            write(b, 0, b.length);
        }
    
        @Override
        public void write(byte b[], int off, int len) throws IOException {
            if (closed) {
                throw new IOException("Cannot write to a closed output stream");
            }
            gzipstream.write(b, off, len);
        }
    
        public boolean closed() {
            return (this.closed);
        }
    
        public void reset() {
            //noop
        }
    }
    /*
     * Copyright 2003 Jayson Falkner (jayson@jspinsider.com)
     * This code is from "Servlets and JavaServer pages; the J2EE Web Tier",
     * http://www.jspbook.com. You may freely use the code both commercially
     * and non-commercially. If you like the code, please pick up a copy of
     * the book and help support the authors, development of more free code,
     * and the JSP/Servlet/J2EE community.
     */
    package cn.javass.common.web.filter;
    
    import javax.servlet.ServletOutputStream;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpServletResponseWrapper;
    import java.io.IOException;
    import java.io.OutputStreamWriter;
    import java.io.PrintWriter;
    
    public class GZIPResponseWrapper extends HttpServletResponseWrapper {
    
        protected HttpServletResponse origResponse = null;
        protected ServletOutputStream stream = null;
        protected PrintWriter writer = null;
    
        public GZIPResponseWrapper(HttpServletResponse response) {
            super(response);
            origResponse = response;
        }
    
        public ServletOutputStream createOutputStream() throws IOException {
            return (new GZIPResponseStream(origResponse));
        }
    
        public void finishResponse() {
            try {
                if (writer != null) {
                    writer.close();
                }
                else {
                    if (stream != null) {
                        stream.close();
                    }
                }
            }
            catch (IOException e) {
            }
        }
    
        @Override
        public void flushBuffer() throws IOException {
            stream.flush();
        }
    
        @Override
        public ServletOutputStream getOutputStream() throws IOException {
            if (writer != null) {
                throw new IllegalStateException("getWriter() has already been called!");
            }
    
            if (stream == null) stream = createOutputStream();
            return (stream);
        }
    
        @Override
        public PrintWriter getWriter() throws IOException {
            if (writer != null) {
                return (writer);
            }
    
            if (stream != null) {
                throw new IllegalStateException("getOutputStream() has already been called!");
            }
    
            stream = createOutputStream();
            writer = new PrintWriter(new OutputStreamWriter(stream, "UTF-8"));
            return (writer);
        }
    
        @Override
        public void setContentLength(int length) {
        }
    }

    web.xml

                    <!--GZIP文件压缩的应用  -->
                <filter>
                    <filter-name>GZIPFilter</filter-name>
                    <filter-class>cn.javass.common.web.filter.GZIPFilter</filter-class>
                </filter>
                <filter-mapping>
                    <filter-name>GZIPFilter</filter-name>
                    <url-pattern>/*</url-pattern>
                </filter-mapping>
  • 相关阅读:
    js中location.href的用法
    entityframework单例模式泛型用法
    [C#]从URL中获取路径的最简单方法-new Uri(url).AbsolutePath
    等到花儿也谢了的await
    ASP.NET MVC下的异步Action的定义和执行原理
    实际案例:在现有代码中通过async/await实现并行
    ASP.NET MVC Controllers and Actions
    扩展HtmlHelper
    iOS开发网络篇—XML数据的解析
    IOS学习:常用第三方库(GDataXMLNode:xml解析库)
  • 原文地址:https://www.cnblogs.com/crazylqy/p/4326299.html
Copyright © 2020-2023  润新知