前端性能调优Gzip Filter

时间:2022-10-21 00:49:10

转自:https://blog.csdn.net/zxk15982106569/article/details/18922613

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

现在主流浏览器都是支持gzip的。服务器压缩网页后进行传输,可减少传输数据的大小使用户感觉访问速度更快。当然,压缩也会消耗一部分服务器处理时间。

Web服务器处理HTTP压缩的过程如下:
1. Web服务器接收到浏览器的HTTP请求后,检查浏览器是否支持HTTP压缩(Accept-Encoding 信息);
2. 如果浏览器支持HTTP压缩,Web服务器检查请求文件的后缀名;
3. 如果请求文件是HTML、CSS等静态文件,Web服务器到压缩缓冲目录中检查是否已经存在请求文件的最新压缩文件;
4. 如果请求文件的压缩文件不存在,Web服务器向浏览器返回未压缩的请求文件,并在压缩缓冲目录中存放请求文件的压缩文件;
5. 如果请求文件的最新压缩文件已经存在,则直接返回请求文件的压缩文件;
6. 如果请求文件是动态文件,Web服务器动态压缩内容并返回浏览器,压缩内容不存放到压缩缓存目录中。

下面是两个演示图:

未使用Gzip:
前端性能调优Gzip Filter

开启使用Gzip后:
前端性能调优Gzip Filter

为了进行比较,我们先给个截图,这是没有启用Gzip的情况:

前端性能调优Gzip Filter

从这里可以看出,在启用Gzip之前,下载ext-all-debug.js需要2.8MB这么大的文件,需要用时1.53秒。

配置Gzip Filter后:

我们打开Firebug进行再次测试,作为结果的比较,果然性能提升很大:

前端性能调优Gzip Filter

从这我们不难看出,现在的Resource Header中 Content-Encoding被设置成了gzip,所以这表示我们的gzip的功能已经被正确的开启,然后我们发现ext-all-debug.js的尺寸从2.8MB缩小成了只有550KB,而且网络的传输时间从1.53秒缩短到了906毫秒,几乎减少了40%的下载时间,其他的资源也都相应的下载时间有了大幅度的减少。可见效果很明显。

应用(2种方式开启GZIP功能):

一.通过Tomcat 开启Gzip :

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

<Connectorport="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" >

如图:前端性能调优Gzip Filter

这样,就能够对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。

二.通过代码开启Gzip :

源码参看F:\odcdownload\IT资料\IT\GZIP filter\tk-filters-1.0.1.zip

解释:

.jsp进行gzip压缩可行方法

一、首先,去http://sourceforge.net/projects/filterlib网站下载tk-filters-1.0.1.zip。
二、解压这个tk-filters-1.0.1.zip压缩文件,将解压后的文件tk-filters.jar放在Ext项目的WEB-INF/lib/下。
三、打开解压后的文件夹tk-filters\conf\tk-filters.properties
GZIPFilter.Enabled=false(默认为false,true打开GZIP压缩功能)
GZIPFilter.LogStats=false(默认为false,true打开GZIP压缩功能日志,可以在后台看到压缩比例信息)
CacheFilter.Enabled=false(默认为false,true打开GZIP缓存功能)
注:可以自行选择想打开的功能,再将此文件复制到Ext项目的WEB-INF/class文件夹下(我这边eclipse3.5是WEB-INF/classes下面)

四、打开Ext项目的WEB-INF/web.xml文件
<!-- GZIPFilter压缩定义 设置此项时tk-filters.properties的GZIPFilter.Enabled=true才可用-->
  <filter>
    <filter-name>GZIPFilter</filter-name>
    <filter-class>com.tacitknowledge.filters.gzipfilter.GZIPFilter</filter-class>
  </filter>
  <!-- GZIPFilter 设置自己想要压缩的文件类型-->  
  <filter-mapping>
    <filter-name>GZIPFilter</filter-name>
    <url-pattern>*.js</url-pattern>
  </filter-mapping>
  <filter-mapping>
    <filter-name>GZIPFilter</filter-name>
    <url-pattern>*.jsp</url-pattern>
  </filter-mapping>
  <!-- CacheFilter缓存定义 设置此项时tk-filters.properties的CacheFilter.Enabled=true才可用 -->
  <filter>
    <filter-name>CacheFilter</filter-name>
    <filter-class>com.tacitknowledge.filters.cache.CacheHeaderFilter</filter-class>
  </filter>
  <!-- CacheFilter 设置自己想要缓存的文件类型-->
  <filter-mapping>
    <filter-name>CacheFilter</filter-name>
    <url-pattern>*.gif</url-pattern>
  </filter-mapping>
  <filter-mapping>
    <filter-name>CacheFilter</filter-name>
    <url-pattern>*.jpg</url-pattern>
  </filter-mapping>
  <filter-mapping>
    <filter-name>CacheFilter</filter-name>
    <url-pattern>*.png</url-pattern>
  </filter-mapping>
  <filter-mapping>
    <filter-name>CacheFilter</filter-name>
    <url-pattern>*.js</url-pattern>
  </filter-mapping>
  <filter-mapping>
    <filter-name>CacheFilter</filter-name>
    <url-pattern>*.css</url-pattern>
  </filter-mapping>

