这个共分 5 部分的系列文章向您介绍了如何使用 Perl 和 Apache 访问 Amazon 的 Simple Storage Service (S3) 和 SimpleDB,从而构建一个照片共享网站。在本期文章中,通过学习 URL 如何为上传的文件创建 SimpleDB 记录,实现站点与 SimpleDB 的交互。同时了解如何以 SimpleDB 记录的形式创建、编辑、删除某个用户的照片评论。
       距离我的上一期文章已经有一段时间了,现在我们来回顾一下:
- 第 1 部分解释了 S3/SimpleDB 架构以及如何通过实际示例使用它们。
- 第 2 部分展示了如何通过 HTML 表单,从一个网页中将文件上传到 S3,从而最小化服务器负载。
       现在,让我们真正开始深入了解照片共享网站与 Amazon SimpleDB 的交互,首先了解成功的 URL 如何为上传的文件创建 SimpleDB 记录,然后了解如何以 SimpleDB 记录形式创建、编辑、删除某个用户的照片评论。(记住,我们并不是要比较 SimpleDB 和 Google 的 BigTable 或 CouchDB 之类的独立解决方案)。
       和此前的文章一样,我将使用 share.lifelogs.com 作为域名。
查看数据库结构
回顾一下 第 1 部分,我们设置了一个如清单 1 所示的表结构:
清单 1. 来自第 1 部分的表结构
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" }
share_users:
"ted" { given: "Ted", family: "Zlatanov" }
"bob" { given: "Bob", family: "Leech" }
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 doesnt",
posted_when: "2009-03-01T20:00:01+05"
}
"random-string3"
{
url: "http://developer.amazonwebservices.com/connect/images/amazon/logo_aws.gif",
comment: "No it doesnt",
reply_to: "random-string2",
posted_when: "2009-03-01T20:00:01+05"
}
|
       在这个版本的结构中,您可以将所有有关某个主题的评论存储到一个 comment 结构内部,但是,两名用户同时删除或编辑同一主题的评论是非常危险的。
       如我在第 1 部分中谈到的一样,这种结构需要进行修改。SimpleDB 的一大优点就在于其灵活性,因此让我们利用这一点。正如 Emerson 所说,“只有无知的人才会愚蠢地坚持”。当然,他也说过 “我痛恨引用名人语录”。多么无聊的超验主义者。
       我发现 SimpleDB 暂存器(scratchpad)比不上从代码直接生成调用,但是您也许会喜欢它。查看参考资料了解暂存器 URL。您还可以购买一些 SimpleDB 管理工具,对 Firefox 使用免费工具,或者尝试 typica shell。参考资料小节提供了所有相关内容。
上传和修改照片
       首先,注意 SimpleDB 允许对单一键使用多个值。因此我提到的照片 URL 或评论文本可以是数组而不是单个值。我们不会使用这个特性,为了保持简单对每个键使用一个值。
       让我们首先从照片更新开始。每次完成一次上传后,将运行代码来向 share_photos
