内容


Java 开发 2.0

Play 框架在 Amazon RDS 中的应用

关系数据管理即服务?为什么不是?

Comments

系列内容:

此内容是该系列 # 部分中的第 # 部分: Java 开发 2.0

敬请期待该系列的后续内容。

此内容是该系列的一部分:Java 开发 2.0

敬请期待该系列的后续内容。

几个月前,我在本系列中提供了 Amazon 的 Elastic Beanstalk 的实用 Java 开发 2.0:攀登 Elastic Beanstalk,Elastic Beanstalk 是一种用于 Java 应用程序开发的平台即服务 (PaaS)。正如我所演示的,Beanstalk 功能非常丰富,允许开发人员使用几乎任何工具组合来完成工作。除了使用 Play 执行快速 Web 开发,我还能够使用另一种 PaaS MongoHQ 来管理我的 MongoDB 实例。将两种 PaaS 基础架构相结合,意味着可以为我处理大多数应用程序维护工作,我可以将精力集中在构建优秀的云到移动应用程序上。

MongoHQ PaaS 出色地完成了该项目,但如果我希望将数据存储在传统的 RDBMS 中怎么办?毕竟,大部分 Java 开发人员更熟悉向关系数据库编码。而且不是每个项目都能不使用 ACID,然而 NoSQL 数据库基本都不支持 ACID。

本月,我将介绍 Amazon 关系数据库服务 (RDS),这是 Amazon Web 服务家族的另一项优秀且多用途的新添成员。Amazon RDS 使用起来非常简单,类似使用由 MongoHQ 托管的 MongoDB 实例。它的关系很容易管理,但它将带来一些有趣的扩展问题。我们仅需要提前执行一些循环来定义模式,然后就可很好地使用它。

Amazon RDS

Amazon 关系数据库服务是一种 PaaS,提供了一种用于应用程序开发的按需、基于云、可扩展的 MySQL 实例。但是,如果 RDS 是一个在云中运行的 MySQL 实例,我们就不会在这里介绍它。毕竟,我已在 2009 年解释了如何使用运行 MySQL 的 Amazon EC2 映像(参见 参考资料)。

使 RDS 值得一看的是,它由 PaaS 管理并由 Amazon 操作,提供了与 Elastic Beanstalk 中相同的服务和灵活性。Amazon 提供了备份、复制甚至补丁。而且,RDS 可全面扩展,您只需几次单击即可增加应用程序的存储容量。Amazon 还支持跨可用性区域复制 RDS,所以如果一个区域出现故障或一个区域中计划了一个维护时窗,您仍然可以提供数据。甚至可以配置数据库的只读实例,确保为高容量应用程序或时间段提供更高的读取速度。

已使用 MySQL 而构建的应用程序可以不断利用 RDS,所以尽管数据库处于云中,但其他关于应用程序的所有内容都无需更改。最后,与 AWS 的其他所有方面很类似,RDS 是一种即付即用模型。没有前期的硬件或许可证成本。您会在使用容量、存储和带宽时为它们付费。

设置和配置 RDS

AWS 允许通过命令行或 AWS 管理控制台配置各种服务。我倾向于使用控制台,因为它指定了可用于 RDS 的各个方面的选项。所以,我们首先将在 AWS 管理控制台中选择 RDS 选项卡,单击 Launch Instance 按钮。

在屏幕上,您应该会看到一个对话框,可在其中指定 MySQL 数据库实例细节,比如机器大小、MySQL 版本和为数据库实例分配了多少存储。请注意,AWS 有一个向多个可用性区域部署数据库的选项。这样做会在本质上创建一个集群,这样,如果特定区域出现故障,其他区域将确保数据库可用。

如图 1 所示,您需要指定数据库的模式名称、管理用户名和密码:

图 1. 设置 RDS 实例
配置 MySQL 数据库实例。
配置 MySQL 数据库实例。

单击 Continue,您将看到另一个对话框,可使用它对数据库进行大体配置。首先是它的名称,名称是其 JDBC URL 中的一部分。也可以更改 MySQL 将监听的端口,选择数据库将存在于的可用性区域,如图 2 所示:

图 2. 配置 RDS 实例
选择数据库可用性区域的屏幕。
选择数据库可用性区域的屏幕。

接下来是与让 AWS 如何备份数据和计划何时维护相关的管理选项,如图 3 所示:

图 3. RDS 管理选项
通过 AWS 控制台启动和配置数据库管理。
通过 AWS 控制台启动和配置数据库管理。

