InfoQ

文章

Ruby/Rails: 不一样的'Web'应用

作者 郑功梓 发布于 2008年9月2日 下午9时2分

社区
Ruby
主题
Ruby on Rails
标签
MVC

从一个有趣的项目说起

近些年来,在Web开发领域发生了许多有趣的变化,涌现出大量的框架,使得快速开发Web应用程序变成现实。其中Ruby on Rails尤其引人注目。Ruby的强大语法和表达能力配合Rails的思想,不仅创造了Web开发效率提升数倍的奇迹,也为非应用Web技术解决非传统 Web领域的问题提供了一个很好的契机。

现在我就来介绍一个刚刚结束的有趣的实际应用项目(暂且称之为W项目)。W项目是一套用于农场的自动化系统。该系统中包含了众多工业设备,如用于工人们佩 戴的移动W设备,RFID 数据收集设备(分散安装在农场各处),闸门控制设备,地磅电子称,显示屏....还有许多名目繁多的设备。这些设备有的通过电缆,有的通过 ZB(ZigBee)无线网络将数据汇集到一台工控服务器上。这台服务器负责收集和处理所有设备传回来的数据,并发送指令给相关设备,例如控制闸门。

系统中大部分设备都是可以直接采购到的产品,但还是有几个设备是需要定制开发,其中最重要的定制设备,就是W设备。W设备可以看作是一个工人佩戴在手臂上 的移动PDA。由于工作环境恶劣,W设备与普通的PDA有很大差别,它只有很小的显示屏,数个按钮,内部配有RFID识读器,并且通过ZB无线网络与服务 器通讯,外壳坚固并可防水。

由于与客户沟通顺畅,该项目的开发工作进行得非常顺利。系统的软件部分集中在服务器,服务器软件的主要任务是:

  • 从众多设备采集数据,处理数据,控制设备。
  • 提供数据浏览,统计报表等功能。

服务器运行Debian,用Ruby On Rails来完成第(2)项任务,用Ruby配上小部分C扩展来完成第(1)项任务。

W设备上的嵌入式软件很简单,基本上就是采集数据,并通过ZB网络将数据发送到服务器,显示一些简单的信息等。

系统很快投入试点使用,运行良好。

变化/挑战

由于试点应用很成功,客户很快就有了把这套系统作为产品推广的想法,因此二期工程随即上马。

二期工程的性质发生了变化,由仅供自家用的“专用系统”要发展为一个“通用的产品”。由此带来的一个显著变化是客户为W设备赋予了更多更重要的角色。现 在,W设备上要完成许多比以前复杂得很多的功能,并且要求日后能够方便地对W设备的功能进行定制(二次开发),以快速适应不同农场的需要。然而W设备是一 个资源非常有限的嵌入式系统,仅有数十KB的内存,各种外设的驱动加上ZB通讯协议棧已经消耗掉了80%的资源,按照原有架构去实现大量新功能已经不可行 了,然而维持现有硬件设备不增加硬件成本是项目的一个关键目标...又要马儿跑又要马儿不吃草?

我们来看看面临的挑战:

  • 增加很多复杂的操作界面(超出了W设备的现有资源能力)
  • 功能变化快
  • 需要日后可定制功能(二次开发)
  • 维持低成本(意味着维持现有硬件架构不变)

解决这些问题最有效的方法就是把计算转移到服务器。所幸的是W设备和服务器是通过ZB无线网络实时连接的,这就为透过网络转移计算创造了条件。

解决方案

最经典的基于服务器端的解决方案莫过于传统的“UNIX终端”模式,而且ZB无线网络窄带宽低延时特性似乎与终端模式可以很好地匹配。

但是要想直接应用“UNIX终端”模式也有一些问题:

  • 服务器端编程比较复杂(例如基于cursor/ncursor库编程),日后要进行二次开发效率低而培训成本会很高。
  • 在W设备上实现标准Terminal Emulator比较困难,有可能要为W设备增加内存而修改硬件设计。

提到终端,我不禁想起DHH在RailsConf 2007上的那个keynote,他把浏览器和IBM 3270做了有趣的对比。是的,那是个绝妙的对比,它给我留下的印象远大于对Cargo Cult调侃。浏览器和终端本质上要解决的是同一个问题。不同的是,由于web技术取得长足的发展,在服务器端开发Web程序比开发终端模式的应用程序要 方便快捷得多。Rails正是其中一个可以支持快速开发的绝佳例子。对这个项目而言,更重要的是服务器已经在跑Rails了,继续用Rails作为基础架 构将会节约很多资源。

