内容


功能丰富的 Perl

Perl 和 Amazon 云,第 1 部分

通过构建简单的照片共享网站学习 Amazon S3 和 SimpleDB 服务的基础知识

Comments

系列内容:

此内容是该系列 # 部分中的第 # 部分: 功能丰富的 Perl

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

此内容是该系列的一部分:功能丰富的 Perl

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

您需要学习 Amazon 的两个 Web 服务:Amazon S3 (Simple Storage Service) 和 Amazon SimpleDB。没有比动手实践更好的学习方法了。在本文中,您将构建一个简单的照片共享站点。

我们的目标并不是构建一个经过良好设计的站点;因为我们已经实现过很多次了。此外,将站点组合起来是件困难 的事情,技术仅仅是诸多因素中之一,因此不要向我抱怨 “伙计你的品味太差了”,因为 sharethewindbeneathmydonkey.com 在第一个星期内不会让您成为百万富翁(但是如果确实是这样的话,请您一定不要忘记您的启蒙者)。

我在本系列中使用 share.lifelogs.com 作为域名。现在让我们来了解一下 Amazon S3 吧。

Amazon S3 概述

我成为一名 UNIX® 管理员已经有一段时间了,因此我可以告诉您备份和文件存储并不是简单的服务。如果 SAN、NAS、LUN、LVM、RAID、JBOD、IDE 和 SCSI 之类的缩略语对您毫无用处的话,那么您就可以松一口气了。但是如果情况不是这样的话,那么在您从损坏了的使用了 4 年的 DLT 备份中进行恢复后的第三月时,您一定会在午餐时愁得抹眼泪并且希望能有一种更好的方法来管理数据。不过我以前可没有做过这种事。

Amazon 的 S3 (Simple Storage Service) 是一个分布式存储系统。如果您愿意将数据交由 Amazon 处理,那么您的生活将变得非常的轻松。当然,您可以经常进行备份以确保万无一失。(安全性也许会成为一个问题:将数据放入 S3 中意味着您必须使用 S3 的访问控制系统,而这个系统可能不能满足您的身份验证和授权需求。请查阅 S3 文档获得详细信息)。

那么您从 S3 可以获得什么呢?S3 使用一个键(一个较长的随机字符串)和一个用户密码(另一个随机字符串)来让您存储和检索文件。您根据 Amazon 的 S3 的定价付费(可以在它们的网站中找到这些定价)。价格不是很高;如果与使用您自己的 NAS 或 SAN 或本地磁盘的成本相比的话,S3 的价格是非常合理的。

在 2009 年初,S3 数据通过良好的网络连接由两个 Amazon 数据中心(US 和 EU 中心)托管。如果您需要将数据提供给美国和欧洲以外的大型客户,那么应当使用 Gomez 或 Keynote 等的服务进行测试,这些服务专门用于确定世界范围内的性能。即使是在美国和欧洲,如果您的业务要求快速、可靠地传递数据,您也应该通过此类服务建立日常性能测试。

分布式存储系统的主要问题是它的更新延迟性。这是指内容所有者发出操作和传播这些操作之间的时间。但是操作和传播之间的时间并不是惟一的潜在问题;操作的传播可能不是统一的,因此客户可能在不同的时间看到不同的内容。Amazon 可以在服务器中确保一致性,意味着您的客户不会看到破损的数据,但是您在评估 S3 是应当考虑到这点。当您卸载、修改或删除图像时,不要指望这些修改会立即生效。

CPAN 提供了有关 S3 访问的 Perl 库(参见 参考资料)。我将展示如何使用 Net::Amazon::S3,但是 Amazon 的 S3 资源页面中列出了大量其他的库。(此外,还提供了大量用于访问 S3 的优秀工具 — 比如 JungleDisk 或 Firefox S3Fox 附件 — 这大大简化了未使用 Perl 的情况下的数据管理)。

Amazon S3 示例

