案例研究:利用Grails搭建Feedlr.com网站

//
案例研究:利用Grails搭建Feedlr.com网站
http://www.infoq.com/cn/articles/case-study-grails-parti
Feedlr和Grails

Feedlr:feed驱动的多平台微博客机器人平台

微博客是由Twitter 创造出的一种web 2.0时代的新事物。在微博客上,人们使用简短的语言随时随地的发表消息,并可以即时地受到好友的消息。由于易用,实时等特点,Twitter在06年推 出至今逐步升温,已经拥有超过300万用户。特别在08年中,Twitter一改起步阶段geek玩具的角色,明显地向主流进化。随着Twitter的兴 起,也出现了非常多其他的微博客。仅国内就有叽歪、饭否、以及做啥等等。微博客的兴起提供了一种全新的在线沟通方式。

Twitter作为微博客的 鼻祖和最成功的例子,其优秀的API接口功不可没。通过Twitter API,开发者们开发出了众多新奇又好用的Twitter第三方应用。我开发Feedlr的出发点是建立一个让用户可以自行定制feed机器人的服务,核 心功能类似Twitter上颇受欢迎的twitterfeed,并且可以同时Twitter,叽歪,饭否以及做啥共4种微博客平台。

通过 Feedlr,用户可以建立微博客广播帐号,来随时追踪自己感兴趣的RSS/Atom Feed内容。一旦有更新,Feedlr就会自动把新的内容发送到指定的微博客平台上。Feedlr上线至今,用户们建立了自定义的新闻播报机器 人,DIY的免费天气预报机器人,不同微博客之间的消息同步机器人,甚至国内地震情况实时监控机器人等等。而通过国内微博客服务的短信通知服务,以上所有 的Feed内容国内用户都可以免费在手机上通过短信接收到。

Grails框架的选择

相关厂商内容

关于红包、SSD云盘等核心技术集锦! 《不一样的技术创新》-阿里巴巴双11背后的技术 Cloudant DBaaS技术概述 下一代 DB2更加突出 BLU Acceleration
Grails是一个崭露头角的基于 Groovy语言,运行与JVM之上,设计上类似于Rails的快速web开发框架,在08年初刚推出1.0版。通过Groovy语言和创新的架 构,Grails把成熟的企业级JEE开源组件Spring,Hibernate等巧妙地整合起来,使用类似Rails的“按约定设计”(design by convention)理念捆绑成一套完整的web开发框架。JEE开发过程的繁琐被Groovy灵活多变的动态特性和按约定设计带来的精简配置所取代, 而又保留了企业级组件在稳定和性能方面的优势,可以说是把Rails式的快速开发带给了水深火热中的JEE开发者们。我来自JEE背景,对Groovy语 言也有一定基础,选用Grails搭建Feedlr是比较自然的选择,同时也是为了在一个没有过多约束的真实项目中体验Grails的完整开发过程。

如何用Grails实现Feedlr的核心功能

Feedlr的核心功能

Feedlr的核心功能主要包括定时查询用户提供的feed的更新,把更新的feed内容发布到微博客,再加上用来增强用户体验的多处AJAX实现以及OpenID登录等。这里逐一对这些功能的实现做一下介绍。

定时查询feed更新

Feedlr 最核心的功能就是定时轮询用户提交的feed,发现新增的条目,从而通过微博客API发送到微博上去。只要使用Grails的Quartz插件就可以非常 方便的实现这一功能。Quartz是一个用途广泛的开源Java库,用于精确地控制定时任务。由于兼容Unix Cron语法,Quartz的功能非常强大。而在Grails中,Quartz是框架自带的核心插件之一,通过Quartz插件来执行定时任务非常方便。 新建一个Quartz定时任务,只需要在Grails项目根目录下执行

grails create-job
根 据提示输入job名称,Grails就会自动在grails-app/jobs/目录下生成一个新的job程序文件。Grails job都是以XXXJob.groovy命名,存放在grails-app/jobs目录下,Grails启动时会自动遍历jobs目录,定时执行每个定 义好的job。一个job文件用来定义一种定时执行模式,通过Unix Cron语法来定义定时逻辑。例如,Feedlr用于轮询feed的job大致是这样的:

class UpdateFeedsJob {
def feedService
def cronExpression = "0 * * * * ?" //每分钟执行一次
def execute() {
feedService.updateFeeds()
}
}
Cron 表达式“0 * * * * ?”表示每分钟执行一次。需要执行的逻辑通过定义一个execute()方法来指定。其中feedService是已经定义好的用来查询feed更新的一 个Grails Service类,使用Rome来解析feed。注意此处不需要实例化feedService变量,只要通过按约定设计的规则定义需要使用的 Service的变量名,Grails会自动找到FeedService这个Service类,注入到UpdateFeedsJob中,并把 Service实例付给feedService变量,听起来很神奇吧。这样,Grails就会每分钟触发一次UpdateFeedsJob,来查询 feed更新了。

发布feed更新到微博客