表添加新照片。第 2 部分展示了 S3 上传表单;下一次我们将把此表单连接到本文编写的代码中。
       现在,让我们编写一个简单的脚本来添加照片。您将获得一个用户名、一个 URL、一个照片名和一个可选的 S3 bucket 名。S3 bucket 就是指表中的一个字段;URL 将用于显示和使用照片。我们希望使用一个复制 URL 来拒绝上传。可以这样做吗?
       分布式数据存储的一个问题就是,您放入其中的数据可能无法超越网络的局限性。就好象从日本呼叫欧洲一样(或做任何体验到网络延迟或语音信号延迟的操作):当对方与您保持同步时,每说一句话都会产生轻微的延迟。您可以不间断地开始讲话,但是另一端的回答将会与您的声音重叠,令您感觉自己好像是一个参加重要会议的主管。尽管延迟性可以为您带来这种良好的感觉,但是它不利于展开有序的会话。
       同样地,如果将数据放到 SimpleDB 系统中,当数据流入数据中心时也会产生停顿。目前,Amazon 还没有确认这一点,但是据我所知,它将构成银河意识(galactic consciousness)的一部分,并改善 Runu 星球(距离 Betelgeuse 3 个秒差距,左转并继续逼近,直到您看见它)上类似于蛞蝓的生物的生活。因此每次使用 SimpleDB 时,您就会为 Runu 星球上类似蛞蝓生物做了一件好事。并且您会认为这篇文章不像是教学文章。
       无论如何,在提高银河意识的同时,您的数据是实时的,并且可以通过查询查看。但是如果在所有一切发生之前发出查询,那么不仅不会提高银河意识,还会得到旧的数据。因此,并不像处理典型的 DB2 数据库(在其中保持数据,并且 ACID 确保事务正如数据库声称的那样被提交)那么简单。使用 SimpleDB 和其他 “非常一致的” 数据库,您不得不忍受这种不确定性。
       重点在于,更新照片没有这么简单。我们希望使用一个复制 URL 拒绝上传,但这并不是总是可行的。假设 Alice 上传 http://horsey.com/wilbur.png,与此同时,Bob 也上传了 http://horsey.com/wilbur.png。如果 Alice 的上传排在前面而 Bob 没有注意到它,那么 Bob 的上传将覆盖前者。那么我们该怎么做呢?
       首先,您会问,这样有什么危害?用户可能会有些不方便,但是这没什么大不了。而且,同时上传的机率也很小。是的,我们希望用户满意,如果我们过分追求质量的话,也许这个问题将纠缠我们终生。
       我们不会把这种痛苦带到坟墓中,相反,我们将针对照片修改表设计,如清单 2 所示:
清单 2. 修改后的表设计
share_photos:
"random-string10"
{ url: "http://developer.amazonwebservices.com/connect/images/amazon/logo_aws.gif",
user: "ted", name: "Amazon Logo"}
"random-string11"
{ url: "http://images.share.lifelogs.com/funny.jpg", user: "bob", name: "Funny Picture",
s3bucket: "images.share.lifelogs.com" }
|
       您到目前为止所看到的 random-strings
将为 UUID。它不够完美,但是至少在 URL 相同的情况下照片不会出现冲突。但是等等……照片评论会怎样?很简单;我们只修改外键,如清单 3 所示:
清单 3. 修改外键
share_comments:
"random-string3"
{
image_id: "random-string10",
comment: "No it doesnt",
reply_to: "random-string2",
posted_when: "2009-03-01T20:00:01+05"
}
|
       现在我们需要注意到 share_photos
中有多个条目使用相同的 URL,但是除此以外系统一切正常。
       我们并不是向您展示人为修改的最终版表,而是将所有表遍历一遍。这使我们可以展示的 SimpleDB 灵活性并展示最佳状态的敏捷开发:投入、测试、优化、重复。但是,我们并没有对每一件事情 进行计划,而仅仅做好通往下一个阶段的计划:
- 我们任何时刻都没有忘记全局性。
- 在制定或修改架构决策时,不会像对待特定于任务的决策那样随意。
       那么照片上传很简单,是吗?只需要使用给定的 URL、照片名和用户名向 SimpleDB 添加一个新条目。S3 bucket 是可选的。这可以通过 PutAttributes
调用完成。
       修改照片也很简单,但是目前我们只修改名称。这也是通过 PutAttributes
完成的。
添加和修改评论
       参考前面小节有关 share_comments
表的内容。非常简单:添加一个评论需要评论文本、照片 ID、父评论 ID(可选)和一个用户名。目前为止,修改评论意味着只能对评论文本进行修改。
独立脚本
       我包含了一个独立的 Perl 脚本(simple_go.pl;可以从本文结束部分的下载小节获得)来执行前面列出的任务(添加和修改照片、添加和修改评论)。它不会创建域,因此您需要通过外部方式创建 SimpleDB domains share_photos.share.lifelogs.com 和 share_comments.share.lifelogs.com。这可以通过任何 SimpleDB 管理工具完成。注意 --domain
switch 将修改 share.lifelogs.com 以获得完整的域名(存储在 $full_domain
)。
       脚本使用 CPAN Data::UUID 模块生成新的惟一标识符。
       脚本在处理错误方面有些随意,在任何可能的情况下都会选择 die()
。这种方式非常懒惰并且叫人看不起,因此您不应选择这样做,除非您在撰写文章并向人们展示写文章的程序员有多么糟糕。
       最后一个任务是提交 SELECT