现在,看看 S3 提供了什么。文件(S3 中称为对象)保存在 bucket 中。在每个 bucket 中,文件名(即)是惟一的。您可以提供 “颜色” 或 “语言” 等文件属性,但是它们不包含在文件名中。

假设我们将美国国旗的图片作为 “images/flag.png” 文件存储在 bucket “us.images.share.lifelogs.com” 中,而将德国国旗作为文件 “images/flag.png” 存储在 bucket “de.images.share.lifelogs.com” 中(它们的文件名相同,但是保存在不同的 bucket 中)。您的用户可以随后请求 http://us.images.share.lifelogs.com.s3.amazonaws.com/images/flag.png 来获得美国国旗,或者通过请求 http://de.images.share.lifelogs.com.s3.amazonaws.com/images/flag.png 获得德国国旗。此外,可以在 DNS 中将 de.images.share.lifelogs.com 作为 de.images.share.lifelogs.com.s3.amazonaws.com 的别名(对 us.images.share.lifelogs.com 做同样的操作),这样,用户只需请求 http://us.images.share.lifelogs.com/images/flag.png 或 http://de.images.share.lifelogs.com/images/flag.png 就可获得国旗。

注意,bucket 名在所有 Amazon S3 帐户中都是惟一的,因此像 “test” 或 “default” 这样的名称是不合适的。如果可以的话,使用完整的域名限定 bucket 的名称。这将使在 DNS 中识别和使用 bucket 变得更加简单。同样,bucket 名称是有限制的,因此不要使用特别长的名称。坚持使用在域名中出现的相同字符。

S3 是一项复杂的服务,因此我建议您在继续阅读之前查看一下 S3 的主页。

Amazon SimpleDB 概述

通常在介绍这一部分内容的时候,职业演讲人和大学教授会对因为前一晚上纵饮过度而在前排大打瞌睡的老兄特别问候一声:数据库非常重要!

您醒了吗?

进行分类、过滤、聚集、平均分配和分析,我们每天面对的数据流会变得难以管理。IT 专业人员需要全天托管这些数据库。他们需要存储空间、电源、备份以及其他许多资源。作为一项财务决策,使用诸如 SimpleDB 之类的托管数据库对您的业务来说可能是值得的;我在这里仅解释一下它的技术方面。

一个简单的数据库示例就是冰箱上面贴的备忘单:每一项占一行,在某些项的旁边划了一个对钩,而其他项可能被勾掉。在传统的关系数据库中,这个备忘单可能被建模成两个表,每个表包含两个列:

表 1. todo_foreign 表
事项状态码 (status.statuscode 的外键, 默认为 0)
给妈妈打电话0
给 IRS 打电话2
取牛奶1
表 2. 状态表
状态码状态描述
0活动
1已完成
2已删除

“但是,等等,” 您会说。“当这些事项被完成或删除后数据会怎么样,谁修改了数据,数据类型是什么?毕竟,这就是我们培养聪明的、有能力的数据库管理员(DBA)的原因。??们了解有关普通表单和外键以及 SQL 的所有内容。当然,您需要他们当中的其中之一来立即检查您的设计,对不对?”

是的,谢谢您的提醒,您可真聪明,不过请别干扰我的这个简单示例,好吗?稍后您可以抱着一本 “Secrets Of The SQL and RDBMS Gods For Dummies” 自我安慰一下。

Amazon SimpleDB 是一个广泛分布的关键属性数据库。它绝对不是面向所有业务的,并且在性能和可伸缩性方面也有严格的限制。每个属性被限制为 1KB,因此您的备忘事项的名称不能超过 1 KB。

安全性也是一个问题;SimpleDB 的访问控制系统类似 S3。一个简单的社会性站点(比如将在本系列中遇到的站点)可以使用 SimpleDB 作为后端数据库。但是,您应当考查业务需求、预算和数据存储需求,以确定 SimpleDB 是否能够完全满足这些要求。

前面提到的 S3 更新延迟问题也将对 SimpleDB 产生影响。您的更新并不能在所有位置立即生效。