那么客户端呢?与实现“Terminal Emulator”相比,实现“Web Browser”难度更大。但是,仔细地去考察浏览器,复杂的原因在于HTML多媒体渲染以及客户端脚本。文本Web浏览器是可以相当小巧的。W设备并不 需要华丽的界面。而且由于HTTP/HTML效率太低并不合适ZB网络的低带宽,定制传输协议和标记语言势在必行。新的自定义的标记语言相当简洁(我们称 之为MML:Micro Markup Language),在W设备中实现对应的“浏览器”变得异常轻松,最终只用了大约2k行左右的C代码。

再回到服务器端,如何使Rails适应这些定制的协议和标记语言?

从外部来看,Rails提供了以下主要服务:

  • MVC编程架构
  • 透过ActiveRecord与数据库打交道
  • 为HTML渲染提供服务
  • 其他如测试,数据迁移,插件...等等

其中份量最重的ActiveRecord部分与web其实完全无关,可以“拿来就用”。很多便利工具,例如测试,数据迁移,插件机制等等,其实与web也无多大关系。

既然Rails的MVC中,M与web无关,C部分主要留给业务逻辑,而V部分对于非web领域价值不大,倘若要基于rails再建一个领域特定的 MVC,工作量也就是集中在V部分了。而这个V部分,既然是领域相关,则无论采用什么方案都是一个不可避免的工作。由于定制的MML是一个非常简单的标记 语言,因此V部分也相当简单。当我们把Rails的思想与习惯用法再应用到这个新的MVC上时,就得到了一个基于Rails并且与Rails神似的框架。 我们可以称这种框架为“领域特定的框架”(Domain Specific Framework,DSF)。

归纳起来,解决方案就是:客户端使用定制的MML展现数据,服务器端为基于Rails的DSF,这样可以满足客户的所有要求,并且具有良好的可扩展性。

实现基于Rails的DSF

要基于Rails来实现这样一个DSF以适应非传统Web应用,有两个途径:一是对Rails的各个部分进行修改,例如修改router以适应新的协议,修改render适应MML等等;二是利用Rails的现有设施,按照Rails的思路重新构造某些部件。

第一种方法实施起来并不容易。虽然使用Rails容易,而Rails本身是比较复杂的,修改起来并不容易,另外还享受不到 未来Rails升级的好处。而第二种方法除了能够享受未来Rails升级的好处外,还可以与现有的Rails程序和平共处,并且无缝连接,因此选择后者来 实现。要实现full stack的MVC framework,除了直接利用Rails的ActiveRecord之外,还需要实 现:Parser,Router,Server,Controller和MML render。

Parser: 负责解析自定义的通讯协议,获取从W设备传来的请求,并解析参数等。

Router: 扫描并装载Controllers,缓存Controller对象,根据Parser的结果取得相应Controller的对象。Router还负责监控 Controller是否已被修改和重新装载Controller,这样就可以和Rails一样,在开发的时候可以立即看到修改的结果而不用重启服务器。

Server: 总控制,从串口中读取ZB网络数据,调用Parser解析,把结果传给Router,从Router中获取Controler对象并根据Parser结果调用Controller#Action,以及缓存Controller的输出等等。

Controller: 用户应用的业务逻辑都放在Controller的子类里实现。在Controller里直接调用rails的Models访问数据库。

MML Render: 使用erb作为模板文件,再加上一些辅助的功能,例如render link,menu之类的,和自定义的MML特性密切相关。MML Render作为module最终mixin到Controller里面。

从上面各个模块的功能描述可以看出,这个DSF模型几乎和rails是一样的,但由于是面向自定制的协议和MML,其复杂度和实现难度大大低于Rails,再加上Ruby语言的强大表达能力,最终这个DSF仅用了不到千行代码!

再来看看在这个DSF下开发应用程序会是怎样的,以一个最简单的"Hello World"为例:

contollers/main.rb:
Ruby代码:
class MainController < WSController
controller_map_to :m
action_maps :index => "i"

def index
@text = "Hello world"
end
end

views/main/main_index.erb:
Ruby代码:
<%= "<p3.20crs2e3.5> #{@text}" %>

