日期:2014-05-16  浏览次数:20780 次

利用GWT开发高性能Ajax应用

近日,InfoQ发表了Ryan Dewsbury所著的Google Web Toolkit Application 》书中的"Integrating with a GWT-RPC Servlet" 一章。

对性能的提升是Ajax受欢迎的主要原因。我们通常以为那些所谓的眩目变换对于用户来说是Ajax最吸引人的地方,可能用户也确实由于这个原因而对 Ajax独有情钟。如果你回头去看那些传统的web应用,会发现它们几乎静态到令人反感,所以说用户仅仅出于这些眩目变换而选择Ajax不无道理。然而, 如果说眩目的变换真得大大改善了用户体验的话,那么动态的gif图片应该受到更广泛的应用才是。谢天谢地,Web应用早已走过这种幼稚的时代。Ajax不 会再重复动态gif图片的老路,它不会再把重点放在这类眩目的变换上了。因此,无论人们是否感受或是意识到,Ajax真正改善用户体验的地方还是在对性能 的提升上。

这篇文章的重点并非要说明Ajax天生在哪些方面比传统Web应用优秀。关于这个问题,只要将Google地图与其他Web地图或者将Gmail与 Hotmail进行对比,自然就可以得出结论。当然,应用Ajax的确能显著改善性能和用户体验。但在此,我要向大家展示的是如何将Ajax应用的性能提 高到一个新的层次——从而使您的应用脱颖而出。

选择GWT的理由

Google Web Toolkit (GWT)将Ajax的开发推进了一大步,然而面对当下种类繁多的Ajax解决方案,此类新技术的推广难免遇到种种挑战。但无可否认,在Ajax开发方 面,GWT给开发者提供了其他解决方案无可比拟的便利。如果你还没有受到任何开发框架束缚的话,实在没有什么理由不选择GWT,因为GWT能够无偿的使应 用的整体性能得到大幅度提升。

我所说的“无偿”是指在开发中可以抛开性能问题不考虑,而将主要精力集中在业务逻辑方面,因为GWT本身已能使性能得到优化。GWT带有一个能将 Java代码编译成JavaScript的编译器。如果熟悉编译语言(C、Java等等)的话,你一定了解平台独立性是此类语言追求的一个目标,因此其编 译器能够针对特定平台对代码进行优化,这样程序员就可以将重点放在代码的结构组织和可读性上。GWT编译器也做了类似的事情,它将Java代码编译成一些 高度优化的JavaScript文件,每个文件对应于一种特定的浏览器,其中的优化步骤还应用了普通编译器中的优化方法,去掉了没有被调用的函数和内联代 码。此种方式得到的代码相对直接编写的JavaScript代码要小的多而且做到了浏览器独立,因此执行效率较高。实质上GWT已将JavaScript 看作web中的汇编代码来处理。在浏览器加载JavaScript代码的时候,仅仅加载针对该浏览器所需的代码而已。使用GWT的应用比任何直接用 JavaScript实现的应用要来得更精炼更快。对即将发布的GWT 1.5版本,GWT开发团队坚信其编译器生成的代码会比其他任何手工编写的代码都要快。以上这些应该足以说服大家选用GWT作为Ajax的解决方案,如果 还不够,还有许多其他充分的理由,比如你可以在开发GWT程序时应用某些Java开发工具(能用Eclipse来调试Ajax程序在我看来确实是一个非常 有分量的砝码)。

锦上添花

还远不止这些呢!Ajax已经比传统web应用要出色得多,而GWT又远超一般的Ajax技术。只简单地做些技术决定就能让你将大部分精力放在业务 功能上,达到事半功倍的效果,开发出完美的应用。当然,GWT并非凭空就能做到这些,下面我将讲述几种进一步提升GWT性能方法。

1、始终做好缓存

