开始之前
本教程将向您介绍实时 web,并详细介绍之所以要构建实时 web 应用程序的几个原因。您将学到一些技术,这些技术将帮助您创建响应及时、持续更新的 web 应用程序,这些应用程序既能保护服务器资源,又能提供良好的用户体验。
关于本教程
实时 web 应用程序允许用户在信息发布时及时接收通知,无需手动检查原始源获取更新。通过 Twitter 和 Friendfeed 这样的社交通知工具,Google Wave 这样的基于 web 的协作工具,以及 Meebo 这样的基于 web 的聊天客户端,实时 web 应用程序逐渐流行起来。
Extensible Messaging and Presence Protocol (XMPP) 是一组基于 XML 的技术,用于实时应用程序,定义为持续更新以响应新数据或更改数据的联网应用程序。它最初作为一个框架研发,以支持企业环境内的实时消息传递和联机状态(presence)应用程序。
在本教程中,您将构建一个简单的工具 Pingstream,它在 RSS 提要更新发布时使用它们持续更新自身(参见
- 了解 XMPP 为何特别适合 web 应用程序;
- 了解 XMPP 通信的组件;
- 安装和配置 Openfire XMPP 服务器;
- 使用 PHP 和 XMPPHP 库连接到 XMPP 服务器;
- 检查并通过 XMPP 传输 RSS 提要中的新项目;
- 借助 Bidirectional-streams Over Synchronous HTTP (BOSH),使用 Strophe 和 jQuery 通过 HTTP 连接到 XMPP 服务器;
- 在 web 页面中显示 XMPP 通知。
先决条件
本教程假定您比较熟悉使用 PHP 开发 web 应用程序,但也会涉及一些高级编程方法。您还应该拥有一定的 HTML 和 JavaScript 经验。拥有 jQuery JavaScript 框架经验可能会有所帮助。但本教程不要求熟悉 XMPP 或类似的技术。
要跟随本教程操作,必须安装和运行以下服务器软件:
- PHP 5.2 或更高版本
- Apache HTTP Server
- MySQL
在本教程中,您还将下载和安装以下软件和库:
- Openfire
- jQuery
- Strophe
- XMPPHP
- Last RSS
您可能会发现 MySQL 服务器工具 phpMyAdmin 能够派上用场。如果您使用一台桌面机器来本地测试您的实时 web 应用程序,您可能会发现 XAMPP 对于管理一个测试 web 服务器基础设施的安装和运行很有用。
参见 参考资料 获取所有这些工具下载的链接。
实时 web 简介
在本小节中,您将了解什么是实时 web 应用程序,您为何可能需要构建这样的应用程序,以及它们与典型的现代 web 应用程序模型的区别何在。
接受输入并提供反馈
应用程序是帮助用户执行任务的专门软件,其特征是:从用户或其他源接收输入,然后提供可读的输出。应用程序也可能动态响应 — 以可视或编程方式 — 自动接收的输入数据中的变化。例如,当包含特定关键字的新闻出现在一个新闻监控应用程序连接到的新闻专线中时,该应用程序可能会通知用户。
由于 web 的起源是作为一个文档服务平台,因此它没有针对应用程序优化。HTML 非常适合表示和超链接文本内容,但不适合创建动态界面。web 应用程序能够接收和响应用户输入,这要归功于 PHP 这样的服务器端脚本语言与表单和 JavaScript 这样的 web 输入技术的结合。但是,要创建自动 更新的界面,您必须克服一些障碍。这些障碍比较难以克服,因为没有任何 web 技术在研发时考虑到这个功能。相比之下(以新闻监控为例),桌名软件无需刷新其界面就能够将通知发送给用户;桌面软件可以持续更新自身。相反,web 则局限于基于页面的模型。
然而,基于 web 的实时应用程序还是可以实现的,而且它们的好处显而易见。这样的应用程序包括企业聊天工具、联网的实时文档协作工具、以及搜索界面,它们能够及时显示新发布的内容。
面向健壮的应用程序开发的 Web 技术
通常,web 应用程序通过使用 Asynchronous JavaScript and XML (Ajax) 工具来模拟持续更新的界面。在这个模型中,应用程序的 web 页面中包含 JavaScript,它在后台反复请求一个服务器回拨。尽管 Ajax 应用程序的响应性在很多情况下都够用,但这种技术还是有一些缺陷。
Ajax 不能容忍不稳定的 Internet 连接:一次临时掉线可能会导致整个界面失败。它在服务器负载方面也效率低下。假设您的后台 Ajax 轮询函数每 10 秒检查一次服务器。每一次都将建立一个新的 HTTP 连接,包括初始化服务请求所需的资源,即使没有新数据可以向用户显示。结果是应用程序不必要地使用过多的处理器时间和带宽。
基于 XML 的技术向 web 应用程序提供了巨大的优势。XML 解析器现在是大多数环境的一个标准组成部分;无需其他软件就能支持以适当的格式读写数据。XML 是自我描述型语言;使用它的文档不需要外部架构。最后,正如 web 是独立于平台的一样,XML 作为一种技术支持在不同平台间互操作。因此,开发人员可以将精力集中于特定于他们的应用程序的逻辑。
web 基于 HTML、Cascading Style Sheets (CSS) 和 JavaScript 等可互操作的免费开源标准。如果针对 web 上的实时通信的新标准出现,那么该标准也应是免费、开源和可互操作的。基于 XML 的 XMPP 满足这些标准。在本教程中,您将使用 XMPP 来构建一个客户端库,它通过标准方法(比如一个 web hook)来接收输入,并将适当的数据实时中继到用户。
XMPP 简介
本小节将简要介绍 XMPP,它的起源,以及为何它是一个适合实时 web 通信的协议。您将检查 XMPP 通信设置的组件,并查看展示这些组件如何使用的示例。
Web 标准和 XMPP
XMPP 是一组基于 XML 的技术,用于实时应用程序。最初,XMPP 作为一个框架开发,目标是支持企业环境内的即时消息传递和联机状态应用程序。当时的即时消息传递网络是私有的,非常不适合企业使用。例如,AOL Instant Messenger 不能针对公司内的安全通信进行调整。尽管存在一些商业解决方案,但它们固定的特性集通常不能进行调整,以满足组织的特殊需求。XMPP,当时名为 Jabber,允许组织构建自己的定制工具来促进实时通信,并允许安装现成的第三方解决方案。
XMPP 是一个分散型通信网络,这意味着,只要网络基础设施允许,任何 XMPP 用户都可以向其他任何 XMPP 用户传递消息。多个 XMPP 服务器也可以通过一个专门的 “服务器-服务器” 协议相互通信,提供了创建分散型社交网络和协作框架的有趣可能性,但这个主题已超出了本教程的讨论范围。
顾名思义,XMPP 可用于满足广泛的、对时间敏感的特性要求。实际上,Google Wave,一个大型多用户协作环境,将 XMPP 作为其联合协议的基础。尽管 XMPP 的出现是为了满足 “个人-个人” 即时消息传递的要求,但它完全不必局限于此任务。
XMPP 通信的结构
要促进消息传递,每个 XMPP 客户端用户必须拥有一个全局惟一标识符。基于历史原因,这些标识符称为 Jabber IDs,或称为 JIDs。鉴于这个协议的分布式特征,重要的是 JID 应包含联系用户所需的所有信息:不存在将用户链接到他们连接到的服务器的中央知识库。JID 的结构类似于电子邮件地址(但不要求 JID 同时也是有效的电子邮件收件人)。
客户端和服务器节点,我将它们统称为 XMPP 实体,都拥有 JIDs。SomeCorp 公司的员工 John Doe 可能拥有 JIDJohn.Doe@somecorp.com
。这里,somecorp.com
是 SomeCorp 公司的 XMPP 服务器的地址,John.Doe
是 John Doe 的用户名。
JIDs 还拥有连接到它们的资源。这允许在一个 XMPP 实体标识符之外进一步处理细粒度;例如,尽管上面的示例总体上能够表示 John Doe,但 John.Doe@somecorp.com/Work
可以用于将数据发送到与他的工作相关的工具。
这些资源可以采用任意用户定义的名称,一个 XMPP 实体可以拥有任意数量的资源。除了可以是上下文依赖的外,它们还可以绑定到设备、工具或工作站。对于您的 Pingstream 示例,web 站点的每个访问者都将作为同一个用户登录 XMPP 服务器,但他们拥有不同的资源。
通信类别
使用 XMPP 的实时消息传递系统包含三大通信类别:
- 消息传递,其中数据在有关各方之间传输;
- 联机状态,它允许用户广播其在线状态和可用性;
- 信息/查询请求,它允许 XMPP 实体发起请求并从另一个实体接收响应。
这些类别是互补的。例如,如果用户或实体离线(尽管在许多用例中,理想的状态是服务器在用户返回之前一直持有用户的消息),则没有将数据发送给用户或发起一个实体的信息/查询请求的点。这些消息中的每一条都将通过一个完整的 XML 节 传递 — XML 节是以 XML 表达的独立信息项。
这三种类型的 XMPP 节都拥有以下公共属性:
from
:源 XMPP 实体的 JID;to
:目标接收者的 JID;id
:这次对话的可选标识符;type
:节的可选子类型;xml:lang
:如果内容是人们可读的,则为消息语言的描述。
基于 XMPP 的数据传输发生在一些 XML 流上,默认在端口 5222 上操作。这些 XML 流实际上是两个完整的 XML 文档,每个文档对应一个通信方向。一旦会话建立,stream
元素将打开。这个元素将封装整个通信文档。然后,一些节被注入这个文档的第二层。最后,一旦通信结束,stream
元素将关闭,形成一个完整的文档。
例如,清单 1 展示了一个 stream
元素,它建立了从客户端到服务器的通信。
清单 1. 建立从客户端到服务器的通信的 stream 标记
<stream:stream from="[server]" id="[unique ID over conversation]" xmlns="jabber:client" xmlns:stream="http://etherx.jabber.org/streams" version="1.0"> |
消息
一旦通信建立,客户端就能使用 message
元素将消息发送到另一个用户,message 元素包含以下任意子元素:
subject
:一个可读的字符串,表示消息主题。body
:一个可读的字符串,表示消息体。如果每个消息体标记都拥有一个不同的xml:lang
值,那么可以包含多个消息体标记。(xml:lang
是惟一可能的属性。)thread
:一个惟一标识符,表示一个消息线程。客户端软件可以使用这个子元素将相关消息串联在一起。
但是,消息也可以非常简单,如 清单 2 所示:
清单 2. 样例消息
<message from="sendinguser@somedomain" to="recipient@somedomain" xml:lang='en'> <body> Body of message </body> </message> |
对于提供实时 web 界面而言