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

相关知识:javascript

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

 

1.实现:  html

Tomcat 开启Gzip :java

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

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:app

<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>
相关文章
相关标签/搜索