使用gzip优化web应用(filter实现)
相关知识:
gzip是http协议中使用的一种加密算法,客户端向web服务器端发出了请求后,通常情况下服务器端会将页面文件和其他资源,返回到客户端,客户端加载后渲染呈现,这种情况文件一般都比较大,如果开启Gzip ,那么服务器端响应后,会将页面,JS,CSS等文本文件或者其他文件通过高压缩算法将其压缩,然后传输到客户端,由客户端的浏览器负责解压缩与呈现。通常能节省40%以上的流量(一般都有60%左右),一些PHP,JSP文件也能够进行压缩。
1.实现:
Tomcat 开启Gzip :
1.找到Tomcat 目录下的conf下的server.xml,并找到如下信息
将它改成如下的形式(其实在上面代码的下面已经有了,将他们打开而已。):
这样,就能够对html和xml进行压缩了,如果要压缩css 和 js,那么需要将
compressableMimeType=”text/html,text/xml”加入css和js:
你甚至可以压缩图片:
compressableMimeType=”text/html,text/xml”加入css和js:
开启后重启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>
使用gzip优化web应用(filter实现)的更多相关文章
- 优化Web中的性能
优化Web中的性能 简介 web的优化就是一场阻止http请求最终访问到数据库的战争. 优化的方式就是加缓存,在各个节点加缓存. web请求的流程及节点 熟悉流程及节点,才能定位性能的问题.而且优化的 ...
- HTML5探秘:用requestAnimationFrame优化Web动画
本文转载自: HTML5探秘:用requestAnimationFrame优化Web动画
- AMD和RequireJS初识----优化Web应用前端(按需动态加载JS)
RequireJS是一个非常小巧的JavaScript模块载入框架,是AMD规范最好的实现者之一.最新版本的RequireJS压缩后只有14K,堪称非常轻量.它还同时可以和其他的框架协同工作,使用Re ...
- java web之Filter详解
java web之Filter详解 2012-10-20 0 个评论 作者:chenshufei2 收藏 我要投稿 .概念: Filter也称之为过滤器,它是Servlet技术中比较激动人心的技术,W ...
- 如何合理优化WEB前端 高效提升WEB前端性能
对前端开发工程师来说,前端性能优化的重要性是不言而喻的,最为大家所知的是YSLOW的23条优化规则,在我的理解中,性能优化不纯粹是指用户访问网站的速度,也包括开发的效率,这里我总结下我理解中的WEB前 ...
- 【转】优化Web程序的最佳实践
自动排版有点乱,看着蛋疼,建议下载中文PDF版阅读或阅读英文原文. Yahoo!的Exceptional Performance团队为改善Web性能带来最佳实践.他们为此进行了 一系列的实验.开发了各 ...
- 性能优化——Web前端性能优化
核心知识点: 1.排查网站性能瓶颈的手法:分析各个环节的日志,找出异常部分 2.Web前端:网站业务逻辑之前的部分(浏览器.图片服务.CDN) 3.优化手段 1)浏览器优化 (1)减少http请求 a ...
- 十条服务器端优化Web性能的技巧
服务器 远程桌面连接工具 提高web应用的性能从来没有比现在更重要过.网络经济的比重一直在增长;全球经济超过5%的价值是在因特网上产生的(数据参见下面的资料).这个时刻在线的超连接世界意味着用户对其 ...
- 十条服务器端优化Web性能的技巧总结
原文地址:http://www.jb51.net/yunying/452723.html 提高 web 应用的性能从来没有比现在更重要过.网络经济的比重一直在增长:全球经济超过 5% 的价值是在因特网 ...
随机推荐
- a标签传值乱码问题怎么解?
- StarUML的9种图
UML的九种图:用例图,类图,对象图,状态图,活动图,序列图,协作图,构件图,部署图.外加包图. (一).用例图 1.定义:由参与者(Actor).用例(Use Case)以及它们之间的关系构成的用于 ...
- 判断iframe页面是否加载完成
if (frames("appIframe").document.readyState !="complete") { alert(& ...
- 业务订单号生成算法,每秒50W左右,不同机器保证不重复,包含日期可读性好
参考snowflace算法,基本思路: 序列12位(更格式化的输出后,性能损耗导致每毫秒生成不了这么多,所以可以考虑减少这里的位,不过留着也并无影响) 机器位10位 毫秒为左移 22位 上述几个做或运 ...
- HashMap 学习笔记
先摆上JDK1.8中HashMap的类注释:我翻译了一下 /** * Hash table based implementation of the <tt>Map</tt> i ...
- linux中移动光标
ctrl + e 光标 移动到 行尾 ctrl + a 光标 移动到 行首
- Excel基本操作1
Excel的基本操作之二,录入及快速填充.不足之处,欢迎补充
- 基于Activiti的流程应用开发平台JSAAS-WF V5.3
第1章 产品概述及体系架构 1.1.概述 红迅JSAAS-WF工作流平台V5是广州红迅软件有限公司面向合作伙伴以及有IT运维团队中大型企业提供新一代的流程管理产品,它基于流行的JAVA开源技术上构建, ...
- 【NLP】3000篇搜狐新闻语料数据预处理器的python实现
3000篇搜狐新闻语料数据预处理器的python实现 白宁超 2017年5月5日17:20:04 摘要: 关于自然语言处理模型训练亦或是数据挖掘.文本处理等等,均离不开数据清洗,数据预处理的工作.这里 ...
- Elasticsearch5中安装Elasticsearch-head插件
介绍 elasticsearch-head是一个用于管理Elasticsearch的web前端插件,搞过ES的同学应该都了解.该插件在es5中可以以独立服务的形式进行安装使用.本文将介绍如何操作. 相 ...