在 前一篇文章 中,我向读者概要介绍了新兴的 Atom 1.0 Syndication Format,并在较高层次上评价了该标准的技术强度。在讨论的末尾,我简要提及了 Atom 的可扩展性机制,该机制旨在提供一种非常灵活、健壮的方法,用于扩展格式的核心功能。自那以后,出现了大量重要而有用的利用该框架的扩展。在本文中,我将探索几个这样的扩展,讨论它们的目的和用途,并提供很多如何使用它们的例子。
本文假设您至少基本了解 Atom 1.0 syndication format 和内容联合的一般知识。在您阅读本文时,我建议您在手头保留 Atom 1.0 规范的一份副本,作为所讨论的各种元素的交叉引用(参见 参考资料)。
在 IBM 内部网上,部署了一个博客环境,拥有 13,000 多名注册用户,包含了全球 1,700 多个活跃的 weblogs。用户一天 24 小时都会向该站点发送帖子。尽管每个单独的 blog 都有其自己的 RSS 和 Atom feed,但是大量的帖子和 blogs 使得它难以在宏观上跟踪内部网上正在讨论什么。要解决这个难题,在该环境中加入了一个 Dashboard,所以用户可以查看(通过浏览器和 feed 阅读器)所有按日期和时间排序的 blog 帖子和注释。该解决方案虽然有用,但也存在明显的问题:因为聚合的 feed 呈现一个滑动的窗口,通过该窗口,读者只能查看到最近的帖子和注释,用户如果不经常检查,或者在夜间关了机,等他们第二天回来再检查 feed 时,通常会遗漏他们不在时张贴的一些东西。
该问题的一个解决方案是增加显示在 feed 中的条目的总数。例如,如果帖子比率是每小时 100 帖,并且一般的用户是每小时检查一次 feed,那么生成 feed 时让它包含最近的 100 个帖子。尽管有些用户会错过一些帖子,但是一般的情况是,大部分用户可以看到大部分条目。该解决方案的困难在于,这是一种不可伸缩的方法。首先,当发帖率成倍增长到 200 帖每小时、400 帖每小时,甚至 1000 帖每小时的时候,该怎么办?您还应该继续增大滑动窗口以确保用户不错过帖子吗?其次,没有可靠的方式能够预测将来多久推出一个 feed。有些用户将会每隔 5 分钟推出一个 feed;有些是隔 30 分钟;有些是隔 1 小时;还有的是一周只推出一个 feed。您可以根据主要用户在过去情况的统计分析来决定滑动窗口的大小,但是肯定有一些用户会错过帖子。或者您应该产生多个 feed,一个显示过去 5 分钟内的所有帖子,一个显示过去 30 分钟内的所有帖子,一个显示过去 1 个小时内的所有帖子,等等。但是这样做会为那些必须计算出要订阅哪个 feed 的用户带来复杂性。
另一方面,Atom Publishing and Protocol Working Group 成员 Mark Nottingham 提议了一个好得多的解决方案,在他的题为“Feed History: Enabling Incremental Syndication”的 IETF Internet-Draft 中有详细描述(参见 参考资料)。Mark 的解决方案提议一个模型,允许 feed 阅读器通过翻阅多个增量 feed 文档而重建 feed 的历史。
当 feed 阅读器访问一个被标记为是增量 的 feed 文档时,阅读器可以看到,feed 的内容只包含 feed 的内容的一部分表示。要重建 feed 的完整内容,阅读器必须遍历一系列包含其余条目的先前 feed 文档(参见 图 1)。
图 1. 使用多个 feed 文档重建 feed 历史
清单 1 中的 fh:incremental 元素有一个值为 true,这表示所讨论的 feed 文档可能 包含一个 fh:prev
元素,它指向另一个包含附加 feed 条目的文档。fh:prev 元素创建本质上是一个链接列表的条目。列表的末尾由一个不包含 fh:prev 元素的 feed 文档来指示。
清单 1. 使用 Feed History 扩展
<feed xmlns="http://www.w3.org/2005/Atom"
xmlns:fh="http://purl.org/syndication/history/1.0">
<title>My Incremental Feed</title>
<link href="http://www.example.com" />
<link rel="self" href="http://www.example.com/feed.xml" />
<updated>2005-12-12T12:00:00Z</updated>
<author><name>James Snell</name></author>
<id>tag:example.com,2005:/feed</id>
<fh:incremental>true</fh:incremental>
<fh:prev>http://www.example.com/prevfeed.xml</fh:prev>
...
</feed>
|
一个具有值 false 的 fh:incremental 元素(如 清单 2 中所示)指出,所讨论的 feed 文档是 feed 内容的完整表示。此类 feed 的例子包括诸如前 10 个列表的东西,或者表示类似于 Odeo.com(用于 podcasts)和 Netflix.com(用于电影)发布的条目队列的 feed。
清单 2. 一个非增量 feed
<feed xmlns="http://www.w3.org/2005/Atom"
xmlns:fh="http://purl.org/syndication/history/1.0">
<title>My Movie Queue</title>
<link href="http://www.example.com/movies"/>
<link rel="self" href="http://www.example.com/movies/feed" />
<updated>2005-12-12T12:00:00Z</updated>
<author><name>James Snell</name></author>
<id>tag:example.com,2005:movies</id>
<fh:incremental>false</fh:incremental>
<entry>
<title>Hitchhiker's Guide to the Galaxy</title>
<link href="..." />
...
</entry>
<entry>
<title>Charlie Chaplin - City Lights</title>
<link href="..." />
...
</entry>
<entry>
<title>Buster Keaton - College</title>
<link href="..." />
...
</entry>
</feed>
|
fh:incremental 元素通过为 feed 阅读器提供一种方法,用于显式地了解一个单独的 feed 文档何时表示滑动窗口,何时表示条目的完整集合,解决了联合中的一个基本问题。fh:incremental 和 fh:prev 元素的结合解决了 feed 的完整历史的重建问题。
最后,如果我不指出这一点就是我的失职,即 Mark 的 Feed History 扩展已经被设计成独立于特定的联合格式而工作,并且与所有版本的 RSS 以及 Atom 1.0 兼容。为了说明这一点,Mark 在他的个人 weblogs 的 RSS 1.0 feed 中实现了 Feed History 支持(参见 参考资料)。
在基于 RSS 的联合的领域,一个广泛接受的实践是以与创建日期相反的顺序来显式地对 feed 中的条目排序。当然,基于一些特定的应用需求,该规则也有许多例外。例如,如果您在 Netflix.com 处有一个帐户,那么在表示您想要观看的电影队列的 RSS feed 中的条目是按优先级排序的,而不是按电影被添加到队列的顺序排序。在队列中移动一个电影会改变其在 RSS feed 中的位置。
使情况更复杂的是,许多此类 feed 是非增量的,也就是说,feed 文档包含所有可用的条目。另一方面,许多 feed 阅读器将把此类 feed 完全看成任何其他种类的增量联合内容,并且将以由阅读器接收新条目的顺序所确定的表示顺序来显示 feed 的完整历史。如果我重新排序 feed 中的条目,或者甚至希望从 feed 删除条目以便让 feed 阅读器不再显示这些条目,那么 RSS 无法允许我指出这些意图。
Atom 1.0 Syndication Format 显式地指出,显示在 feed 中的条目的顺序没有任何意义,意味着 feed 阅读器可以自由地以它们认为适合的任何顺序来表示和处理条目。为了允许 feed 发布者指出 feed 条目的顺序很重要,一个新的 Feed Rank 扩展已经作为 IETF Internet-Draft 被提议出来。
Feed Rank 机制在 feed 条目中引入了数值等级值。
每个数值等级值都与一个被绑定到等级方案(ranking scheme) 的逻辑等级域(ranking domain)相关联。等级域提供一种方法,可以组合那些与其他条目具有相对等级的条目。等级方案定义了,为了分类和组织条目集合,域中的数值等级值是如何被解释的。清单 3 中的例子说明了等级域与等级方案的关联以及一组数值等级值。
清单 3. 一个具有等级条目的 feed
<feed xmlns="http://www.w3.org/2005/Atom"
xmlns:r="http://purl.org/syndication/index/1.0">
<title>My Ordered Feed</title>
<link href="http://www.example.com" />
<link rel="self" href="http://www.example.com/feed.xml" />
<updated>2005-12-12T12:00:00Z</updated>
<author><name>James Snell</name></author>
<id>tag:example.com,2005:/feed</id>
<r:ranking-scheme
domain="urn:example.com"
label="Example Five Star Ranking Scheme"
significance="ascending"
precision="1"
max-value="5"
min-value="0" />
<entry>
<title>Entry</title>
<link href="http://www.example.com/1</link>
<updated>2005-12-12T12:00:00Z</updated>
<content>My entry</content>
<id>tag:example.com,2005:/feed</id>
<r:rank domain="urn:example.com">5.0</r:rank>
</entry>
<entry>
<title>Entry</title>
<link href="http://www.example.com/1</link>
<updated>2005-12-12T12:00:00Z</updated>
<content>My entry</content>
<id>tag:example.com,2005:/feed</id>
<r:rank domain="urn:example.com">3.5</r:rank>
</entry>
</feed>
|
r:ranking-scheme 元素用于定义和描述等级方案。该元素的属性指定数值等级的最大和最小值(max-value、min-value)、用于应用这些等级的 precision、significance 的顺序,以及可用于引用方案的人类可读的 label。最重要的是,r:ranking-scheme 元素的 domain 属性将方案绑定到特定的等级域。
等级域由惟一的国际化资源标识符(Internationalized Resource Identifiers,IRI)识别。r:ranking-scheme 和 r:rank 元素上的域属性的值是识别域的 IRI。如果没有 domain 属性,那么它的值默认为由包含的 Atom feed 的 id 元素所指定的 IRI 值。如果 domain 属性的值是一个 "same document reference",那么该值默认为包含文档的基本 URI。
清单 4 中的每个等级方案的等级域标识符是:
-
Ranking Scheme 1:默认为
/atom:feed/atom:id(tag:example.com,2005:/feed) - Ranking Scheme 2:默认为文档基本 URI (http://www.example.com/myrankedfeed)
-
Ranking Scheme 3:
urn:example.com
清单 4. 具有多个等级域的 http://www.example.com/myrankedfeed
<feed xmlns="http://www.w3.org/2005/Atom"
xmlns:r="http://purl.org/syndication/index/1.0">
<title>My Ordered Feed</title>
<link href="http://www.example.com" />
<link rel="self" href="http://www.example.com/feed.xml" />
<updated>2005-12-12T12:00:00Z</updated>
<author><name>James Snell</name></author>
<id>tag:example.com,2005:/feed</id>
<r:ranking-scheme
label="Ranking Scheme 1"
significance="ascending"
precision="1"
max-value="5"
min-value="0" />
<r:ranking-scheme
domain=""
label="Ranking Scheme 2"
significance="ascending"
precision="1"
max-value="5"
min-value="0" />
<r:ranking-scheme
domain="urn:example.com"
label="Ranking Scheme 3"
significance="ascending"
precision="1"
max-value="5"
min-value="0" />
<entry>
<title>Entry</title>
<link href="http://www.example.com/1</link>
<updated>2005-12-12T12:00:00Z</updated>
<content>My entry</content>
<id>tag:example.com,2005:/feed</id>
<r:rank>5.0</r:rank>
<r:rank domain="">5.0</r:rank>
<r:rank domain="urn:example.com">5.0</r:rank>
</entry>
</feed>
|
对于诸如 Netflix.com 电影队列之类的应用程序,feed rank 在与 fh:incremental 元素结合时成了一个非常有用的工具。该元素由 feed history 扩展引入进来,如 清单 5 所示。
清单 5. 一个具有等级条目的电影队列 feed
<feed xmlns="http://www.w3.org/2005/Atom"
xmlns:fh="http://purl.org/syndication/history/1.0"
xmlns:r="http://purl.org/syndication/index/1.0">
<title>My Movie Queue</title>
<link href="http://www.example.com/movies"/>
<link rel="self" href="http://www.example.com/movies/feed" />
<updated>2005-12-12T12:00:00Z</updated>
<author><name>James Snell</name></author>
<id>tag:example.com,2005:movies</id>
<fh:incremental>false</fh:incremental>
<r:ranking-scheme
domain="http://www.example.com/movies/queue"
label="Queue"
significance="descending"
precision="0"
min-value="1" />
<r:ranking-scheme
domain="http://www.example.com/movies/ratings"
label="Ratings"
significance="ascending"
precision="1"
min-value="0"
max-value="5" />
<entry>
<title>Hitchhiker's Guide to the Galaxy</title>
<link href="..." />
<r:rank
domain="http://www.example.com/movies/queue">1</r:rank>
<r:rank
domain="http://www.example.com/movies/ratings">5.0</r:rank>
...
</entry>
<entry>
<title>Charlie Chaplin - City Lights</title>
<link href="..." />
<r:rank
domain="http://www.example.com/movies/queue">3</r:rank>
<r:rank
domain="http://www.example.com/movies/ratings">4.5</r:rank>
...
</entry>
<entry>
<title>Buster Keaton - College</title>
<link href="..." />
<r:rank
domain="http://www.example.com/movies/queue">2</r:rank>
<r:rank
domain="http://www.example.com/movies/ratings">3.5</r:rank>
...
</entry>
</feed>
|
消费者可以使用以下两种已定义的等级域之一来对这些条目分类:
- Domain: http://www.example.com/movies/queue
- Rank 1: Hitchhiker's Guide to the Galaxy
- Rank 2: Buster Keaton - College
- Rank 3: Charlie Chaplin - City Lights
- Domain: http://www.example.com/movies/ratings
- Rank 5.0: Hitchhiker's Guide to the Galaxy
- Rank 4.5: Charlie Chaplin - City Lights
- Rank 3.5: Buster Keaton - College
此外,因为该 feed 被标记为是非增量的,所以 feed 消费者可以看到,feed 包含所有的条目。
通常,发布的内容只想在特定时期内有效。例如,一个前 10 项列表可能只在它发布后的 10 天之内有效。或者,某个特定的文档可能会在给定的时期之后自动过期,就像 IETF Internet-Drafts 一样,它会在六个月后过期。为了解决这个问题,一个新的题为“Atom Metadata Expiration: Specifying Expiration Timestamps for Atom Feed and Entry metadata”的 IETF Internet Draft(参见 参考资料)提出了两个新的扩展元素,可用于指出一个确切的时间或者一个最长寿命,在这样的时间之后,feed 或条目包含的元数据将会过期。
例如,假设我想要上面描述的非增量且排好序的电影队列在其发布之后 10 天(864,000,000 毫秒)后过期。为此,我使用 expiration 扩展引入的 max-age 元素来指定,从由 feed 的 atom:updated 元素指定的当前时间算起,feed 的元数据应该被认为有效的总的毫秒数(参见 清单 6)。
清单 6. 一个其内容在 10 天后过期的 feed
<feed xmlns="http://www.w3.org/2005/Atom"
xmlns:fh="http://purl.org/syndication/history/1.0"
xmlns:fa="|http://purl.org/atompub/age/1.0|">
<title>My Movie Queue</title>
<link href="http://www.example.com/movies"/>
<link rel="self" href="http://www.example.com/movies/feed" />
<updated>2005-12-12T12:00:00Z</updated>
<author><name>James Snell</name></author>
<id>tag:example.com,2005:movies</id>
<fh:incremental>false</fh:incremental>
<fa:max-age>864000000</fa:max-age>
<entry>
<title>Hitchhiker's Guide to the Galaxy</title>
<link href="..." />
...
</entry>
<entry>
<title>Charlie Chaplin - City Lights</title>
<link href="..." />
...
</entry>
<entry>
<title>Buster Keaton - College</title>
<link href="..." />
...
</entry>
</feed>
|
指定最长寿命的另一种方法是,使用 expires 元素来指定当 feed 或条目被认为过期的确切时刻(参见 清单 7)。
清单 7. 一个其内容在 2005 年 12 月 22 日中午过期的 feed
<feed xmlns="http://www.w3.org/2005/Atom"
xmlns:fh="http://purl.org/syndication/history/1.0"
xmlns:fa="|http://purl.org/atompub/age/1.0|">
<title>My Movie Queue</title>
<link href="http://www.example.com/movies"/>
<link rel="self" href="http://www.example.com/movies/feed" />
<updated>2005-12-12T12:00:00Z</updated>
<author><name>James Snell</name></author>
<id>tag:example.com,2005:movies</id>
<fh:incremental>false</fh:incremental>
<fa:expires>2005-12-22T12:00:00Z</fa:expires>
<entry>
<title>Hitchhiker's Guide to the Galaxy</title>
<link href="..." />
...
</entry>
<entry>
<title>Charlie Chaplin - City Lights</title>
<link href="..." />
...
</entry>
<entry>
<title>Buster Keaton - College</title>
<link href="..." />
...
</entry>
</feed>
|
当 feed 阅读器访问一个已经过期的 Atom feed 或条目时,它会以某种方式向用户发出警告。
feed 或条目过期 的确切含义故意是含糊的,也就是说,当 feed 或条目过期时隐含的惟一断言是,包含在 feed 或条目中的信息元数据将不再有效或者可能发生了更改。要由每个单独的应用程序来定义适当处理有效的元素和过期的元素所需的任何进一步的语义需求。
还要注意,永远不要将 expires 和 max-age 元素用于缓存控制目的,也不要为何时下载新版本的 Atom 文档作出计划。相反,应该为此目的使用标准的 HTTP 缓存控制机制。
清单 8 演示了一个使用了目前所讨论的每个扩展的 feed。
清单 8. 一个综合的例子
<feed xmlns="http://www.w3.org/2005/Atom"
xmlns:fh="http://purl.org/syndication/history/1.0"
xmlns:fr="http://purl.org/syndication/index/1.0"
xmlns:fa="http://purl.org/atompub/age/1.0">
<title>My Movie Queue</title>
<link href="http://www.example.com/movies"/>
<link rel="self" href="http://www.example.com/movies/feed" />
<updated>2005-12-12T12:00:00Z</updated>
<author><name>James Snell</name></author>
<id>tag:example.com,2005:movies</id>
<fh:incremental>false</fh:incremental>
<fr:ranking-scheme
domain="http://www.example.com/movies/queue"
label="Queue"
significance="descending"
precision="0"
min-value="1" />
<fr:ranking-scheme
domain="http://www.example.com/movies/ratings"
label="Ratings"
significance="ascending"
precision="1"
min-value="0"
max-value="5" />
<fa:expires>2005-12-22T12:00:00Z</fa:expires>
<entry>
<title>Hitchhiker's Guide to the Galaxy</title>
<link href="..." />
<r:rank
domain="http://www.example.com/movies/queue">1</r:rank>
<r:rank
domain="http://www.example.com/movies/ratings">5.0</r:rank>
...
</entry>
<entry>
<title>Charlie Chaplin - City Lights</title>
<link href="..." />
<r:rank
domain="http://www.example.com/movies/queue">3</r:rank>
<r:rank
domain="http://www.example.com/movies/ratings">4.5</r:rank>
...
</entry>
<entry>
<title>Buster Keaton - College</title>
<link href="..." />
<r:rank
domain="http://www.example.com/movies/queue">2</r:rank>
<r:rank
domain="http://www.example.com/movies/ratings">3.5</r:rank>
...
</entry>
</feed>
|
在本系列的第 2 部分中,我将介绍另外三个扩展,它们用于:
- 将版权许可证与 feed 和条目相关联
- 控制链接的自动处理
- 联合线程的讨论
最后需要注意的一点是,应该将这里讨论的所有扩展都看成是未完工程,它们将随着 IETF Internet Standards 的进程而不断演变。大多数都相当稳定,但是如果您选择现在就实现其中的任何一个,那么您应该随着最后的细节的讨论和确定而在未来看到一些更改。
学习
- 您可以参阅本文在 developerWorks 全球站点上的 英文原文。
- “Atom 1.0 Syndication Format 概述”(developerWorks,2005 年 8 月):回顾 James Snell 的前一篇文章,其中讨论了 Atom 与其他联合格式相关的技术强度,并提供了几个说明这些强度的有说服力的用例。
-
developerWorks XML 专区:找到成百上千的更多 XML 资源。
- IBM Certified Developer in XML and Related Technologies:了解您如何才能通过认证。
获得产品和技术
- developerWorks content feeds:获得我们的内容的 RSS 或 Atom feed。

James Snell 是 IBM Emerging Technologies Toolkit 团队的一名成员。他过去几年的时间都花在新兴的 Web 服务技术和标准上,并且是 Atom 1.0 规范的贡献者。他在 http://www.ibm.com/developerworks/blogs/dw_blog.jspa?blog=351 处维护着一个关于新兴技术的 weblog。