五、测试:用的也是FireFox,以及FireBug调试测试。我的项目中用的Ext是3.2.1,ext-all.js为662KB。压缩后得到的大小仅为181K,压缩率达到了27%,页面加载速度得到了很大的提高。

前端性能调优Gzip Filter 以下是控制台打出来的压缩日志(GZIPFilter.LogStats=true会显示):
前端性能调优Gzip Filter

下面为大致的模仿以便理解:

(1).CachedResponseWrapper类 
    实现定制输出的关键是对HttpServletResponse 进行包装,截获所有的输出,等到过滤器链处理完毕后,再对截获的输出进行处理,并写入到真正的HttpServletResponse 对象中。JavaEE 框架已经定义了一个HttpServletResponseWrapper 类使得包装HttpServletResponse 更加容易。我们扩展这个HttpServletResponseWrapper,截获所有的输出,并保存到ByteArrayOutputStream 中。 
    定制的包装响应能方便地从帮助类 HttpServletResponseWrapper 中导出。这一类粗略地执行许多方法,允许我们简单地覆盖 getOutputStream() 方法以及 getWriter() 方法,提供了定制输出流的实例。 
    HttpServletResponseWrapper这个类的使用包括以下五个步骤: 
1)建立一个响应包装器。扩展javax.servlet.http.HttpServletResponseWrapper。 
2)提供一个缓存输出的PrintWriter。重载getWriter方法,返回一个保存发送给它的所有东西的PrintWriter,并把结果存进一个可以稍后访问的字段中。 
3)传递该包装器给doFilter。此调用是合法的,因为HttpServletResponseWrapper实现HttpServletResponse。 
4)提取和修改输出。在调用FilterChain的doFilter方法后,原资源的输出只要利用步骤2中提供的机制就可以得到。只要对你的应用适合,就可以修改或替换它。 
5)发送修改过的输出到客户机。因为原资源不再发送输出到客户机(这些输出已经存放到你的响应包装器中了),所以必须发送这些输出。这样,你的过滤器需要从原响应对象中获得PrintWriter或OutputStream,并传递修改过的输出到该流中。

  1. /**
  2. * Wrapper:在内存中开辟一个ByteOutputStream,然后将拦截的响应写入byte[],
  3. * 写入完毕后,再将wrapper的byte[]写入真正的response对象
  4. * This class is used for wrapped response for getting cached data.
  5. */
  6. class CachedResponseWrapper extends HttpServletResponseWrapper {
  7. /**
  8. * Indicate that getOutputStream() or getWriter() is not called yet.
  9. */
  10. public static final int OUTPUT_NONE = 0;
  11. /**
  12. * Indicate that getWriter() is already called.
  13. */
  14. public static final int OUTPUT_WRITER = 1;
  15. /**
  16. * Indicate that getOutputStream() is already called.
  17. */
  18. public static final int OUTPUT_STREAM = 2;
  19. private int outputType = OUTPUT_NONE;
  20. private int status = SC_OK;
  21. private ServletOutputStream output = null;
  22. private PrintWriter writer = null;
  23. private ByteArrayOutputStream buffer = null;
  24. public CachedResponseWrapper(HttpServletResponse resp) throws IOException {
  25. super(resp);
  26. buffer = new ByteArrayOutputStream();
  27. }
  28. public int getStatus() {
  29. return status;
  30. }
  31. public void setStatus(int status) {
  32. super.setStatus(status);
  33. this.status = status;
  34. }
  35. public void setStatus(int status, String string) {
  36. super.setStatus(status, string);
  37. this.status = status;
  38. }
  39. public void sendError(int status, String string) throws IOException {
  40. super.sendError(status, string);
  41. this.status = status;
  42. }
  43. public void sendError(int status) throws IOException {
  44. super.sendError(status);
  45. this.status = status;
  46. }
  47. public void sendRedirect(String location) throws IOException {
  48. super.sendRedirect(location);
  49. this.status = SC_MOVED_TEMPORARILY;
  50. }
  51. public PrintWriter getWriter() throws IOException {
  52. if (outputType == OUTPUT_STREAM)
  53. throw new IllegalStateException();
  54. else if (outputType == OUTPUT_WRITER)
  55. return writer;
  56. else {
  57. outputType = OUTPUT_WRITER;
  58. writer = new PrintWriter(new OutputStreamWriter(buffer,
  59. getCharacterEncoding()));
  60. return writer;
  61. }
  62. }
  63. public ServletOutputStream getOutputStream() throws IOException {
  64. if (outputType == OUTPUT_WRITER)
  65. throw new IllegalStateException();
  66. else if (outputType == OUTPUT_STREAM)
  67. return output;
  68. else {
  69. outputType = OUTPUT_STREAM;
  70. output = new WrappedOutputStream(buffer);
  71. return output;
  72. }
  73. }
  74. public void flushBuffer() throws IOException {
  75. if (outputType == OUTPUT_WRITER)
  76. writer.flush();
  77. if (outputType == OUTPUT_STREAM)
  78. output.flush();
  79. }
  80. public void reset() {
  81. outputType = OUTPUT_NONE;
  82. buffer.reset();
  83. }
  84. /**
  85. * Call this method to get cached response data.
  86. *
  87. * @return byte array buffer.
  88. * @throws IOException
  89. */
  90. public byte[] getResponseData() throws IOException {
  91. flushBuffer();
  92. return buffer.toByteArray();
  93. }
  94. /**
  95. * This class is used to wrap a ServletOutputStream and store output stream
  96. * in byte[] buffer.
  97. */
  98. class WrappedOutputStream extends ServletOutputStream {
  99. private ByteArrayOutputStream buffer;
  100. public WrappedOutputStream(ByteArrayOutputStream buffer) {
  101. this.buffer = buffer;
  102. }
  103. public void write(int b) throws IOException {
  104. buffer.write(b);
  105. }
  106. public byte[] toByteArray() {
  107. return buffer.toByteArray();
  108. }
  109. }
  110. }