如果使用简单的数据库示例,SimpleDB 结构将如下面所示:

表 3. SimpleDB todo 结构
事项状态
给妈妈打电话活动
给 IRS 打电话已删除
取牛奶已完成

到目前为止,一切良好。这要比第一个例子简单,不是吗?但是让我们再添加另一个事项:

取奶牛活动

您看到状态被复制了吗?active 在数据库中被存储了两次。对于存储和性能而言,这对大型表来说开销太大。另一方面,每个 SimpleDB 行被设计为可以自己提供自己需要的东西。当您获得该行时,您将得到其中包含的所有内容。您不需要查找状态描述。对于 SimpleDB 的更新延迟,这会产生问题。

更多 SimpleDB 备忘单

假设您添加了一个新的状态码 waituntiltomorrow,并将它应用到 todo_foreign 表(包含外键的表 1)中的某一项。这样,您现在拥有了两个更新(一个针对状态表,一个针对 todo_foreign)。如果状态表(表 2)在 todo_foreign 更新之后 执行更新,那么您将得到不一致的数据。记住,SimpleDB 并不保证您的更新按照您执行的顺序立即生效,因此除了执行两个查找(一个用于查找项,另一个用于查找状态码描述)而使性能受损外,您可能还会得到不一致的数据。

SimpleDB 的关键在于:忘记了 todo_simple 中的列(表 2)。SimpleDB 并没有列!它为每个行提供了属性。这些属性不是静态的,因此可以随意添加和删除它们。您希望您的备忘事项有一个创建和删除日期?那么设置属性就可以了。在 todo_foreign 中,这将需要两个列;删除日期可能为 null,表示该事项仍然在活动中。让我们再添加一个列,表示完成该事项的日期。或者只使用状态码,并使用删除日期作为完成日期。我们该怎么做?

SimpleDB 的风格就是随心所欲地执行您需要的操作。需要一个创建日期?设置一个 created_date 属性。想要一个删除日期?只需为已被删除的项分配相关属性。属性的存在 告诉我们它应用于这个项。

不要再从列的角度思考了。SimpleDB 的行更类似于 Perl 的散列。每个键就是一个字符串。每个值就是一个字符串或字符串数组。让我们再次进行设计。

清单 1. todo_freeform
{ item: "call Mom" }
{ item: "call IRS", deleted_date: "2009-03-01" }
{ item: "get milk", done_date: "2009-03-02" }

注意 SimpleDB 有一个名为 ItemName 的隐含键,在本例中为字符串形式的备忘事项,如下所示?

清单 2. SimpleDB 备忘单
"call Mom" {  }
"call IRS" { deleted_date: "2009-03-01" }
"get milk" { done_date: "2009-03-02" }

SimpleDB 要求对象必须具有属性,因此为所有对象分配一个 created_date 属性,如下所示:

清单 3. 添加了 created_date 属性的 SimpleDB 备忘单
"call Mom" { created_date: "2009-02-01" }
"call IRS" { created_date: "2009-02-01", deleted_date: "2009-03-01" }
"get milk" { created_date: "2009-02-01", done_date: "2009-03-02" }

“但是等等”,您会喊道,“所有内容真的是一个字符串吗?数据没有被严格输入吧?哈!完蛋了!”

是的。所有内容都是字符串。是不是很令人惊奇?

并且您可以向该表中任何已经删除了三个月的事项添加一个 deletereason 属性。不会产生任何影响,并且只有新添加的代码才会使用它。

这里将产生一些戏剧化的效果,DBA 需要服用几片阿司匹林。同时,Perl 程序员会为他们递上一杯水,为什么?仅仅因为我们本来就是一些非常不错的人。

继续这个示例。这里的重点是创建搜索活动的、已删除的或已完成的事项的查询。这非常简单;可以查看 SimpleDB 文档找到所有查询选项。我们将使用 SELECT 语言。还有一种 QUERY 语言,但是 SELECT 更接近 SQL,因此更容易被大部分读者理解。

