InfoQ

InfoQ

文章

我的书签

登录注册 以永久保存书签。

该内容已经被标记书签!

标记书签错误,请重试!

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

作者 郑功梓 发布于 2008年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中文站用户讨论组参与我们的线上讨论。

非常不错,受启发了! 发表人 AnPing Yin 发表于
Re: 非常不错,受启发了! 发表人 sun fjun 发表于
Re: 非常不错,受启发了! 发表人 小刀 凉粉 发表于
很creative呀 发表人 曹 云飞 发表于
一个很好的软件重用的例子 发表人 Li Ming 发表于
受用了 发表人 liu kai 发表于
不错 发表人 黄 晓昱 发表于
Re: 不错 发表人 功梓 郑 发表于
  1. 返回顶部

    非常不错,受启发了!

    发表人 AnPing Yin

    如题!

  2. 返回顶部

    很creative呀

    发表人 曹 云飞

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

  3. 返回顶部

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

    发表人 Li Ming

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

  4. 返回顶部

    受用了

    发表人 liu kai

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

  5. 返回顶部

    不错

    发表人 黄 晓昱

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

  6. 返回顶部

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

    发表人 sun fjun

    rubynroll.javaeye.com/blog/219826

    这不是和这个一样么

  7. 返回顶部

    Re: 不错

    发表人 功梓 郑

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

  8. 返回顶部

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

    发表人 小刀 凉粉

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

深度内容

大规模视频网站的计费与流量管理

本次分享将会就大规模视频网站的计费与流量管理这个话题,从操作层面细细进行讲解和分析,为系统工程师们揭示平日里我们没有关心的另一些内容。同时也希望本次分享能揭示行业中的一些“潜规则”,让互联网行业的流量与带宽管理更为开放与简洁。
本次演讲视频录制于QCon杭州2011

专访Jeffrey Richter:Windows 8是微软的重中之重

Jeffrey Richter以其多本Windows核心技术的经典著作而闻名,同时,他深入掌握微软的.NET等一系列核心技术,2012年1月,Jeffrey Richter在北京接受了InfoQ中文站的专访,谈到Windows 8和WinRT编程,并就异步编程、Windows编程中的可扩展性、性能和安全性方面给出自己的建议。

应用云平台的可用性——从新浪SAE看云平台设计

云计算平台的可用性,相比传统互联网服务而言,更加复杂和困难,也更具有挑战性。本文借助新浪SAE云平台为读者讲述了云平台可用性的定义、如何打造高可用的平台,以及对云计算的用户提出了建议。

JVM定制改进 @ 淘宝

淘宝高度重视Java平台的健康发展,组建了一个团队专注于Java平台的底层部分的性能、功能与稳定性改进;工作主要基于OpenJDK中的HotSpot VM开展,其中一些通用的功能随后也会逐渐反馈给OpenJDK社区。希望能与使用Java平台开发应用的大家交流经验。
本次演讲视频录制于QCon杭州2011

"伤得起"的云计算应用——对云端应用之架构的思考

2011年4月21日至22日是值得云计算从业者纪念的日子。Amazon的IaaS服务出现故障,导致许多商业网站的服务中断,影响非常严重。作为云计算用户,我们需要思考的是,如何保证即便在云服务不可用的情况,我们的应用架构仍然能够屹立不倒?本文正是站在云计算用户的角度试图探讨这一问题。

让交付的速度跟上思考的速度

12人的技术团队,4组刀片服务器,每月20亿的访问量,每日1次准时部署,99.9%的可用性。这可能吗?当然。想知道如何做的吗?百姓网将与您分享他们在DevOps实践过程中的经验和技巧。
本次演讲视频录制于QCon杭州2011

架构之路——穿行在产品和业务之间

篱笆作为一家起源于社区的电子商务公司,反映到技术层面就是同时要面对产品和业务,以及经营战略的变化调整。如何在产品和业务的夹缝之间完成技术架构的抽象与平衡,寻找更有效的价值定位,这当中有些经验教训和个人感悟愿与众人分享。
本次演讲视频录制于QCon杭州2011

特性注入:成功三部曲

本文将对特性注入以及相关方法做一个扫盲性的介绍。我们会解释这个框架的关键要素,并附上实例来证实它们。为了让文章保持相对较短,我们不会深入到某个工具或方法中,而是会给出一些参考资料,以便大家做进一步的研究。