BT

如何利用碎片时间提升技术认知与能力? 点击获取答案

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

| 作者 郑功梓 关注 0 他的粉丝 发布于 2008年9月3日. 估计阅读时间: 15 分钟 | Google、Facebook、Pinterest、阿里、腾讯 等顶尖技术团队的上百个可供参考的架构实例!

从一个有趣的项目说起

近些年来,在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中文站用户讨论组参与我们的线上讨论。

评价本文

专业度
风格

您好,朋友!

您需要 注册一个InfoQ账号 或者 才能进行评论。在您完成注册后还需要进行一些设置。

获得来自InfoQ的更多体验。

告诉我们您的想法

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

非常不错,受启发了! by AnPing Yin

如题!

很creative呀 by 曹 云飞

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

一个很好的软件重用的例子 by Li Ming

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

受用了 by liu kai

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

不错 by 黄 晓昱

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

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

rubynroll.javaeye.com/blog/219826

这不是和这个一样么

Re: 不错 by 功梓 郑

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

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

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

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

8 讨论

登陆InfoQ,与你最关心的话题互动。


找回密码....

Follow

关注你最喜爱的话题和作者

快速浏览网站内你所感兴趣话题的精选内容。

Like

内容自由定制

选择想要阅读的主题和喜爱的作者定制自己的新闻源。

Notifications

获取更新

设置通知机制以获取内容更新对您而言是否重要

BT