在MainController中的controller_map_to起的作用是把自己(main)映射到一个较短的名字"m",而 action_maps则对actions取短的别名(本例中,index的别名是i),这样做的目的是为了减少请求串的长度。(ZB网络的带宽非常有 限,节约每一个字节意义都很大)。

再看看view,其中“<P3.20CrS2E3.5>”表示在第3行20列处(P3.20),以红色(Cr)2号字(S2)显示 @text,经过3.5妙后清除屏幕(E3.5)。当然,如果想更方便地描述这些MML属性,则可以定义一系列helper函数,或者干脆定义一套DSL。

这个简单的例子没有演示Models,因为它可以直接使用Rails的Models,因此使用起来和在Rails中没有丝毫差别。例如可以使用has_many,belongs_to等等。

通过这个例子,我们可以体会到这个DSF与Rails有多么“神似”,熟悉Rails的人通过几分钟简单的了解就可以立即在这个新的DSF下开发应用。二次开发的问题迎刃而解。

结论

当我们把MVC Web框架的概念推广到web之外,那么这个V就可以是任意的领域特定的数据展示格式。它既可以是基于文本的,也可以是基于二进制的;既可以是自定义的, 也可以去兼容已有的格式。通过定制的DSF来解决问题不是没有意义的“重复造轮子”,而是解决领域特定问题的一个有效手段。而基于Rails来实现DSF 更是有着非常显著的优势,整个复杂的Model(ActiveRecord)可以不经任何修改而直接利用,所需要的代价仅仅是在DSF里面增加一行代码:

 require File.dirname(__FILE__) + '/../config/boot'

Rails中的众多优秀工具例如数据迁移,测试,调试,插件等等,大部分也可以直接使用,可以说基于Rails来实现DSF只需要付出5%的努力,就可以达到100%的效果。

当遇到需求变化时,运用恰当的技术手段有时候可以柳暗花明,特别是跨领域交叉应用,往往能收到意想不到的效果。web技术的蓬勃发展带来了异彩纷呈的诸多 框架技术,开发工具,以及丰富的人才储备,这些资源对于非Web领域也有巨大的吸引力。本文所举的这个例子就是巧妙地通过基于Rails的DSF来解决实 际问题。实际上这个例子还有一个精彩的插曲值得一提,那就是客户希望可以脱离W设备和ZB网络来开发应用程序,简单地说,就是希望有一个W设备的硬件模拟 器。在传统解决方案里面,硬件模拟器是一项非常复杂的工作,但在这里,由于整个解决方案采用的是Web技术,因此实际上模拟器的核心就是一个简单的MML 到HTML的转换程序加上少许JavaScript而已,浏览器就摇身变成了一个硬件模拟器。突破传统思维的束缚,就容易找到金矿。

我不是一个Web程序员,也从未开发过用户超过十个人的传统Web程序,但这并不意味着Web技术对我无用。正相反,Web技术经常被应用到我所从事的嵌入式系统领域。基于Rails的DSF解决方案为加速Web技术在其它领域的应用开启了一道光明之门。

作者简介:郑功梓,资深潜入式系统工程师,技术涉猎广泛,前新大陆自动识别技术有限公司总工程师,现旅居新西兰。


志愿参与InfoQ中文站内容建设,请邮件至editors@cn.infoq.com。也欢迎大家到InfoQ中文站用户讨论组参与我们的线上讨论。

