`

【转】Java 高性能Web 开发(5)-GZIP 压缩

 
阅读更多

为了减少传输的数据,压缩是一个不错的选择,而 HTTP 协议支持 GZIP 的压缩格式,服务器响应的报头包含 Content-Encoding: gzip,它告诉浏览器,这个响应的返回数据,已经压缩成 GZIP 格式,浏览器获得数据后要进行解压缩操作。这在一定程度可以减少服务器传输的数据,提高系统性能。那么如何给服务器响应添加 Content-Encoding: gzip 报头,同时压缩响应数据呢?如果你用的是 Tomcat 服务器,打开 $tomcat_home$/conf/server.xml 文件,对 Connector 进行配置,配置如清单 8 所示。

清单 8. TOMCAT 配置清单

<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" />

 我们为 Connector 添加了如下几个属性,他们意义分别是:

 

compression="on" 打开压缩功能

 

compressionMinSize="2048" 启用压缩的输出内容大小,这里面默认为 2KB

 

noCompressionUserAgents="gozilla, traviata" 对于以下的浏览器,不启用压缩

 

compressableMimeType="text/html,text/xml, image/png" 压缩类型

 

有时候,我们无法配置 server.xml,比如如果我们只是租用了别人的空间,但是它并没有启用 GZIP,那么我们就要使用程序启用 GZIP 功能。我们将需要压缩的文件,放到指定的文件夹,使用一个过滤器,过滤对这个文件夹里文件的请求。

清单 9. 自定义 Filter 压缩 GZIP

// 监视对 gzipCategory 文件夹的请求
 @WebFilter(urlPatterns = { "/gzipCategory/*" }) 
 public class GZIPFilter implements Filter { 

 @Override 
 public void doFilter(ServletRequest request, ServletResponse response, 
 FilterChain chain) throws IOException, ServletException { 
 String parameter = request.getParameter("gzip"); 
 // 判断是否包含了 Accept-Encoding 请求头部
 HttpServletRequest s = (HttpServletRequest)request; 
 String header = s.getHeader("Accept-Encoding"); 
 //"1".equals(parameter) 只是为了控制,如果传入 gzip=1,才执行压缩,目的是测试用
 if ("1".equals(parameter) && header != null && header.toLowerCase().contains("gzip")) { 
 HttpServletResponse resp = (HttpServletResponse) response; 
 final ByteArrayOutputStream buffer = new ByteArrayOutputStream(); 

 HttpServletResponseWrapper hsrw = new HttpServletResponseWrapper( 
 resp) { 

 @Override 
 public PrintWriter getWriter() throws IOException { 
 return new PrintWriter(new OutputStreamWriter(buffer, 
 getCharacterEncoding())); 
 } 

 @Override 
 public ServletOutputStream getOutputStream() throws IOException { 
 return new ServletOutputStream() { 

 @Override 
 public void write(int b) throws IOException { 
 buffer.write(b); 
 } 
 }; 
 } 

 }; 

 chain.doFilter(request, hsrw); 
 byte[] gzipData = gzip(buffer.toByteArray()); 
 resp.addHeader("Content-Encoding", "gzip"); 
 resp.setContentLength(gzipData.length); 
 ServletOutputStream output = response.getOutputStream(); 
 output.write(gzipData); 
 output.flush(); 
 } else { 
 chain.doFilter(request, response); 
 } 
 } 
 // 用 GZIP 压缩字节数组
 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(); 
 } 
……
 }

 该程序的主体思想,是在响应流写回之前,对响应的字节数据进行 GZIP 压缩,因为并不是所有的浏览器都支持 GZIP 解压缩,如果浏览器支持 GZIP 解压缩,会在请求报头的 Accept-Encoding 里包含 gzip。这是告诉服务器浏览器支持 GZIP 解压缩,因此如果用程序控制压缩,为了保险起见,还需要判断浏览器是否发送 accept-encoding: gzip 报头,如果包含了该报头,才执行压缩。为了验证压缩前后的情况,使用 Firebug 监控请求和响应报头。

清单 10. 压缩前请求

GET /testProject/gzipCategory/test.html HTTP/1.1 
 Accept: */* 
 Accept-Language: zh-cn 
 Accept-Encoding: gzip, deflate 
 User-Agent: Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1) 
 Host: localhost:9090 
 Connection: Keep-Alive

 清单 11. 不压缩的响应

HTTP/1.1 200 OK 
 Server: Apache-Coyote/1.1 
 ETag: W/"5060-1242444154000"
 Last-Modified: Sat, 16 May 2009 03:22:34 GMT 
 Content-Type: text/html 
 Content-Length: 5060
 Date: Mon, 18 May 2009 12:29:49 GMT 

 清单 12. 压缩后的响应

HTTP/1.1 200 OK 
 Server: Apache-Coyote/1.1 
 ETag: W/"5060-1242444154000"
 Last-Modified: Sat, 16 May 2009 03:22:34 GMT 
 Content-Encoding: gzip
 Content-Type: text/html 
 Content-Length: 837
 Date: Mon, 18 May 2009 12:27:33 GMT 

 可以看到,压缩后的数据比压缩前数据小了很多。压缩后的响应报头包含 Content-Encoding: gzip。同时 Content-Length 包含了返回数据的大小。GZIP 压缩是一个重要的功能,前面提到的是对单一服务器的压缩优化,在高并发的情况,多个 Tomcat 服务器之前,需要采用反向代理的技术,提高并发度,而目前比较火的反向代理是 Nginx(这在后续的文章会进行详细的介绍)。对 Nginx HTTP 配置部分里增加如下配置。

清单 13. Nginx GZIP 配置

gzip  on; 
 gzip_min_length  1000; 
 gzip_buffers     4 8k; 
 gzip_types       text/plain application/x-javascript text/css text/html application/xml; 

 由于 Nginx 具有更高的性能,利用该配置可以更好的提高性能。在高性能服务器上该配置将非常有用。

原创文章@java教程网 转载请标明出处

java教程网编辑发布:希望本系列的文章对你的个人成长和发展有帮助。

java新手入门,开发工具 Java进阶,高级编程,java教程网几乎囊括了java编程的所有方面的资料

分享到:
评论

相关推荐

    Java压缩技术(四) GZIP——Java原生实现

    NULL 博文链接:https://snowolf.iteye.com/blog/643010

    java源码包---java 源码 大量 实例

     用JAVA开发的一个小型的目录监视系统,系统会每5秒自动扫描一次需要监视的目录,可以用来监视目录中文件大小及文件增减数目的变化。 Java日期选择控件完整源代码 14个目标文件 内容索引:JAVA源码,系统相关,日历,...

    成百上千个Java 源码DEMO 4(1-4是独立压缩包)

    Java目录监视器源程序 9个目标文件 内容索引:JAVA源码,综合应用,目录监视 用JAVA开发的一个小型的目录监视系统,系统会每5秒自动扫描一次需要监视的目录,可以用来监视目录中文件大小及文件增减数目的变化。...

    成百上千个Java 源码DEMO 3(1-4是独立压缩包)

    Java目录监视器源程序 9个目标文件 内容索引:JAVA源码,综合应用,目录监视 用JAVA开发的一个小型的目录监视系统,系统会每5秒自动扫描一次需要监视的目录,可以用来监视目录中文件大小及文件增减数目的变化。...

    JAVA上百实例源码以及开源项目源代码

     用JAVA开发的一个小型的目录监视系统,系统会每5秒自动扫描一次需要监视的目录,可以用来监视目录中文件大小及文件增减数目的变化。 Java日期选择控件完整源代码 14个目标文件 内容索引:JAVA源码,系统相关,日历,...

    JAVA上百实例源码以及开源项目

     用JAVA开发的一个小型的目录监视系统,系统会每5秒自动扫描一次需要监视的目录,可以用来监视目录中文件大小及文件增减数目的变化。 Java日期选择控件完整源代码 14个目标文件 内容索引:JAVA源码,系统相关,日历,...

    java源码包4

     用JAVA开发的一个小型的目录监视系统,系统会每5秒自动扫描一次需要监视的目录,可以用来监视目录中文件大小及文件增减数目的变化。 Java日期选择控件完整源代码 14个目标文件 内容索引:JAVA源码,系统相关,日历...

    java源码包3

     用JAVA开发的一个小型的目录监视系统,系统会每5秒自动扫描一次需要监视的目录,可以用来监视目录中文件大小及文件增减数目的变化。 Java日期选择控件完整源代码 14个目标文件 内容索引:JAVA源码,系统相关,日历...

    java源码包2

     用JAVA开发的一个小型的目录监视系统,系统会每5秒自动扫描一次需要监视的目录,可以用来监视目录中文件大小及文件增减数目的变化。 Java日期选择控件完整源代码 14个目标文件 内容索引:JAVA源码,系统相关,日历...

    openscoring:REST Web服务,用于R,Scikit-Learn和Apache Spark模型的真实实时评分(&lt;1毫秒)

    高性能和高吞吐量: 亚毫秒级的响应时间。 使用gzip和deflate编码进行请求和响应压缩。 线程安全。 开放,可扩展的体系结构,可轻松与专有系统和服务集成: 用户身份验证和授权。 先决条件 Java 1.8或更高版本...

Global site tag (gtag.js) - Google Analytics