单击 Continue 并检查配置之后,启动 RDS 实例。这可能需要几分钟才能完成,所以正好可以喝杯咖啡或浏览浏览 Twitter。RDS 正常运行之后,操作就快了!

RDS 安全

实例激活后,还需要执行一些操作,才能使用您最喜爱的 SQL 管理工具访问它。如果之前使用过 AWS,那么不应该对默认情况感到惊奇,一些功能被锁定,所以需要显式允许访问。

RDS 的安全约束非常强大,允许您指定可与 RDS 通信的单个 IP 或 IP 范围,但出于本文的意图,我们将保持简单,允许任何 IP 与我的实例通信。为此,我进入 RDS DB Security Group 窗格并将 CIDR/IP 约束编辑为 0.0.0.0/0,这基本上意味着允许所有 IP。这一步如图 4 所示:

图 4. RDS 安全设置
AWS 上的 RDS DB Security Group 窗格的屏幕截图。
AWS 上的 RDS DB Security Group 窗格的屏幕截图。

完成此步骤后,重新启动 RDS 的实例。(右键单击 AWS Management Dashboard 中的实例,您可以看到重新启动选项。)

如果选择您正在运行的 RDS 实例,将会看到一个 Description 窗格(如图 5 所示),它提供了一些重要细节。我们现在最感兴趣的是端点,它是您将用于连接 MySQL 实例的 URL。

图 5. RDS 仪表板
AWS 控制台上的 RDS 仪表板的屏幕截图。
AWS 控制台上的 RDS 仪表板的屏幕截图。

现在您可以获取最喜爱的数据库管理工具(或者如果愿意,使用命令行),将它指向您的 RDS 实例。我将使用 Sequel Pro,它与大部分面向 GUI 的工具一样,提供了一个优秀的界面来查看表和数据,还提供了一个查询控制台。要进行连接,您需要知道您数据库的用户名、密码和端点 URL。

Magnus、RDBMS 样式

如果阅读我的 Amazon Elastic Beanstalk 简介(参见 参考资料),您会很熟悉我为该文章构建的云到移动应用程序 Magnus。对于 Magnus,我在 MongoDB 中创建了两个集合:AccountLocation。(但是请注意,我可以让一个集合复制 Account,每个文档包含一个嵌入式的 Location 文档。)拥有两个集合使我能够持久保留帐户位置,这些位置在我的应用程序场景中来自全球的移动设备。

我将为演示 Amazon RDS 建模相同的关系,但这次我将采用老式方法。像 Magnus 这样的应用程序(具有来自分散在全球的频繁位置更新)可从 RDS 获益,尤其是在 RDS 支持在众多可用性区域执行集群化时。请记住,RDBMS 领域还有其他一些技术,比如分片,它将数据分解为逻辑分区,所以也可以按地理位置对帐户和位置进行分片。无论如何,集群、复制和分片都具有优缺点,在开始采用一种具体战略时应该认真权衡它们。

为了让 Magnus 程序使用 SQL 数据库,我需要首先定义我的关系表,(可以预测)它将调用 accountlocation。这如清单 1 中所示:

清单 1. account 表
CREATE TABLE `account` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(30) DEFAULT NULL,
  PRIMARY KEY (`id`)
)

在清单 2 中我的 location 表中,我提供了一个指向 account 的外键。这样,我在 account 与它们的各种 location 之间设置了一对多关系。

