领先一步
VMware 提供培训和认证,以加速您的进步。
了解更多在我之前的文章中,我向您展示了如何使用相应的插件轻松地将 Grails 应用程序部署到Cloud Foundry。希望这能激发您的兴趣,并准备好查看更复杂的 Grails 应用程序,该应用程序演示了 GORM 插件的功能并扩展了 Cloud Foundry 服务。如果您还没有 Cloud Foundry 帐户,请耐心等待。对公告的响应非常热烈,因此需要一些时间才能处理积压的请求。
简单的 Twitter 克隆已成为 Grails 应用程序示例的标准,因此另一个为 Cloud Foundry 开发的版本也就不足为奇了。您可以在GitHub 上找到代码以及其他 Cloud Foundry 示例,您还可以测试已在云端运行的应用程序实例。
在这种情况下,我们使用了混合的数据存储,其中 MongoDB 存储状态消息和标签,SQL 数据库用于用户信息(因为我们使用的是 Spring Security),Redis 用于缓存整体标签信息。
该Person和Authority域类是标准的 Spring Security,因此我不会详细介绍它们,除了它们由 Hibernate 映射,因此在应用程序部署时需要 Cloud Foundry MySQL 服务。
更令人感兴趣的是Status域类。通过向类添加静态mapWith属性,我们可以确保其实例存储在 MongoDB 而不是 SQL 存储中。
package org.grails.twitter
import org.grails.twitter.auth.Person
class Status {
static mapWith = "mongo"
static transients = ["author"]
String message
Long authorId
List<String> tags = []
Date dateCreated
Person getAuthor() {
return Person.get(authorId)
}
}
除此之外,它看起来像一个非常标准的 GORM 域类。那么我们如何将存储在 MongoDB 中的状态消息与存储在 SQL 数据库中的用户关联起来呢?在这种情况下,我们无法使用标准的 GORM 一对一关系,因此我们显式地将用户的 ID 存储在状态消息中,并添加一个瞬态的、只读的author属性,以便轻松访问关联的Person实例。当你知道了方法,这很简单!
您还可以看到标签不是作为单独的域实例存储的(标签是状态消息中以“#”开头的任何字符串)。相反,每个Status将标签列表存储为纯字符串 - 这是 MongoDB 网站上建议的一种方法。这意味着保存和检索状态消息非常便宜,但它确实提出了一个重要的问题:如何找出系统中有哪些标签以及每个标签出现了多少次?毕竟没有“标签”表可以查询,这通常是在关系数据库中进行的操作。
这就是 MongoDB 对映射归约函数的支持发挥作用的地方。映射函数提取所有状态消息中的所有标签,然后归约函数将它们聚合,并为每个标签提供一个总数。您可以在TagService.cacheTags()方法中看到相关的代码。理解映射/归约可能需要一些时间,但它非常适合并行计算。但是,使用单个 MongoDB 实例,此操作的速度并不快。因此,缓存结果是有意义的。
每当我想起缓存时,我的思绪就会立即转向 Ehcache。它就像 Java 平台上开源缓存的鼻祖。即使是 Grails 的 Spring Cache 插件目前也硬编码为使用它。那么当它部署到云端时,我们能否将其用于我们的应用程序?如果我们只打算拥有一个应用程序实例,那么当然可以。您只需要配置磁盘存储的位置,尽管这并不简单,我稍后会谈谈文件系统访问。但云部署的主要优势之一是能够快速创建多个实例来处理负载,在这种情况下,Ehcache 不是一个选择。
缓存通常是简单的键值存储,因此 Cloud Foundry 目前提供了一种替代方案,即 Redis 服务,以及其他缓存服务,如vFabric Gemfire正在开发中。在 GrailsTwitter 应用程序中,我们枚举标签及其总数,并将结果存储在 Redis 排序集中。这是在事务中完成的(通过redis.multi()/.exec()),确保集合以原子方式更新 - 这很重要,因为可能会有很多并发请求获取标签列表。作为奖励,排序集确保我们始终按“总数”顺序检索标签。
当然,由于 Redis 是一个独立的实例,因此您可以根据需要拥有任意数量的应用程序实例:所有实例都将使用相同的 Redis 服务。该应用程序也是如何直接使用 Redis 而不是通过 GORM API 的一个很好的演示 - 我们没有在 GrailsTwitter 中将任何域类映射到 Redis。
示例应用程序还演示了 Searchable 插件的使用,该插件基于 Compass,在这种情况下处理用户搜索。现在,插件必须将其搜索索引存储在某个地方,那么当我们将应用程序部署到 Cloud Foundry 时,它如何知道将它们放在哪里呢?
在许多生产环境中,您提前知道可以存储文件(例如搜索索引)的位置,因此您将路径放在运行时配置中。或者,您可以通过grails.config.locations设置包含外部配置文件,并且 Ops 将在该文件中放置相应的路径。但是对于 Cloud Foundry,您事先不知道任何可用的文件位置,也不能部署外部配置文件。
幸运的是,Cloud Foundry 通过HOME环境变量在运行时提供合适的路径。您的应用程序只需读取它,然后使用该路径作为基础创建任何它需要的目录和文件即可。在 Searchable 的情况下,Cloud Foundry 插件会自动将搜索索引的标准位置替换为基于HOME变量的位置,因此您无需执行任何操作!
这一切都很棒,但云中的文件系统访问存在一个严重的问题,我在前面谈论 Ehcache 时提到了它:如果您有多个应用程序实例,那么每个实例将拥有自己的(不同的)文件副本。这很少是您想要的。例如,所有应用程序实例都应共享相同的搜索索引,否则,访问一个实例的用户将获得与由另一个实例提供的用户不同的搜索结果。
Cloud Foundry 仍处于早期阶段,因此您可以期待在今年剩余时间内上线更多服务。已经有人请求提供搜索服务(以及其他服务)。同时,我们可以提供一种解决方法,它说明了云中协调挑战的常见解决方案:使用 Redis 对发布/订阅消息的支持来保持文件同步。例如,在 Grails Twitter 中,我们可以在每次添加用户时触发一个事件,然后所有实例都会侦听该事件并索引新用户。修改或删除用户也是如此。Grails 的 Redis 插件在撰写本文时(即将推出!)并不开箱即用地支持发布/订阅,但在该支持可用之前,您可以使用 Spring Data。
如您所见,您已经可以通过明智地使用可用服务(特别是 Redis)将中等复杂的应用程序部署到 Cloud Foundry。而且,您可以轻松地将应用程序部署到 Cloud Foundry,这使其成为一个引人注目的部署目标。
尽管如此,了解将来会向 Cloud Foundry 添加更多服务这一点很重要,这将使您能够部署具有更多功能的应用程序。并且随着这些服务的到来,我们致力于确保相应的 Grails 支持与现有支持一样流畅易用。因此,请尝试、玩得开心,并期待 Cloud Foundry 服务的一些令人兴奋的补充!