非常不错,受启发了! 发表人 Yin AnPing 发表于 2008年9月3日 上午12时33分
Re: 非常不错,受启发了! 发表人 fjun sun 发表于 2008年9月9日 上午5时0分
Re: 非常不错,受启发了! 发表人 凉粉 小刀 发表于 2008年9月12日 上午9时0分
很creative呀 发表人 cao yunfei 发表于 2008年9月3日 上午1时33分
一个很好的软件重用的例子 发表人 Ming Li 发表于 2008年9月3日 上午8时35分
受用了 发表人 kai liu 发表于 2008年9月3日 下午9时8分
不错 发表人 晓昱 黄 发表于 2008年9月8日 上午3时10分
Re: 不错 发表人 功梓 郑 发表于 2008年9月9日 上午8时2分
  1. 返回顶部

    非常不错,受启发了!

    2008年9月3日 上午12时33分 发表人 Yin AnPing

    如题!

  2. 返回顶部

    很creative呀

    2008年9月3日 上午1时33分 发表人 cao yunfei

    作者的例子说明ror的架构是多么优秀,可以方便的实现基于Rails的DSF。 Java的世界中是否有框架有类似能力,可以实现基于其上的DSF?

  3. 返回顶部

    一个很好的软件重用的例子

    2008年9月3日 上午8时35分 发表人 Ming Li

    在ROR基础上有效地利用并扩展了MVC模式。不过是用基于Java/J2EE的框架要花费多得多的开发工作量,但设计思想是相同的。看好Ruby、Python等脚本语言的发展,在商业应用程序领域,便捷的开发和灵活的适应性是最重要的特性。

  4. 返回顶部

    受用了

    2008年9月3日 下午9时8分 发表人 kai liu

    我的理解是:作者只是用了rails框架的设计以及ActiveRecord.让人开阔了思路

  5. 返回顶部

    不错

    2008年9月8日 上午3时10分 发表人 晓昱 黄

    利用既有web模式转移计算工作,为瘦客户端放弃HTML改用自定义MML,修改V部分输出MML——MML Render、改写了Parser实现了自定义的通讯协议——看来也放弃了HTTP、修改Server以便从特有接口读取数据。 RoR不是很熟悉内部结构,Router、Controller修改了什么? 没用RoR干过大事儿,编码量也许不大,设计还是要深思熟虑的。

  6. 返回顶部

    Re: 非常不错,受启发了!

    2008年9月9日 上午5时0分 发表人 fjun sun

    http://rubynroll.javaeye.com/blog/219826 这不是和这个一样么

  7. 返回顶部

    Re: 不错

    2008年9月9日 上午8时2分 发表人 功梓 郑

    这里对Rails并不进行修改,实际上这个DSF是寄生于Rails并与Rails应用程序一起工作。你可以把DSF看作是另一个Rails实例,与其它Rails实例共享Model而Router/Controller是完全独立的。唯一交叉的是模拟器,它是通过把DSF的Server嵌入到Rails的一个Controller里面来实现的。 由于Ruby的表达能力非常强,所以编码量很少,关键在于设计。

  8. 返回顶部

    Re: 非常不错,受启发了!

    2008年9月12日 上午9时0分 发表人 凉粉 小刀

    在这篇文章的新闻稿里面,已经标明原出处了。这是作者对博客整理后的文章

深度内容

和Google互补的搜索引擎Wolfram|Alpha

Wolfram|Alpha与Google究竟是什么关系,Wolfram|Alpha自己是如何定位的?Wolfram|Alaph在多大程度上是语义网搜索呢?InfoQ中文站就等等这些问题采访了Wolfram研究公司中国区商务经理王翔。

SOA契约成熟度模型

本文说明了所推荐的契约版本管理设计策略是如何与SOA成熟度模型发生联系的。文章目的是为实现版本管理和可组合性提供一个路线图。

数据服务简介

Vijay Narayanan在这篇文章中对数据服务的几个方面进行了介绍,它们都是SOA实践者和数据架构师感兴趣的内容。本文对数据服务的几个方面进行了介绍,包括需求定义,基本原理和好处、范围、开发以及消费模式。

分块云计算

在本文中,Jimmy Nilsson描述了一种他在过去数年间观察到的一种正在缓慢成长的架构风格,他把这种风格称为“分块云计算”。

豆瓣网技术架构变迁

罗马不是一天建成的,豆瓣的技术架构也是随着用户规模的增长一直在持续变化中。在本次演讲中,豆瓣的首席架构师洪强宁将与大家一起分享从上线时的单台服务器架构开始一直到现在的豆瓣架构变迁历程。

融合思想:深入探索S#arp架构

Billy McCafferty展示了S#arp架构,它在ASP.NET MVC框架的基础上,荟萃了当今的最佳实践,应用在ASP.NET Web应用程序的架构设计中。

王雷谈开源以及新兴市场计划

中国作为新兴市场中的新兴市场,是Sun在美国之外实施SSE(SUN Startup Essentials)项目重点关注的地区。在QCon Beijing 2009期间,InfoQ中文站有幸对此项目的负责人王雷先生进行了采访,探讨了关于开源、新兴市场、SSE等话题。

使用HTML5构建下一代的Web Form

HTML5 是由 WHATWG发起的,最开始的名称叫做Web Application 1.0,而后这个标准吸纳了Web Forms 2.0的标准,并一同被W3C组织所采用,合并成为下一代的HTML5标准。