目前流行的微博客API都是已REST风格设计,通过GET和POST方法来得到或者更新内容的。例如发布一条消息到Twitter,就是通过POST方法发送到Twitter指定的API地址,简化的代码实例如下:

def conn = new URL('http://twitter.com/statuses/update.xml').openConnection()
conn.setRequestProperty ('Authorization', 'Basic ' + 'username:password'.bytes.encodeBase64())
conn.requestMethod = 'POST'
conn.doOutput = true
try{
conn.outputStream.withWriter('UTF8'){
it << "status=" << newMessage
}
}catch(Exception e){
...
}
以上Groovy代码很清晰易读。通过Twitter RESTful API发布新消息需要使用Http Basic验证用户登录信息,所以这里按照Basic验证规范在请求中加入了验证数据。其中encodeBase64()方法是Grails提供的神奇的 动态方法,对于合适类型的对象在Grails程序中直接就可以使用这些动态方法,其他的编码方法还包括encodeAsURL()等。

Ajax

在web 2.0时代没有Ajax的网站是不完整的。幸运的是,在Grails中使用Ajax非常方便。通过Grails内建的多才多艺的render方法,就可以轻松地给前端Ajax请求返回任何形式的输出。例如,

直接返回简单的纯文本字串
class FooController{
...
def ajaxResponse = {
...
render("This is an Ajax response.")
}
指定返回内容的格式和编码
render(text:"<xml>some xml</xml>",contentType:"text/xml",encoding:"UTF-8")
返回模板内容
render(template:"feeds", model:[feeds:feeds], contentType:"text/html", encoding:"UTF-8")
返回JSON,直接自动转换一个object为JSON
import grails.converters.*
...
def jsonObj = [object:[collection:[[name:‘value1′],[name:‘value2′]]]]
render jsonObj as JSON
返回JSON,通过JSON builder DSL直接构造JSON数据
render(contentType:‘text/json’, , encoding:'UTF-8'){
studio(name:‘Pixar’,website:‘pixar.com’)
films{
film(title:‘Toy Story’,year:‘1995′)
film(title:‘Monsters, Inc.’,year:‘2001′)
film(title:‘Finding Nemo’,year:‘2003′)
}
}
OpenID支持

Feedlr支持使用OpenID登录。由于Grails社区已经提供了OpenID插件,通过Grails的插件机制,实现OpenID支持也是一件轻松的事情。

首先,安装OpenID插件,在Grails应用根目录执行命令:
grails install-plugin openid
然后,使用openid插件提供的taglib来编写openid登录表单
<openid:form success="[controller:'login',action:'openidSuccess']" error="[controller:'login',action:'openidError']">
<openid:input size="30" value="http://" class="input-text"/>
...
</openid:form>
OpenID 插件代为处理了具体的OpenID登录验证过程,在<openid:form>中,通过success参数和error参数指定登录成功或失 败以后重定向到哪个controller action。登录成功后,就可以在controller中直接得到当前登录的openid信息。

def openid = session.openidIdentifier
当然,需要实现完整的OpenID和普通帐号的整合还有更多工作要做,包括把登录的OpenID和已有的普通帐号关联起来,从普通帐号添加OpenID信息等。这些都是需要开发者根据自己的情况自行实现的。

测试

测 试是开发一个完整项目不可缺少的部分,幸运的是Grails已经为开发者考虑到了这点,测试Grails程序也能像开发一样轻松。Grails中的测试建 立在Groovy testing的基础上,通过使用Groovy来编写JUnit测试代码,减轻程序员的负担。Grails中的测试分为unit test和integration test两种,两者的区别主要在于unit test是相对独立的测试,而integration test执行的时候Grails会按照实际运行的方式启动框架程序。建立一个unit test或者integration test各自只需要一条命令即可。

grails create-unit-test
grails create-integration-test
Grails 会自动在grails-app/test/unit或者grails-app/test/integration下面建立相应的 XXXTests.groovy文件。具体的test case定义在XXXTests.groovy里,通过定义继承groovy.util.GroovyTestCase的类来实现,这些其实都是 Groovy测试的内容,通过JUnit方式编写测试代码即可。

准备好了test case之后,Grails同样已经为你准备好的命令来自动执行测试。

grails test-app
执行这条命令,Grails就会自动按照unit test到integration test的顺序来执行定义好的所有test case,并将测试结果整理成HTML格式展现出来。test-app命令还有更多具体用法,可以参考Grails文档。

Feedlr的部署

使用Grails的开发过程是很令人感到愉快的。那么,一切都搞定以后,怎么部署呢?

Grails 文档中说明Grails经测试可以部署到大多数常用的Java应用服务器上,但是具体有关部署的资料文档比较缺乏。Feedlr选择的是Tomcat 6,相对来讲比较常用,资料也比较丰富。准备部署Grails应用的时候,首先通过Grails把项目打包成war文件。

grails prod war feedlr.war
这 里,"prod"参数用来指定打包的时候使用Config.groovy里针对生产环境的配置。部署环境的配置在Config.groovy中设定,包 括"prodcution",“development"和"testing"三种,主要用于对不同环境指定不同的数据源和特定的环境变量。具体用法可以 参考Grails文档。war命令默认的打包环境就是"prod",所以次数"prod"也可以省略。如需指定其他环境只需要将"prod"替换 成"dev"或者"test"即可。

最后指定war文件的文件名是可选的。在Tomcat下如果想让应用跑在URL根路径下,可以指定文件名为ROOT.war。打包完成后,把war文件复制到Tomcat应用目录下,启动Tomcat,正常的话Grails应用就能跑起来了。

在 系统性能和伸缩性方面,我其实没有花太多力气去优化。最主要的优化工作就是在内存占用方面。Feedlr目前使用的是一台540MB内存的VPS服务器, 在初期曾使用340MB内存的配置,遇到了内存资源紧张的问题,导致JVM性能底下。由于Feedlr的特定用途,需要解析大量feed内容,所以在内存 方面要求不低。后来经过一系列的优化措施,目前在这个环境下运行的相对比较稳定了。我之前也总结过一些Grails服务器优化的经验,有兴趣的朋友可以参考我的这篇博客文章。

实际开发中的一些困难

在Feedlr的开发中,可以体会到目前阶段使用Grails进行完整Web项目开发的一些困难和问题。

开发环境的不完善

Feedlr 是在Eclipse加上Groovy插件的环境下开发的。但是这个环境目前还非常不完善,主要是Groovy插件的可用程度还比较低,而且没有 Grails支持,不支持GSP语法等,只是能够支持Groovy语言,加上Eclipse本身的强大功能,能够给开发提供一定帮助。不过好在使用 Groovy语言开发比Java要省力不少,所以不需要非常强大的IDE也可以不错的完成任务。IDE方面另外的选择主要包括IntelliJ IDEA和NetBeans。简单来讲收费的IDEA对Groovy/Grails的支持最为完整强大,但是代价也不菲。开源方面的选择,Eclipse 方面还是比刚刚开始支持Groovy的Netbeans好过不少。

Grails本身尚不够成熟

在开发过程中也遇到过若干 Grails的bug,有的bug甚至导致某功能无法正常运行。使用一个尚不成熟的框架,遇到bug也是正常的事情。解决bug的话基本上可以先到 Grails邮件列表里提问和寻找答案,需要的话提交bug报告到Grails官方JIRA,提供bug数据等待修复。不过如果遇到紧急问题,还是自己动 手更好。可以从Grails主页下载带源码的Grails安装包,就可以直接调试bug并编译修改代码,这样不用等官方发布下一个小版本就可以直接解决问 题了。在Feedlr开发过程中遇到过若干比较紧急的bug,我随着bug报告也提供过若干补丁程序,在最新的Grails 1.0中都已被集成。在这里也要建议大家在使用过程中多多提交bug报告和提供补丁程序,这是良好的参与建设开源社区的方式。

总结

要 完整地实现Feedlr显然还有很多工作要做,但Grails确实大大减轻了程序员编码工作的负荷。通过grails stats命令可以看到,除去测试代码,feedlr最终的代码规模约是1.9kloc。对于一个完整地具备了包括全文检索,RSS/Atom feed生成(Feedlr提供最新机器人服务列表的feed),Tag标签功能,OpenID+普通登录方式整合等功能的web 2.0网站来说,这确实意味着我省去了不少打字的工夫,避免了传统JEE繁琐的开发方式。

那么,Grails到底是不是JEE世界的圣杯呢?我将在本系列的下篇文章中进一步进行分析。

参考资料

Grails:http://grails.org
Grails文档:http://grails.org/doc/1.0.x/
Groovy:http://groovy.codehaus.org/
Mastering Grails系列文章:http://www.ibm.com/developerworks/views/java/libraryview.jsp?search_by=mastering+grails
作者简介

侯雍容,毕业于复旦大学计算机专业,毕业后在IBM中国开发中心从事软件开发工作,目前创立了上海睿谷信息科技有限公司,从事软件开发和咨询工作。对于敏捷Web开发、动态程序语言、云计算等方面都有特别的兴趣和经验。可以从这里看到关于他的详细资料:http://www.linkedin.com/in/houyr,也可以访问他的个人博客:http://damienh.org

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,294评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,493评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,790评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,595评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,718评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,906评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,053评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,797评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,250评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,570评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,711评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,388评论 4 332
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,018评论 3 316
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,796评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,023评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,461评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,595评论 2 350

推荐阅读更多精彩内容

  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,633评论 18 139
  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,825评论 25 707
  • Swift版本点击这里欢迎加入QQ群交流: 594119878最新更新日期:18-09-17 About A cu...
    ylgwhyh阅读 25,335评论 7 249
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,066评论 4 62
  • 在实际开发中,有许多跟键盘打交道的地方: 避免键盘遮挡视图(注册通知,键盘出现和消失时改变视图位置) 自定义表情键...
    geekLiu阅读 2,048评论 2 1