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

来源:互联网 发布:淘宝收藏 猪八戒网 编辑:程序博客网 时间:2024/04/30 18:06

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框架的选择

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 = truetry{    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-testgrails 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繁琐的开发方式。

0 0
原创粉丝点击