(2).GZipFilter类

  1. public class GZipFilter implements Filter {
  2. public void init(FilterConfig arg0) throws ServletException {
  3. }
  4. public void doFilter(ServletRequest request, ServletResponse response,
  5. FilterChain chain) throws IOException, ServletException {
  6. HttpServletResponse httpResponse = (HttpServletResponse) response;
  7. CachedResponseWrapper wrapper = new CachedResponseWrapper(httpResponse);
  8. // 写入wrapper:
  9. chain.doFilter(request, wrapper);
  10. // 对响应进行处理,这里是进行GZip压缩:
  11. byte[] data = GZipUtil.gzip(wrapper.getResponseData());
  12. httpResponse.setHeader("Content-Encoding", "gzip");
  13. httpResponse.setContentLength(data.length);
  14. ServletOutputStream output = response.getOutputStream();
  15. output.write(data);
  16. output.flush();
  17. }
  18. public void destroy() {
  19. }
  20. }

(3).GZipUtil类

  1. public final class GZipUtil {
  2. /** * Do a gzip operation. */
  3. public static byte[] gzip(byte[] data) {
  4. ByteArrayOutputStream byteOutput = new ByteArrayOutputStream(10240);
  5. GZIPOutputStream output = null;
  6. try {
  7. output = new GZIPOutputStream(byteOutput);
  8. output.write(data);
  9. } catch (IOException e) {
  10. throw new RuntimeException("G-Zip failed.", e);
  11. } finally {
  12. if (output != null) {
  13. try {
  14. output.close();
  15. } catch (IOException e) {
  16. }
  17. }
  18. }
  19. return byteOutput.toByteArray();
  20. }
  21. }

(4).在web.xml中配置 GZipFilter

    1. <filter>
    2. <filter-name>GZipFilter</filter-name>
    3. <filter-class>com.logcd.filter.GZipFilter</filter-class>
    4. </filter>
    5. <filter-mapping>
    6. <filter-name>GZipFilter</filter-name>
    7. <url-pattern>*.html</url-pattern>
    8. </filter-mapping>