当你将GWT的Java代码编译成JavaScript后,对应于每个浏览器版本都会有生成一个相应的文件,该文件采用唯一标识的文件名。这些就是 你的应用程序的代码文件,直接把它们放到一个web服务器上就能发布你的应用了。由于文件名是通过对你的代码进行Hash函数计算而得,所以文件名本身就 已包含了版本信息。如果你修改了代码后重新编译,生成的文件会有新的文件名。这意味着要么文件已经被下载到了本地浏览器,要么从来没有被请求过,因此就没 有必要用检查文件修改日期(HTTP的If-Modified-Since头)的方法来决定是否需要版本更新。这样可以减少很多不必要的HTTP请求过 程,虽然这些请求过程单独可能很微不足道,但是当用户量达到一定程度,它们就会变成不得不考虑的因素。这类请求对客户端来说也是一种拖累,因为对同一个应 用,每个浏览器最多只能有两个活动的请求。很多对Ajax下载时间的优化都是从减少向服务器发送的请求量入手的。

为了避免浏览器对版本的请求,你可以通过配置web服务器来向客户端发送Expires HTTP头。这个Expires HTTP头包含页面过期的时间,这样就可以避免浏览器在页面过期时间之前发送版本检查的请求。在Apache中设置这些非常容易,只需要将以下内容加入 到.htaccess文件即可:

 	ExpiresDefault "now plus 1 year"

Apache会给所有符合*.cache.*模式的文件加上expires头,设置其失效日期为一年后,此模式将匹配所有GWT应用文件。如果你使 用的是Tomcat,也可直接通过servlet过滤器来添加头部。增加一个servlet过滤器非常简单,只需要在WEB_INF/web.xml文件 中添加此过滤器的声明,例如:

 	CacheFilter
 	com.rdews.cms.filters.CacheFilter


 	CacheFilter
 	/gwt/*

这样tomcat就知道在哪里找到此过滤器、知道哪些文件可以通过该过滤器。本例中,/gwt/*模式表示gwt文件夹下的所有文件。这个过滤器的 实现类将通过doFilter方法来添加Expires头。对GWT应用来说,我们需要在每个不符合*.nocache.*模式的文件里添加此 Expires头。nocache文件是不需要缓存的,因为其中含有版本选择的逻辑。以下是这个过滤器的实现代码:

public class CacheFilter implements Filter {
 	private FilterConfig filterConfig;

 	public void doFilter( ServletRequest request, ServletResponse response,
 			FilterChain filterChain) throws IOException, ServletException {

 		HttpServletRequest httpRequest = (HttpServletRequest)request;

 		String requestURI = httpRequest.getRequestURI();
 		if( !requestURI.contains(".nocache.") ){
 			long today = new Date().getTime();
 			HttpServletResponse httpResponse = (HttpServletResponse)response;
 			httpResponse.setDateHeader("Expires", today+31536000000L);
 		}
 		filterChain.doFilter(request, response);

 	}

 	public void init(FilterConfig filterConfig) throws ServletException {
 		this.filterConfig = filterConfig;
 	}

 	public void destroy() {
 		this.filterConfig = null;
 	}
}

2. 程序压缩

通过去掉未被调用的方法和艰涩的代码、使用简短的变量名和方法名等方式,GWT编译器在减少代码量方面表现得非常出色,但是最后得到的代码文本仍然 是未经压缩的。因此可以通过gzip压缩需要部署的应用程序的方法进一步减小代码文件的大小。gzip可以将应用程序压缩到原来的70%左右,从而提高应 用的下载速度。

幸运的是,文件压缩也可以简单地通过配置服务器来实现,唯一要做的只是在Apache服务器的.htaccess文件中加上以下语句:

?

SetOutputFilter DEFLATE

Apache首先会自动与浏览器进行沟通,根据浏览器的支持情况从而决定是否发送压缩版本,不过目前所有主流浏览器都支持gzip压缩。

如果使用的是Tomcat,那么可以直接利用server.xml文件中Connector元素,只要加上以下的属性就可以进行程序文件的压缩了:

?

compression="on"
<