REST+RIA方案
一直很想体会一下使用REST+RIA这种组合开发Web应用效果究竟如何,乘着这几天有空简单试验了一把。
我选择的案例很简单,就是《应用Rails进行敏捷开发》一书中的depot应用。为了简化问题,我只使用了其中的store控制器部分,并将这一部分改写成REST+RIA的方式。具体地说,就是:
1、重写一个控制器(我把它称为RestStore),它的功能和Store控制器类似,但只提供RESTful风格的接口;
2、表示层部分用Flex重写。该层与RestStore控制器通信以获取必要的后台数据;
为了比较的目的,以下将《应用Rails进行敏捷开发》所使用的“经典”方法(Rails作为后台,rhtml作为表现层)称为方案一,将我所试验的方法(RESTful Rails作为后台,Flex作表现层)称为方案二。除了结构上的差异,这两个方案所实现的功能和界面基本上是完全相同的。
代码量比较
方案一:
Controllers(1个文件,57行)
Helpers(1个文件,9行)
Models(5个文件,85行)
Views(6个文件,93行)
CSS(1个文件,227行)
------------------------------------------
共计14个文件,471行
方案二:
Controllers(1个文件,37行)
Models(3个文件,39行)
Views(3个文件,15行)
MXMLs(3个文件,278行)
ActionScripts(4个文件,142行)
CSS(1个文件,76行)
--------------------------------------------
共计15个文件,587行
方案二比方案一要大概多出25%的代码,这主要是因为前后台采用了不同的技术,所以必须写一些用于转换数据格式的方法。
需要说明的是,由于没有详细统计注释和空行,因此这个数字并不十分精确,只能作为一个数量级上的参考。但是代码量的分布上还是能看出一些问题来的。方案二比起方案一而言,服务器部分得到了很大的简化,原因在于(1)视图部分只提供XML数据,不再负责处理界面;(2)购物车移到客户端来实现,后台不再需要Cart和相关的类。
此外,方案一的CSS文件相当长,而方案二的CSS就要小得多。这是因为方案一的CSS也包括许多用于页面布局的部分,而在Flex中,布局是用布局容器来实现的,CSS只负责组件样式。这就使得Flex中MXML比较大而CSS可以稍小一些。(我个人的看法是使用布局容器进行整体布局比用CSS更加合理)
通信量比较
在客户端使用如下操作步骤,这些操作覆盖了控制器所实现的所有功能。使用HttpLook软件记录并比较两个方案与服务器通信的次数和通信数据量大小。
1、浏览首页
2、添加货品1
3、清空购物车
4、添加货品2
5、添加货品3
6、结账
7、不输入任何数据就提交
8、输入正确的数据并提交
以下为比较结果。括号内为服务器返回的HTTP body大小,如果有两个数字,则分别表示请求数据和应答数据的大小。
比较结果不包含货品的图片,因为这部分数据量对所有方案都是一样的。
方案一 | 方案2 | |
1 | GET /store(9834) GET /stylesheets/depot.css(3263) GET /javascripts/prototype.js(71260) GET /javascripts/effects.js(38200) GET /javascripts/dragdrop.js(30550) GET /javascripts/controls.js(28911) GET /javascripts/application.js(148) GET /images/logo.png(1070) GET /images/favicon.ico(0) |
GET /depotclient.html(4308) GET /AC_OETags.js(7826) GET /history.js(1292) GET /history.html(1272) GET /depotclient.swf(298104) GET /history.swf(2656) GET //favicon.ico(0) GET /rest_store/list_pay_types.xml(157) GET /rest_store/list.xml(5281) |
2 | POST /store/add_to_cart/11(1851) | |
3 | POST /store/empty_cart(93) GET /store(9834) GET /stylesheets/depot.css(0) |
|
4 | POST /store/add_to_cart/5(1881) | |
5 | POST /store/add_to_cart(1(2065) | |
6 | POST /store/checkout(2718) GET /images/logo.png(0) |
|
7 | POST /store/save_order(94,3217) | |
8 | POST /store/save_order(129,93) GET /store(9891) |
POST /rest_store/save_order(300,21) |
可以看到两种方案在通信方式上的巨大差异。类似的地方是,两种方案初次浏览时都需要加载相当多的附件,不同之处在于方案二要加载的东西中有一个相当大的swf文件,其他内容则都比较小。而方案一则也需要加载几个比较大的JS文件,这些JS文件大多是Ajax所需要的。
在后续过程中,方案二的优点就体现出来了:由于购物车是在客户端处理的,结账的表单也嵌入在SWF中,因此方案二可以在绝大部分时间不必与服务器通信,只在最后提交表单的时候需要再次请求服务器。方案一虽然每次请求的数据量都不大,但是请求次数相当频繁,对于服务器来说负担仍然相当沉重,而且请求次数越多,由于网络问题等原因出错的可能性也越大。
仔细观察表中的数据也能看出一些有意思的结论。请看方案一中的步骤2和步骤3:步骤3清空购物车使用的传统的Web方法,即向服务器发出请求,然后重新载入整个页面;步骤2添加一件货品,使用的是Ajax方法。两个步骤其实数据量差不多大小,步骤2还要多出一个product_id参数,但是无论从请求数量(1:3)还是数据量大小(1851:9927)都表明Ajax比传统的全页面刷新方法要优越得多。不过在这个方面,RIA走得更远:完全不需要向服务器请求,客户端本身就可以完成相当一部分工作。
结论
REST+RIA是一种新兴的Web应用结构,这种结构具有如下的优点:
1、将表现层与后台彻底分离
传统的Web表现层技术总是依赖于特定的服务器编程语言的。比如你用JSP编写页面,就意味着服务器后台必须使用Java,如果后来决定改用ROR,那么除了用rhtml重写表现层以外大概没有什么更好的办法。而使用RESTful风格的接口意味着服务器只需要提供资源,不论后台使用Java,Ruby或.Net来实现,对表现层都完全没有影响,哪怕把后台整个换掉也不需要重写表现层,这也意味着 表现层是完全可重用的。考虑到真实的项目中对表现层的修改往往是最麻烦且工作量极大的部分,这项分离意义重大。
要将表现层与后台分离,其实也有其他的一些技术方案可选。以前出现过的两种比较常见的办法:(1)服务器提供XML数据,然后用XSLT转换为HTML或其他格式;(2)服务器提供Web Service接口,用支持WS的客户端访问。不过这两种方法可能也存在一定的问题,最后并没有真正流行开来。Web Service更多的用在异构系统整合而不是系统内部通信上。
2、方便程序员和美工协同开发
在页面中嵌入代码是好是坏?问题不在于代码本身,而在于这样一来美工和程序员的工作难以协调,任何一方做出修改,都必须复核自己的修改是否破坏了对方的工作。RESTful风格的接口只提供资源数据,不暴露服务器上的任何实现细节,这对美工和程序员来说绝对是个好消息:只需要约定服务器提供数据的格式,美工就可以完全按照自己的节奏去设计和测试页面,程序员也可以专心实现后台逻辑,彼此都不需要担心破坏对方的工作,可以实现完全的并行开发,这是以往任何Web开发技术都没能做到的。
3、有利于采用快速原型的开发方式
在上面已经提到过,在这种方式下,美工和程序员可以完全并行工作。即使在还没有实现任何后台逻辑之前,只要先写一个符合格式的Xml占位文件,表现层就可以开始设计,不论后台开发速度快慢,表现层都可以尽快提供一个可以工作的原型,对于尽早审核需求和获得用户反馈都是非常有利的。
4、合理分配负载,减轻服务器压力
大多数Web应用在负载的分配问题上其实是非常不合理的:服务器承担着成千上万的用户请求,每时每刻都在忙碌之中,而客户端机器在这个时间里只是傻傻的等待响应,根本无事可做。从前面的测试结果可以看到,RIA的方案需要一次性下载较大的数据,但在这之后客户端可以承担相当一部分工作,避免频繁请求服务器,不仅在资源分配上更加合理,也能够让服务器同时承载更多的用户。
5、提高用户体验
减少服务器请求不仅是资源问题,也关系到用户体验,频繁的页面闪烁是相当糟糕的。Ajax可以在相当程度上缓解这一问题,
但是Ajax并不太适用于大范围的页面变化和页面跳转,一般的Web应用中使用页面跳转的比例仍然远高于使用Ajax的比例。当网络情况不佳或服务器繁忙时,等待服务器响应也是很恼人的事情。
一个RIA(如Flex)应用中通常包括多个页面,在页面之间切换不会有停顿,甚至不太大的应用可以One page one application,更不必担心愉快的冲浪过程中突然抛给你一个404或500错误(这对心脏相当有好处)。
(PS:不过世界上的事情有时候也难说。我的确知道有这么一些人,他们觉得看浏览器页面闪动的白屏比看Flash的平滑窗口渐变更舒服。除了习惯成自然外,我实在很难想出有什么别的理由可以解释这个问题)
但是这种开发方式也有一些问题:
1、至少在目前,Flex面临着和浏览器一样的限制——只支持GET/POST调用,还不能完整支持REST所要求的全部动词。虽然这个问题有一些临时的解决办法,不过需要在后台和表现层上都必须写一些hack代码,并不是很优雅。上面的例子也仅使用了GET/POST调用,还不能算是很严谨的REST方案。我们期待下一个版本能解决此一问题;
2、这种方式的开发代码量要略大于纯ROR的方法。Flex中使用的Actionscript语法比较类似于Java,仍然需要写很多大括号,对于循环也只能用笨拙的for ...。动态语言在生产力方面的优越性还是相当明显的。当然,比起Java或ASP.Net来说,Flex的代码量或许已经算是很少了。
3、关于如何组织Flex程序,目前还没有一致的意见,因此在Flex程序中还没法体现惯例优于配置的原则,仍然要编写一些自定义代码;
4、Flex编译后的程序通常比较大(普通规模的一般有200~300K,嵌入资源的话更多),在低速网络上首次下载会比较慢。据说Flex 3已经在着手解决此问题。
感想:
RIA(这里的RIA特指Adobe Flex和M$ WPF/Silverlight等,不包括Ajax)现在还是一种褒贬不一的技术。赞赏者认为,RIA结合了B/S结构和C/S结构的优点,安装部署的代价接近于前者而表现力上接近于后者;批评者则认为RIA在两方面都算不上最好:部署上不如B/S(需要安装浏览器插件)而表现力不如C/S(因为安全原因不能访问全部本地资源)。其实这两种说法都有道理。世上没有一种技术能具有所有的优点而没有缺点,也没有一种技术能包打天下。DHH说RIA没用,现代的Web技术足够发达了,我觉得此说不确。目前的Web应用或许有80%用HTML加上一些Ajax就够了,但余下的那20%呢?那些是用纯Web方案很难甚至没办法解决的领域,虽然少,但是硬骨头也要有人来啃。
因为REST和RIA目前都不能算比较成熟的技术,即使在这个很小的例子里也暴露出一些问题,大规模采用这种解决方案还是不太可行的。不过这些问题大多已经有人开始想办法去克服,相信未来会逐步得到解决。
事实上在这个简单的例子里我已经感觉到这种方案“分离关注点”带来的好处。在ROR的方案里,我需要同时考虑:在Controller里面提供什么数据,在View中怎么展示这些数据,思路总是在Model,Controller和View中来回切换。但是在REST+RIA的方案下,我可以在实现Controller的时候完全不考虑表示层,在实现界面的时候也完全不用考虑后台,思路非常集中,也容易专注于去思考一些设计上的问题。这是一次很好的开发体验,也让我有足够的信心说:尽管还存在一些问题,但是REST+RIA的方案必将有着光明的前途。
java lover