语句并删除一个项。我将展示如何实现它们,因为这些任务很简单,并且您稍后就将用到。
要列出照片,需要像清单 4 一样调用脚本:
清单 4. 列出照片
./simple_go.pl -l -i --ak=accesskey --sk=secretkey
|
       注意:确保您运行脚本的机器不会被其他人使用。他们会查看进程列表并获知您的 Amazon 秘密密匙。类似地,如果位于一个保存历史的 shell 中,您的秘密密匙将出现在您的历史文件中。更好的方法是传递一个文件名并从该文件中获得密码,但是为了保持简单性,我没有实现这种方法。
要列出评论:
清单 5. 列出评论
./simple_go.pl -l -c --ak=accesskey --sk=secretkey
|
       目前为止,一切都很简单。脚本在内部调用 $service->select()
方法,解析结果,使用 show_list()
输出数据。执行所有操作的前提就是键只有一个值(注意我们在 put()
方法中指定 Replace=true
),因此这并不是一个通用 SimpleDB 脚本。
       为什么不选择通用脚本?我们不需要它。现在让我们使用一个简单的解决方法。如果需要多个值,可以稍后采用脚本或编写些新代码。这个脚本是创建网站数据库结构的基础。
       不要在实际站点中尝试使用这个脚本(“我将仅调用 system()
并让错误进入到日志文件中”)。是的,它包括几百行代码并且可以工作,但是必须毫不犹豫地扔掉所有原型,这样才可以编写一个真正的程序。这个脚本也不应该有例外,即使我们已经有点喜欢上它以一个空格为缩进的散漫(或者说 “有创意”)布局。
回到脚本中来。创建一个新照片非常简单(bucket 是可选的):
清单 6. 创建一个新照片
./simple_go.pl -i --ak=accesskey --sk=secretkey -u ted --url="any url"
--name="any name you like" --bucket=mybucket
|
编辑照片名字(-l -i
提供了一个 ID 25EC17B8-0F6B-11DE-A1A1-944E07F9DEC1)。这将创建一个具有惟一 UUID 的照片:
清单 7. 创建具有惟一 UUID 的新照片
./simple_go.pl -i --ak=accesskey --sk=secretkey --name="new name"
--id=25EC17B8-0F6B-11DE-A1A1-944E07F9DEC1
|
类似地,创建评论也很简单(user
和 refcommentid
是可选的):
清单 8. 创建一个评论
./simple_go.pl -c --ak=accesskey --sk=secretkey -u ted --refimageid="any image ID"
--text="the text" --refcommentid=any comment ID
|
同样,评论 ID 将是一个惟一 UUID。编辑评论的文本也执行类似的操作:(-l -c
提供了 ID 4BE2EA0A-0F6B-11DE-976B-A542FC6BD07C):
清单 9. 创建具有惟一 UUID 的评论
./simple_go.pl -c --ak=accesskey --sk=secretkey --text="the text"
--id=4BE2EA0A-0F6B-11DE-976B-A542FC6BD07C
|
最后,像下面这样删除照片或评论:
清单 10. 删除图像和评论
./simple_go.pl --delete -i --ak=accesskey --sk=secretkey
--id=25EC17B8-0F6B-11DE-A1A1-944E07F9DEC1
./simple_go.pl --delete -c --ak=accesskey --sk=secretkey
--id=4BE2EA0A-0F6B-11DE-976B-A542FC6BD07C
|
结束语
       本期文章展示了如何在一个 SimpleDB 数据库中创建、编辑和删除照片和评论,SimpleDB 数据构成了我们所构建的照片共享站点的基础。
       我们确立了松散的模式,并实现一个工具来添加、列举、修改和删除照片和评论。我们使用 UUID 作为照片和评论的主键,防止出现两个用户同时上传相同的照片 URL。
       我们还确定将对每个键使用一个单一值,因为目前的模式不需要使用多个值,并且我们希望保持简单性。这一缺陷可能需要在以后解决,但是目前我们将继续保持下去。
       在第 4 部分中,您将看到如何将所有功能集成到 mod_perl
网站中。
本文的样例脚本,simple_go.zip:
temp_10030721452849.zip