清单 4. todo_freeform 查询
-- get active
select * from todo_freeform where done_date is null and deleted_date is null
-- get deleted
select * from todo_freeform where deleted_date is not null
-- get done
select * from todo_freeform where done_date is not null

就是这样。现在让我们把 SimpleDB 和 S3 结合起来。

集成服务并共享照片

您接下来也许要问,如何将 SimpleDB 和 S3 联系起来?(除了使用访问控制模型外,它们并不存在内在联系)。很简单:您可以轻松地将一个 S3 对象的 bucket 和名称存储到 SimpleDB 中。不管怎样,至少可以实现备忘单;让我们开始设计照片共享站点吧。

站点需要在 S3 中存储照片并使用 SimpleDB 中的用户评论。那么用户帐户放在哪里?我们需要忍受 SimpleDB 的分布式特性,这意味着我们有时会得到无效的用户(例如在没有推出用户但推出了引用该用户的行的时候)。但是,对于这个应用程序,我们将用户保存在 SimpleDB 中。没有依赖任何外部数据库,因为我们的目标是能够在任何位置快速创建站点,所要做的仅仅是在 mod_perl 下运行一些 Perl glue,在 S3 和 SimpleDB 中执行一些真正的操作。

首先,您需要一个照片表。表记录如下所示:

清单 5. 照片表记录,share_photos
"http://developer.amazonwebservices.com/connect/images/amazon/logo_aws.gif" 
{ user: "ted", name: "Amazon Logo"}

"http://images.share.lifelogs.com/funny.jpg" 
{ user: "bob", name: "Funny Picture",  s3bucket: "images.share.lifelogs.com" }

接下来,创建一个用户表:

清单 6. 用户表,share_users
"ted" { given: "Ted", family: "Zlatanov" }
"bob" { given: "Bob", family: "Leech" }

然后是评论:

清单 7. 评论,share_comments
"random-string"
{ 
 url: "http://images.share.lifelogs.com/funny.jpg",
 comment: "Ha ha", 
 posted_when: "2009-03-01T19:00:00+05" 
}

"random-string2"
{ 
 user: "ted",
 url: "http://developer.amazonwebservices.com/connect/images/amazon/logo_aws.gif", 
 comment: "No it doesn't", 
 posted_when: "2009-03-01T20:00:01+05" 
}

"random-string3"
{ 
 url: "http://developer.amazonwebservices.com/connect/images/amazon/logo_aws.gif", 
 comment: "No it doesn't", 
 reply_to: "random-string2", 
 posted_when: "2009-03-01T20:00:01+05" 
}

我们将通过查看 reply_to 键将评论串接在一起。每一个帖子将使用一个随机字符串作为惟一键。

注意,我们在这里确立了一些约定:

  • 缺乏 user 属性意味着评论是匿名的。
  • 照片 URL 将所有评论合并在一起,因此改变照片的 URL 是不允许的。
  • S3 对象也有一个 URL,并使用一个 bucket 名来将它们标识为 S3 对象。
  • 复制照片 URL 是不允许的,因为 URL 是对象的键。

这并不是这个表设计的最终版本(记住,SimpleDB 非常灵活),但是足够作为入门的基础。

结束语

我在本文中比较详细地讨论了 S3 和 SimpleDB 的优点和不足。尽管谈不上完整,但是这些讨论将帮助您决定 Amazon 的 S3 和 SimpleDB 是否适合您的项目。

在 SimpleDB 中提供了一个简单备忘单示例后,我展示了如何设计用户、照片和评论表的数据布局。

在第 2 部分中,我将对该 Web 站点和库进行设置(使用 Apache 和 mod_perl)以处理 S3 和 SimpleDB。


相关主题


评论

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

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Linux, Open source, Web development, Information Management
ArticleID=385507
ArticleTitle=功能丰富的 Perl: Perl 和 Amazon 云,第 1 部分
publish-date=04272009