清单 2. location 表
CREATE TABLE `location` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `account_id` int(11) DEFAULT NULL,
  `latitude` double DEFAULT NULL,
  `longitude` double DEFAULT NULL,
  `date_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
  `name` varchar(50) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `account_id` (`account_id`),
  FOREIGN KEY (`account_id`) REFERENCES `account` (`id`)
)

定义表之后,我需要在我的 RDS 实例中创建它们。在 RDS 实例中创建表就像在本地机器上运行的 MySQL 实例中创建它们一样。只需打开一个终端或使用您最喜爱的 GUI 打开您的 RDS 端点并创建表。就这么简单。

配置 Play

Play Web 应用程序框架(参见 参考资料)在 Amazon RDS 中的工作原理与在 Elastic Beanstalk 中类似,所以我将再次使用它来开发我的应用程序。Play 使得切换各种数据存储非常轻松,它开箱即用地支持 JPA。从 MongoHQ 实现切换到使用 Amazon RDS 的实现只需对我的模型进行少量更改。首先,Play 内置的 JPA 支持意味着我需要修改 Magnus 的 application.conf 文件并将内容指向我的 RDS 实例,如清单 3 所示:

清单 3. location 表
db.url=jdbc:mysql://magnus.cp3pl5vineyp.us-east-1.rds.amazonaws.com/magnus_locations
db.driver=com.mysql.jdbc.Driver
db.user=admin
db.pass=g3tf0kl

这里没有需要注意的特殊内容。清单 3 百分之百是 JDBC!

建模关系

与几乎所有 RDBMS ORM 库一样,我将使用顶级 Java 对象在一种一对一关系中建模我的表:一个 Account 类型和一个 Location 类型。我的 Location 将链接回 Account

从 Play 的模型类型扩展,为我提供了一些内置的免费服务。首先,我获得一个类似 finder 的方法,以及典型的 CRUD 方法 savedelete

我的 Account 类利用了两个 JPA 注释。清单 4 中所示的 Table 注释需要指向我重命名的(小写的)account 表。

清单 4. 一种 Account 类型
package models;

import play.db.jpa.Model;
import javax.persistence.Entity;
import javax.persistence.Table;

@Entity
@Table(name = "account") //required otherwise table is Account
public class Account extends Model {
 public String name;
}

我的 Location POJO(如清单 5 中所示)稍微复杂一点。我需要向它添加两个额外的注释,以在 LocationAccount 之间创建一种多对一的关系。

清单 5. 使用 JPA 定义的 Location 类
package models;

import play.db.jpa.Model;
import javax.persistence.*;
import java.math.BigDecimal;
import java.sql.Timestamp;

@Entity
@Table(name = "location")
public class Location extends Model {

 public BigDecimal latitude;
 public BigDecimal longitude;
 public String name;
 @Column(name = "date_time")
 public Timestamp timestamp;

 @ManyToOne
 @JoinColumn(name="account_id", nullable = false)
 public Account account;

}

最后,在清单 6 中,我更新了 Application 控制器的 saveLocation 方法。(请记住,在以前的 Magnus 实现中,我将所有对 /location/ 的 HTTP PUT 路由到了此方法。)saveLocation 创建一个新 Location 对象(因此创建了一条相应的记录)并将此 Location 实例链接到现有的 Account。因为我扩展了之前的 Play 的 Model 对象,所以我也获得了一个方便的 findById 方法。

清单 6. 使用 RDS 进行基于位置的更新
public static void saveLocation(String id, JsonObject body) throws Exception {
 String eventname = body.getAsJsonPrimitive("name").getAsString();
 double latitude = body.getAsJsonPrimitive("latitude").getAsDouble();
 double longitude = body.getAsJsonPrimitive("longitude").getAsDouble();

 Location loc = new Location();
 loc.longitude = new BigDecimal(longitude);
 loc.latitude = new BigDecimal(latitude);
 loc.name = eventname;
 loc.account = Account.findById(new Long(id));

 loc.save();

 renderJSON(getSuccessMessage());
}

使用 RESTClient 进行测试

我可以使用 RESTClient 确定我更新的位置服务是否有效。就像我对 Magnus 所做的一样,我将形成一些 JSON 文档并发送它们。

图 6. 使用 RESTClient 进行测试
RESTClient 界面的屏幕截图。

当我的方法成功持久化一条新 Location 记录时,会发送回一条 JSON 响应来表明成功。因为我的二选一式 GUI 允许我查看 RDBMS 中的数据,我只需检查我的 location 表,您猜怎么着?我通过 RDS 看到位于云中的新 Magnus 记录!

图 7. 瞧,您将到达的目的地!
云中的 Magnus,显示在 AWS 控制台上。
云中的 Magnus,显示在 AWS 控制台上。

结束语

PaaS 是希望快速开发和部署 Web 应用程序的软件开发团队的好伙伴。在本文中,我介绍了 Amazon RDS,一个将关系数据库(在本例中为 MySQL,但 RDS 也支持 Oracle 数据库)放在云中的 PaaS 解决方案。Amazon RDS 很容易配置,它的操作与您可能构建了多年的各种 RDBMS 系统没什么区别。重要的区别在于 Amazon 会为您处理维护。


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Java technology, Cloud computing
ArticleID=755736
ArticleTitle=Java 开发 2.0: Play 框架在 Amazon RDS 中的应用
publish-date=09052011