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

 

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 = 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世界的圣杯呢?我将在本系列的下篇文章中进一步进行分析。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值