OceaBase开发者大会落地上海!4月20日共同探索数据库前沿趋势!报名戳 了解详情
写点什么

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

  • 2009-02-05
  • 本文字数:5646 字

    阅读完需:约 19 分钟

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

参考资料

作者简介

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


给 InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家加入到 InfoQ 中文站用户讨论组中与我们的编辑和其他读者朋友交流。

2009-02-05 02:298056

评论

发布
暂无评论
发现更多内容

产品更新 | 思码逸 DevInsight,新增虚拟项目团队的管理与度量

思码逸研发效能

Spark技巧大揭秘:构建分布式造数工具加速工作效率

测吧(北京)科技有限公司

测试

TikTok直播专线,全程稳定的专属网络

Ogcloud

Tik Tok直播 Tik Tok直播网络 Tik Tok 海外直播 直播专线

聚道云软件连接器2月新增应用/产品更新合集

聚道云软件连接器

功能更新

DevChat上新:/commit让你躺平都能写好规范的提交消息

思码逸研发效能

评估模型效果:深入解读混淆矩阵、精准率、召回率和F1 score

测吧(北京)科技有限公司

测试

数据闭环的建立:确保模型发展的可持续性

测吧(北京)科技有限公司

测试

智达方通总经理蔡志宏先生受邀参与哈工大京津冀校友发展研讨会,共话科技创新与发展新篇章

智达方通

智达方通 企业绩效管理 预算管理 哈工大 校友会

数据与特征:解密模型解决实际问题的关键

测吧(北京)科技有限公司

测试

训练集、验证集和测试集:区别与应用详解

测吧(北京)科技有限公司

测试

数据闭环构建技巧:确保模型稳定性与数据质量

测吧(北京)科技有限公司

测试

Spark在分布式造数工具中的应用探索

测吧(北京)科技有限公司

测试

深入理解训练集、验证集和测试集在模型训练中的作用

测吧(北京)科技有限公司

测试

Pygame:实现Python游戏开发的跨平台梦想

技术冰糖葫芦

API 文档

防水堡是什么?各行各业怎么定义?

行云管家

网络安全 防水堡 行云防水堡

人工智能发展史:从专家系统到机器学习的演进

测吧(北京)科技有限公司

测试

数据挑选技巧:提升模型质量的关键一步

测吧(北京)科技有限公司

测试

2024年,提升Windows开发和使用体验实践 - 小工具篇

EquatorCoco

架构 工具 windows 工具分享

微店商品详情 API 的调用频率有限制吗?

技术冰糖葫芦

API 文档

TikTok直播:是否需要专线,以及加速的方法有哪些?

Ogcloud

海外直播专线 Tik Tok直播 Tik Tok直播网络 直播加速 海外直播

自学习的现实应用:构建智能系统的新思路

测吧(北京)科技有限公司

测试

面试官上来就让手撕HashMap的7种遍历方式,当场愣住,最后只写出了3种

不在线第一只蜗牛

Python 程序员 面试 开发语言

如何选择合适的数据提升模型性能

测吧(北京)科技有限公司

测试

JavaScript 的 structuredClone:深拷贝技术全解析

Liam

JavaScript 前端 Web 前端开发 structuredClone

思码逸获得 InfoQ 2023 年度技术生态构建奖

思码逸研发效能

自学习概念探析:构建稳定模型的核心思想

测吧(北京)科技有限公司

测试

从历史视角看人工智能:专家系统、机器学习与深度学习

测吧(北京)科技有限公司

测试

坐标 DISTRIBUTECH,TDengine 发力海外电力行业

TDengine

tdengine 时序数据库

实时数据驱动:API商品数据接口引领业务飞跃

Noah

云游戏:畅享3A游戏大作的全新时代

Ogcloud

游戏 云游戏 3A游戏 3A云游戏

item_get-根据ID取商品详情(shopee.item_get):提高跨境电商交易效率的关键

技术冰糖葫芦

API 文档

案例研究:利用Grails搭建Feedlr.com网站_Java_侯雍容_InfoQ精选文章