清单 28. 对单个 FOM 对象计算 XPath 表达式



级别: 中级

James Snell (jasnell@us.ibm.com), 软件工程师, IBM

2007 年 1 月 26 日

本系列的前几期介绍了 Atom 发布协议(Publishing Protocol),描述了在实际应用程序中使用的各种方式。本文将举例说明如何使用一个新的开放源代码项目 Abdera 实现支持 Atom 的应用程序,该项目目前处于 Apache Software Foundation 孵化阶段。

本文假设您已经阅读过 Atom Format 规范,对连锁有一定了解(请参阅 参考资料)。所有的例子都是用 Java 编写,并提供了包含全部代码例子的 Eclipse 项目供您下载。

开始

首先要保证安装了 Apache Abdera 的当前版本。源代码可以从 Apache Subversion 资料库(http://svn.apache.org/repos/asf/incubator/abdera/java/branches/0.2.0-incubating/)下载。要检索源代码,需要安装 subversion 客户机并使用下面的命令:

> svn co http://svn.apache.org/repos/asf/incubator/abdera/java/branches/0.2.0-incubating/

下载源代码镜像之后,就可以使用 Ant 1.6.5 或更高版本构建 Abdera 了。

> cd trunk
> ant -f build/build.xml dist

构建完成之后,编译后的 jar 和依赖关系文件存放在新建的 “dist” 目录中。运行本文中的例子需要在类路径中包含下列 jar。并非所有例子都需要依赖关系列表中的全部 jar:

表 1. 运行示例需要的 jar
Abdera (dist) 依赖关系(dist/lib)
  • abdera.client.0.2.0-incubating.jar
  • abdera.core.0.2.0-incubating.jar
  • abdera.parser.0.2.0-incubating.jar
  • abdera.protocol.0.2.0-incubating.jar
  • axiom-api-1.0.jar
  • axiom-impl-1.0.jar
  • commons-codec-1.3.jar
  • commons-httpclient-3.0.1.jar
  • commons-logging-1.0.4.jar
  • geronimo-activation_1.0.2_spec-1.1.jar
  • log4j-1.2.12.jar
  • stax-1.1.2-dev.jar
  • stax-api-1.0.jar
  • jaxen-1.1-beta-7.jar

起步

Abdera 项目包含一组独立的模块,下表按字母顺序列出了这些模块。当然,最重要也是最常用的模块是 core、dependencies 和 parser:

表 2. Apache Abdera 项目模块
模块 说明 依赖关系
build 整个项目的 Ant 构建 Apache Ant 1.6.5+
client Atom Publishing Protocol Client core, parser, protocol, commons-codec-1.3.jar, commons-httpclient-3.0.1.jar
core Feed Object Model 接口 Java Activation Framework, commons-logging-1.0.4.jar, log4j-1.2.12.jar
dependencies 所有模块共用的依赖关系
extensions 提要语法和协议扩展 core, protocol, json-1.0.jar
parser StAX 和基于 Axiom 的默认 Feed Object Model (FOM)实现 core, axiom-api-1.0.jar, axiom-impl-1.0.jar, stax-1.1.2-dev.jar, stax-api-1.0.jar, jaxen-1.1-beta-7.jar
protocol Common Atom Publishing Protocol 代码 core, parser
security XML Digital Signature 和 Encryption 支持 core, parser, xmlsec-1.3.0.jar, Bouncy Castle JCE 实现
server Atom Publishing Protocol 服务器实现 core, parser, protocol, Servlet API

下表列出了 core 模块中的包,该模块定义了 Abdera 所称的 “Feed Object Model”,它是按照 Atom Syndication Format 规范建模的一组接口,用于解析、创建和操作 Atom 文档。

表 3. Abdera “core” 模块中的包
说明
org.apache.abdera 主包,包含一个 “Abdera” 对象
org.apache.abdera.factory 定义了创建新的 Feed Object Model 对象实例的 Factory 接口
org.apache.abdera.filter 定义了在解析过程中筛选 ATom 文档的接口
org.apache.abdera.model 定义了处理 Atom Feed 和 Entry 文档的基本接口,该模型还包括处理 Atom Publishing Protocol Service 和 Category 文档的支持。
org.apache.abdera.parser 定义了从 XML 文档创建 Feed Object Model 新的对象实例的 Parser 接口
org.apache.abdera.util 提供了各种工具类,主要针对希望扩展或替换 Abdera 默认解析器和工厂实现的开发人员
org.apache.abdera.writer 定义了用于序列化 Feed Object Model 对象实例的 Write 接口
org.apache.abdera.xpath 定义了使用 XPath 导航 Feed Object Model 的接口

下载并构建 Abdera 源代码后,如果需要的话,可以花点时间通过浏览构建过程中生成的 Javadoc 文档(位于创建的 $ABDERA_HOME/dist/docs 目录下)来熟悉这种 API。

创建记录和提要

Abdera Feed Object Model 的两个主要功能是简化 Atom 提要和记录文档的生产与消费。

创建 Atom 文档首先要获得 org.apache.abdera.factory.Factory 的实例并设置提要或记录的属性。清单 1 显示了一个简单 Atom Entry Document 的创建:

清单 1. 创建简单的 Atom Entry 文档

级别: 中级

James Snell (jasnell@us.ibm.com), 软件工程师, IBM

2007 年 1 月 26 日

本系列文章的 上一期 简要介绍了 Atom 发布协议(Atom Publishing Protocol,APP)。本文继续介绍该协议,举例说明如何用它与一些已经部署好的应用程序交互。

与以前一样,本文假设您了解使用 Atom 1.0 Syndication Format 的内容连锁,并对 HTTP 有基本的了解。本文的例子使用 Java 1.5 编写,并使用了 Apache Software Foundation 开发的 Apache Abdera 开放源码 Atom 实现的最新预览版本。

准备开始

首先要保证安装了 Apache Abdera 的当前版本。源代码可以从 Apache Subversion 资料库 http://svn.apache.org/repos/asf/incubator/abdera/java/trunk 下载。要检索源代码,需要安装 subversion 客户机并使用下面的命令:

> svn co http://svn.apache.org/repos/asf/incubator/abdera/java/trunk

下载源代码的镜像之后就可以用 Ant version 1.6.5 或更高版本构建 Abdera 了。

> cd trunk
> ant -f build/build.xml dist

构建完成后,编译后的 jar 和 dependency 放在新建的 dist 目录中。要运行这些例子,需要在类路径中添加下列 jar:

表 1. 运行例子需要的 jar
Abdera (dist) Dependency (dist/lib)
  • abdera.client.0.2.0-incubating-SNAPSHOT.jar
  • abdera.core.0.2.0-incubating-SNAPSHOT.jar
  • abdera.parser.0.2.0-incubating-SNAPSHOT.jar
  • abdera.protocol.0.2.0-incubating-SNAPSHOT.jar
  • axiom-api-1.0.jar
  • axiom-impl-1.0.jar
  • commons-codec-1.3.jar
  • commons-httpclient-3.0.1.jar
  • commons-logging-1.0.4.jar
  • geronimo-activation_1.0.2_spec-1.1.jar
  • geronimo-javamail_1.3.1_spec-1.1.jar
  • log4j-1.2.12.jar
  • stax-1.1.2-dev.jar
  • stax-api-1.0.jar

Weblog

根据 IETF Atom Publishing Format and Protocol 工作组的章程,Atom 发布协议(Atom Publishing Protocol)的设计目标主要用于发布和管理 weblog 记录。毫不奇怪,随后很多 blogging 软件提供商如 Google、SixApart 和 Roller 已经开始初步支持该协议。

Google 的 Blogger Beta

2006 年 8 月初,Google 宣布将对其提供的网络日记服务进行期待已久的升级。服务新增加的一个特性就是支持使用 Atom 发布协议创建和编辑公告。

创建公告非常简单。首先需要知道新记录所要发送到的 Atom 集合的 URL。对于 Blogger 来说,Atom 提要用于连锁 blog 的内容组成 Atom 发布协议集合供提要阅读器和聚合器使用。因此要确定集合的 URI,只要查看 weblog 主页头部的替代链接即可。

清单 1. Blogger 主页的头部

介绍协议的基本操作



级别: 中级

James Snell (jasnell@us.ibm.com), 软件工程师, IBM

2007 年 1 月 23 日

Atom 发布协议(Publishing Protocol)是一种重要的内容发布和管理新标准。本文从高层概述了该协议及其基本操作和能力。

在过去几年中,Web 内容连锁技术在互联网上以及防火墙后面变得越来越重要。2005 年 7 月,互联网工程任务组(IETF)的 Atom Publishing Format and Protocol 工作组(简称为 “atompub”)发布了两个标准规范中的第一个,目标是提供 “用于表示的提要格式和用于编辑 Web 资源的协议,如 Weblog、在线日志、Wiki 以及类似的内容。” 此后,通常被称为 Atom 1.0 的 Atom 连锁格式(Syndication Format),部署到了成千上万个网站上,并得到了市场上所有主要连锁平台的支持。今天,仅仅过了一年之后,两个规范中的第二个也将宣告完成,即 Atom 发布协议(Publishing Protocol)。

Atom 发布协议是一种基于 HTTP 的用于创建和编辑 Web 资源的方法。它基本上围绕着这样一种观念设计,即利用 HTTP 协议提供的基本操作(如 GET、PUT 和 DELETE)传输表示 blog 项、博客、wiki 页面、日程记录等内容的 Atom 1.0 Feed and Entry 文档实例。

后面的讨论将带领您初步了解该协议的基本操作。本文假定读者非常熟悉使用 Atom 1.0 Syndication Format 的内容连锁,并对 HTTP 有基本的了解。阅读这篇概述文章的同时,建议您在手头上有 Atom 1.0(RFC 4287)和 HTTP 1.1(RFC 2616)规范的副本,作为涉及到的各种元素和方法的交叉参考。如果不熟悉 Atom 格式,建议您看一看我去年为 developerWorks 撰写的文章,“An overview of the Atom Syndication Format”(请参阅 参考资料)。

高层概述

Atom 发布协议的核心是可编辑资源集合 的概念,用 Atom 1.0 Feed and Entry 文档表示。集合 有一个惟一的 URI。向这个 URI 发出 HTTP GET 请求将返回 Atom Feed Document。为了在提要中创建新记录,客户机需要向集合的 URI 发送 HTTP POST 请求。这些新创建的记录将分配惟一的编辑 URI。要修改这些记录,客户机只需要从集合中检索资源,修改后再放回去。要从提要中删除记录,只需要向适当的编辑 URI 发送 HTTP DELETE 请求。所有操作都是用简单的 HTTP 请求完成的,通常不会比简单的文本编辑器和命令提示更难。

图 1. 使用简单的 HTTP 方法发布和管理内容的 Atom 发布协议
Atom 发布协议概述
清单 1. 使用开放源码的 curl HTTP 客户机和 Atom 发布端点交互
curl -s -X POST --data-binary @entry.xml http://example.org/atom/entries
curl -s -X GET http://example.org/atom/entries/1
curl -s -X PUT --data-binary @entry.xml http://example.org/atom/entries/1
curl -s -X DELETE http://example.org/atom/entries/1
 

发现可用的集合

使用任何支持 APP 的服务第一步都是确定有哪些集合可用以及这些集合可能包含什么类型的资源。Atom 协议规范定义了一种被称为服务文档的 XML 格式,客户机可用它确定一个端点。

检索服务文档需要向服务文档的 URI 发送 HTTP GET 请求。

清单 2. 从服务器上检索 APP 服务文档
GET /servicedocument HTTP/1.1
Host: example.org
 

服务器应该用一个服务文档来响应这个请求,其中列出了客户机可用的集合,如 清单 3 所示。

清单 3. 简单的 APP 服务文档
HTTP/1.1 200 OK
Date: ...
Content-Type: application/atomserv+xml; charset=utf-8
Content-Length: nnn


 
 My Weblog
 
 Entries
 entry
 
 
 Photos
 image/*
 
 

 

服务文档中列出的每个集合元素都代表一个容器,容器中可以存储一些内容。文档中的 workspace 元素仅用于对相关的集合进行逻辑分组。比方说,在给定的日志服务上一个用户可能有多个帐户,分别为 blog 记录、上传的文件、书签等提供不同的容器。每个服务可以用服务文档中单独的 workspace 表示。

collection 元素提供了集合的地址(href 属性)和可以添加到集合中的内容类型的列表(用 accept 元素中的 mime 类型标识)。清单 3 中所示的例子有两个集合,一个只接受 Atom Entry Documents,另一个只接受图像文件(如 PNG、GIF、JPEG 等)。

向集合中添加记录

得到集合的地址后,我们使用 HTTP POST 方法增加新的资源,如 清单 4 所示。

清单 4. 向 APP 集合中增加新记录
POST /blog/entries HTTP/1.1
Host: www.example.org
Content-Type: application/atom+xml; charset=utf-8
Content-Length: nnn



 Atom-Powered Robots Run Amok
 
 urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a
 2003-12-13T18:30:02Z
 James
 Some text.

 

清单 4 的例子中向 http://example.org/blog/entries 的集合中添加了一个 Atom Entry Document。集合的 URI 从 清单 3 所示的服务文档中取得。要注意,发出的记录必须是有效的,就是说必须有 id、author 和 updated 元素,虽然很多 APP 应用程序选择忽略或改写客户机提供的值。

对 POST 请求的成功响应(如 清单 5 所示)为客户机提供了两个重要信息:请求的状态(HTTP 响应代码)和新建资源的地址(包含在 Location 头部中)。

清单 5. 成功 POST 操作的响应
HTTP/1.1 201 Created
Date: nnnn
Content-Type: application/atom+xml; charset=utf-8
Content-Location: /blog/entries/1
Location: /blog/entries/1
ETag: "/blog/entries/1?1"
Last-Modified: Sat, 12 Aug 2006 13:40:03 GMT


 tag:example.org,2006:/blog/entries/1
 Atom-Powered Robots Run Amok
 
 
 2006-08-12T13:40:03Z
 James M Snell
 Some text.

 

因为一些 APP 服务器可能改变记录的主要内容(比如 id、author 和 updated 元素),服务器返回的响应可能包含实际添加到集合中的记录的副本。这样客户机就可以使发送给服务器的记录和实际创建的记录保持一致。

列出集合中的记录

将记录添加到集合中之后,客户机可以通过向集合的 URI 发送 GET 请求检索其成员资源的列表,如 清单 6 所示。

清单 6. 检索集合中的提要
GET /blog/entries HTTP/1.1
Host: example.org
 

该请求的响应应该是一个 Atom Feed Document,其中每个记录表示集合中的一个成员资源,如 清单 7 所示。

清单 7. APP 集合的 Atom Feed Document
HTTP/1.1 200 OK
Date: ...
Content-Type: application/atom+xml; charset=utf-8
Content-Length: nnn
ETag: "/blog/entries?132"
Last-Modified: Sat, 12 Aug 2006 13:40:03 GMT


 http://example.org/blog/entries
 My Blog Entries
 2006-08-12T13:40:03Z
 
 
 
 tag:example.org,2006:/blog/entries/1
 Atom-Powered Robots Run Amok
 
 
 2006-08-12T13:40:03Z
 James
 Some text.
 
 
 ...
 
 ...

 

可以将返回的提要看作是集合上的某种索引,和文件系统中的 “dir” 或 “ls” 命令很相似。

记录本身按照记录 updated 元素的值排列,最近更新的记录排在最前面。此外,记录列表可能需要多个 Atom Feed Document,使用所谓的分页链接串在一起,如 清单 8 所示。

清单 8. 使用分页链接的提要片段

 
 
 ...
 

分页链接提供了一种方法,将可能很大的集合成员资源列表划分成较小的、更容易管理的子集。

编辑记录

要编辑记录,客户机首先需要检索可编辑的表示。为此可以向成员的 Edit URI 发送一个 GRT 请求,如 清单 9 所示。这基本上相当于在本地文本编辑器中编辑文档时首先要打开文档。

清单 9. 检索资源的可编辑表示
GET /blog/entries/1 HTTP/1.1
Host: example.org
 

对该请求的响应应该是一个如 清单 10 所示的 Atom Entry Document。

清单 10. 表示可编辑资源的 Atom Entry Document
HTTP/1.1 200 OK 
Date: nnn
Content-Type: application/atom+xml; charset=utf-8
Content-Length: nnn
ETag: "/blog/entries/1?1"
Last-Modified: Sat, 12 Aug 2006 13:40:03 GMT



 tag:example.org,2006:/blog/entries/1
 Atom-Powered Robots Run Amok
 
 
 2006-08-12T13:40:03Z
 James
 Some text.

 

得到可编辑的表示之后,客户机可以对选择的记录做任何(合理的)修改,然后向记录的 Edit URI 发出 PUT 请求更新(清单 11)。

清单 11. 把修改后的 Atom 记录返回服务器
PUT /blog/entries/1 HTTP/1.1
Host: example.org
Content-Type: application/atom+xml; charset=utf-8
Content-Length: nnnn
If-Match: "/blog/entries/1?1"
If-Unmodified-Since: Sat, 12 Aug 2006 13:40:03 GMT



 tag:example.org,2006:/blog/entries/1
 Atom-Powered Robots Run Crazy
 
 
 2006-08-12T13:40:03Z
 John
 Some different text.

 

要注意,PUT 请求中使用了 If-Match 和 If-Unmodified-Since 头。虽然不是必需的,但使用这些头可以让 APP 实现避免覆盖其他客户机对成员资源所做的修改。如果这些条件都不满足,服务器可以拒绝请求并通知客户机尝试修改的资源可能存在冲突。如果条件满足,服务器就会认为客户机提交的修改是可以接受的,并返回适当的成功响应。

删除记录

如果客户机要从集合中删除资源,需要向 Edit URI 发送 DELETE 请求,如 清单 12 所示。

清单 12. 从集合中删除资源
DELETE /blog/entries/1 HTTP/1.1
Host: example.org
 

删除成功后,该记录将不再出现在集合的 Atom 提要中,也不再能编辑。

向集合中增加媒体资源

也可以向 APP 集合中增加任何媒体资源,如图片、文档、音频记录等。这些项被 APP 规范称为 media-link 项,因为当把这些资源增加到集合中时,服务器将创建一个 Atom 记录文档,链接到客户机提交的媒体资源。

虽然最初的设计目的仅仅是为了让 Weblog 作者能够上传需要的媒体对象并包含在文档中,但由于 Atom 发布协议支持任何媒体资源,因此非常适合各种应用程序,包括:

  • 博客
  • 视频日志
  • 图库
  • Wiki 和语境应用程序
  • 文档管理
  • XML 资料库
  • 软件分发
  • 产品应用程序(如 Office Suites)
  • 等等

创建 media-link 记录,客户机需要向集合 URI 发送 POST 请求,但包含的不是 Atom Entry Document 而是要链接的媒体资源的表示(清单 13)。

清单 13. 向 APP 应用程序增加二进制图像文件
POST /blog/photos HTTP/1.1
Host: example.org
Content-Type: image/png
Content-Length: nnnn
Slug: A trip to the beach

{binary image data}
 

如果集合可以保存客户机发送的媒体资源类型,就会保存它,并创建一个 Atom Entry Document 链接到该资源,如 清单 14 所示。请求中包含的 Slug 头是 Atom Publishing Specification 新增加的 HTTP Entity Header,用于在创建和管理资源时把一个简单的名字和具有多种用途的成员资源关联起来。比方说,服务器可以在创建成员资源的 URI 或者设置 Atom Entry Document 中 title 元素的值时使用 slug 的值。Slug 头可以在发布 Atom 记录或者媒体资源时使用,但多用于后者。

清单 14. 在媒体发布响应中创建的 media-link 记录
HTTP/1.1 201 Created
Date: nnnn
Content-Location: /blog/photos/a_trip_to_the_beach
Location: /blog/photos/a_trip_to_the_beach
Content-Type: application/atom+xml; charset=utf-8
Content-Length: nnnn
Slug: A trip to the beach
ETag: "/blog/photos/a_trip_to_the_beach?1"
Last-Modified: Sat, Aug 12 2006 14:11:04 GMT



 tag:example.org,2006:/blog/photos/a_trip_to_the_beach
 A trip to the beach
 
 
 2006-08-12T14:11:04Z
 James
 A trip to the beach
 

 

media-link 记录必须包含 content 元素,它的 src 属性提供了新建媒体资源的 URI。这个 URI 对于媒体资源的公共引用很方便。使用单独的 edit-media 链接可以确定用于更新媒体资源的 URI。

编辑媒体资源

编辑添加到集合中的媒体资源和编辑 Atom 记录基本相同。首先通过对 eit-media 链接所指定的 URI 发送 GET 请求来检索资源的可编辑版本(清单 15)。

清单 15. 检索媒体资源的可编辑表示
GET /blog/photos/a_trip_to_the_beach?media HTTP/1.1
Host: example.org
 

返回可编辑的表示之后,客户机可以做需要的任何修改,然后向 edit-media URI 发出 PUT 请求(清单 16)。

清单 16. 修改媒体资源
PUT /blog/photos/a_trip_to_the_beach?media HTTP/1.1
Host: example.org
Content-Type: image/png
Content-Length: nnn

{new binary image data}
 

保护集合

虽然 Atom 发布协议不要求实现使用身份验证,但最好这样做以便防止有恶意的客户机创建和修改集合成员。至少,实现应该能够使用 HTTP Basic 身份验证和 TLS/SSL 连接。但实际上,APP 客户机很可能遇到各种形式的身份验证机制。但无论采用什么形式的身份验证,服务器都应该利用标准 HTTP 风格的挑战来确定所选身份验证的类型。

比方说,如果服务器从客户机收到未经授权的请求,服务器应该返回包含 WWW-Authenticate 头的 401 Unauthorized 响应,如 清单 17 所示。

清单 17. 未授权请求的响应
HTTP/1.1 401 Unauthorized
Date: nnn
WWW-Authenticate: Basic realm="my blog"
 

然后,客户机可以使用适当的 Authorization 头重新发出请求。

清单 18. 授权的请求
POST /entries/blog HTTP/1.1
Host: example.org:443
Authorization: Basic SmFtZXM6bm90IG15IHJlYWwgcGFzc3dvcmQgOi0p
...
 

使用 APP

到目前为止,我讨论了 Atom 发布协议的基本操作,并通过例子示范了所有核心功能。不过,还没有讨论可以将 Atom 发布协议付诸应用的各种方法。在本系列的下一期文章中,我将介绍被认为最适合采用该协议的几种应用。其中包括一些明显的应用,如 Weblog、社区书签和照像簿类型的应用;也包括一些不那么明显的用法,比如日程表、联系人管理、文档和媒体内容资料库、数据库管理、语境应用程序,甚至面向服务体系结构。

此外,我们还将讨论如何利用 Apache Abdera 开放源码的 Atom(目前在 Apache Software Foundation 下孵化)用 Java 实现 Atom 发布客户机和服务器,最后再讨论创建支持 APP 的应用程序服务。

]]>

根据动网的论坛无限级的分类,特开发了PHP版的无限级的分类.

即然是PHP,数据表当然是 MYSQL:在应用之前,先在mysql中建立数据表.b_mtype.其中的字段包括:typeid,typename,parentid,paretnstr,rootid,child,orders.

具体PHP程序如下:

转载请保留出处:俊麟 Michael’s blog (http://www.toplee.com/blog/?p=71)
Trackback Url : http://www.toplee.com/blog/wp-trackback.php?p=71

  我在CERNET做过拨号接入平台的搭建,而后在Yahoo&3721从事过搜索引擎前端开发,又在MOP处理过大型社区猫扑大杂烩的架构升级等工作,同时自己接触和开发过不少大中型网站的模块,因此在大型网站应对高负载和并发的解决方案上有一些积累和经验,可以和大家一起探讨一下。


  一个小型的网站,比如个人网站,可以使用最简单的html静态页面就实现了,配合一些图片达到美化效果,所有的页面均存放在一个目录下,这样的网站对系统架构、性能的要求都很简单,随着互联网业务的不断丰富,网站相关的技术经过这些年的发展,已经细分到很细的方方面面,尤其对于大型网站来说,所采用的技术更是涉及面非常广,从硬件到软件、编程语言、数据库、WebServer、防火墙等各个领域都有了很高的要求,已经不是原来简单的html静态网站所能比拟的。

  大型网站,比如门户网站。在面对大量用户访问、高并发请求方面,基本的解决方案集中在这样几个环节:使用高性能的服务器、高性能的数据库、高效率的编程语言、还有高性能的Web容器。但是除了这几个方面,还没法根本解决大型网站面临的高负载和高并发问题。

  上面提供的几个解决思路在一定程度上也意味着更大的投入,并且这样的解决思路具备瓶颈,没有很好的扩展性,下面我从低成本、高性能和高扩张性的角度来说说我的一些经验。

1、HTML静态化
  其实大家都知道,效率最高、消耗最小的就是纯静态化的html页面,所以我们尽可能使我们的网站上的页面采用静态页面来实现,这个最简单的方法其实也是最有效的方法。但是对于大量内容并且频繁更新的网站,我们无法全部手动去挨个实现,于是出现了我们常见的信息发布系统CMS,像我们常访问的各个门户站点的新闻频道,甚至他们的其他频道,都是通过信息发布系统来管理和实现的,信息发布系统可以实现最简单的信息录入自动生成静态页面,还能具备频道管理、权限管理、自动抓取等功能,对于一个大型网站来说,拥有一套高效、可管理的CMS是必不可少的。

  除了门户和信息发布类型的网站,对于交互性要求很高的社区类型网站来说,尽可能的静态化也是提高性能的必要手段,将社区内的帖子、文章进行实时的静态化,有更新的时候再重新静态化也是大量使用的策略,像Mop的大杂烩就是使用了这样的策略,网易社区等也是如此。目前很多博客也都实现了静态化,我使用的这个Blog程序WordPress还没有静态化,所以如果面对高负载访问,www.toplee.com一定不能承受 :)

  同时,html静态化也是某些缓存策略使用的手段,对于系统中频繁使用数据库查询但是内容更新很小的应用,可以考虑使用html静态化来实现,比如论坛中论坛的公用设置信息,这些信息目前的主流论坛都可以进行后台管理并且存储再数据库中,这些信息其实大量被前台程序调用,但是更新频率很小,可以考虑将这部分内容进行后台更新的时候进行静态化,这样避免了大量的数据库访问请求。

  在进行html静态化的时候可以使用一种折中的方法,就是前端使用动态实现,在一定的策略下进行定时静态化和定时判断调用,这个能实现很多灵活性的操作,我开发的台球网站故人居(www.8zone.cn)就是使用了这样的方法,我通过设定一些html静态化的时间间隔来对动态网站内容进行缓存,达到分担大部分的压力到静态页面上,可以应用于中小型网站的架构上。故人居网站的地址:http://www.8zone.cn,顺便提一下,有喜欢台球的朋友多多支持我这个免费网站:)

2、图片服务器分离
  大家知道,对于Web服务器来说,不管是Apache、IIS还是其他容器,图片是最消耗资源的,于是我们有必要将图片与页面进行分离,这是基本上大型网站都会采用的策略,他们都有独立的图片服务器,甚至很多台图片服务器。这样的架构可以降低提供页面访问请求的服务器系统压力,并且可以保证系统不会因为图片问题而崩溃。

  在应用服务器和图片服务器上,可以进行不同的配置优化,比如Apache在配置ContentType的时候可以尽量少支持,尽可能少的LoadModule,保证更高的系统消耗和执行效率。

  我的台球网站故人居8zone.cn也使用了图片服务器架构上的分离,目前是仅仅是架构上分离,物理上没有分离,由于没有钱买更多的服务器:),大家可以看到故人居上的图片连接都是类似img.9tmd.com或者img1.9tmd.com的URL。

  另外,在处理静态页面或者图片、js等访问方面,可以考虑使用lighttpd代替Apache,它提供了更轻量级和更高效的处理能力。

3、数据库集群和库表散列
  大型网站都有复杂的应用,这些应用必须使用数据库,那么在面对大量访问的时候,数据库的瓶颈很快就能显现出来,这时一台数据库将很快无法满足应用,于是我们需要使用数据库集群或者库表散列。

  在数据库集群方面,很多数据库都有自己的解决方案,Oracle、Sybase等都有很好的方案,常用的MySQL提供的Master/Slave也是类似的方案,您使用了什么样的DB,就参考相应的解决方案来实施即可。

  上面提到的数据库集群由于在架构、成本、扩张性方面都会受到所采用DB类型的限制,于是我们需要从应用程序的角度来考虑改善系统架构,库表散列是常用并且最有效的解决方案。我们在应用程序中安装业务和应用或者功能模块将数据库进行分离,不同的模块对应不同的数据库或者表,再按照一定的策略对某个页面或者功能进行更小的数据库散列,比如用户表,按照用户ID进行表散列,这样就能够低成本的提升系统的性能并且有很好的扩展性。sohu的论坛就是采用了这样的架构,将论坛的用户、设置、帖子等信息进行数据库分离,然后对帖子、用户按照板块和ID进行散列数据库和表,最终可以在配置文件中进行简单的配置便能让系统随时增加一台低成本的数据库进来补充系统性能。

4、缓存
  缓存一词搞技术的都接触过,很多地方用到缓存。网站架构和网站开发中的缓存也是非常重要。这里先讲述最基本的两种缓存。高级和分布式的缓存在后面讲述。

  架构方面的缓存,对Apache比较熟悉的人都能知道Apache提供了自己的mod_proxy缓存模块,也可以使用外加的Squid进行缓存,这两种方式均可以有效的提高Apache的访问响应能力。

  网站程序开发方面的缓存,Linux上提供的Memcached是常用的缓存方案,不少web编程语言都提供memcache访问接口,php、perl、c和java都有,可以在web开发中使用,可以实时或者Cron的把数据、对象等内容进行缓存,策略非常灵活。一些大型社区使用了这样的架构。

  另外,在使用web语言开发的时候,各种语言基本都有自己的缓存模块和方法,PHP有Pear的Cache模块和eAccelerator加速和Cache模块,还要知名的Apc、XCache(国人开发的,支持!)php缓存模块,Java就更多了,.net不是很熟悉,相信也肯定有。

5、镜像
  镜像是大型网站常采用的提高性能和数据安全性的方式,镜像的技术可以解决不同网络接入商和地域带来的用户访问速度差异,比如ChinaNet和EduNet之间的差异就促使了很多网站在教育网内搭建镜像站点,数据进行定时更新或者实时更新。在镜像的细节技术方面,这里不阐述太深,有很多专业的现成的解决架构和产品可选。也有廉价的通过软件实现的思路,比如Linux上的rsync等工具。

6、负载均衡
  负载均衡将是大型网站解决高负荷访问和大量并发请求采用的终极解决办法。

  负载均衡技术发展了多年,有很多专业的服务提供商和产品可以选择,我个人接触过一些解决方法,其中有两个架构可以给大家做参考。另外有关初级的负载均衡DNS轮循和较专业的CDN架构就不多说了。

6.1 硬件四层交换
  第四层交换使用第三层和第四层信息包的报头信息,根据应用区间识别业务流,将整个区间段的业务流分配到合适的应用服务器进行处理。 第四层交换功能就象是虚IP,指向物理服务器。它传输的业务服从的协议多种多样,有HTTP、FTP、NFS、Telnet或其他协议。这些业务在物理服务器基础上,需要复杂的载量平衡算法。在IP世界,业务类型由终端TCP或UDP端口地址来决定,在第四层交换中的应用区间则由源端和终端IP地址、TCP和UDP端口共同决定。

  在硬件四层交换产品领域,有一些知名的产品可以选择,比如Alteon、F5等,这些产品很昂贵,但是物有所值,能够提供非常优秀的性能和很灵活的管理能力。Yahoo中国当初接近2000台服务器使用了三四台Alteon就搞定了。

6.2 软件四层交换
  大家知道了硬件四层交换机的原理后,基于OSI模型来实现的软件四层交换也就应运而生,这样的解决方案实现的原理一致,不过性能稍差。但是满足一定量的压力还是游刃有余的,有人说软件实现方式其实更灵活,处理能力完全看你配置的熟悉能力。

  软件四层交换我们可以使用Linux上常用的LVS来解决,LVS就是Linux Virtual Server,他提供了基于心跳线heartbeat的实时灾难应对解决方案,提高系统的鲁棒性,同时可供了灵活的虚拟VIP配置和管理功能,可以同时满足多种应用需求,这对于分布式的系统来说必不可少。

  一个典型的使用负载均衡的策略就是,在软件或者硬件四层交换的基础上搭建squid集群,这种思路在很多大型网站包括搜索引擎上被采用,这样的架构低成本、高性能还有很强的扩张性,随时往架构里面增减节点都非常容易。这样的架构我准备空了专门详细整理一下和大家探讨。

总结:
  对于大型网站来说,前面提到的每个方法可能都会被同时使用到,Michael这里介绍得比较浅显,具体实现过程中很多细节还需要大家慢慢熟悉和体会,有时一个很小的squid参数或者apache参数设置,对于系统性能的影响就会很大,希望大家一起讨论,达到抛砖引玉之效。

  转载请保留出处:俊麟 Michael’s blog (http://www.toplee.com/blog/?p=71)

]]>

phpmore第七期(memcache) 在phpmore杂志的第七期《memcache文章的简介》,此文章可以说是此篇文章的摘抄及总结。

  1. 用户通过浏览器访问Web服务器LAMP系统的情景描述
    首先用户请求被发送到服务器,通过tcp协议访问到Apache进程,Apache从文件系统中加载PHP代码后,进行解析,并运行其命令,可能的操作是查询数据库得到数据,然后生成HTML返回给浏览器。
  2. 为什么要使用memcache?
    在Web系统中,最容易产生瓶径的就是PHP查询数据库并返回结果的部份,常见的解决办法是除了对SQL查询语句进行优化,对数据库表添加必要的索引外,就是将数据库的查询缓存起来。它将有效的降低数据的压力。
  3. memcache简介?
    memcache是一高效、快速的分布式内存对象缓存系统,主要用于加速WEB动态应用程序
  4. memcache的安装?
    首先是下载 memcached 了,目前最新版本是 1.1.12 ,直接从官方网站(http://www.danga.com/memcached/download.bml)即可下载到memcached-1.1.12.tar.gz 。除此之外, memcached 用到了 libevent ,我下载的是libevent-1.1a.tar.gz。接下来是分别将 libevent-1.1a.tar.gz 和 memcached-1.1.12.tar.gz 解开包、编译、安装安装完成之后,memcached 应该在 /usr/bin/memcached
  5. 运行memcache守护程序
    运行 memcached 守护程序很简单,只需一个命令行即可,不需要修改任何配置文件
    /usr/bin/memcached -d -m 128 -l 192.168.1.1 -p 11211 -u httpd
    参数解释:
    -d 以守护程序(daemon)方式运行 memcached;
    -m 设置 memcached 可以使用的内存大小,单位为 M;
    -l 设置监听的 IP 地址,如果是本机的话,通常可以不设置此参数;
    -p 设置监听的端口,默认为 11211,所以也可以不设置此参数;
    -u 指定用户,如果当前为 root 的话,需要使用此参数指定用户。
    当然,还有其它参数可以用,man memcached 一下就可以看到了。
  6. memcache工作原理?
    首先 memcached 是以守护程序方式运行于一个或多个服务器中,随时接受客户端的连接操作,客户端可以由各种语言编写,目前已知的客户端 API 包括 Perl/PHP/Python/Ruby/Java/C#/C 等等。PHP 等客户端在与 memcached 服务建立连接之后,接下来的事情就是存取对象了,每个被存取的对象都有一个唯一的标识符 key,存取操作均通过这个 key 进行,保存到 memcached 中的对象实际上是放置内存中的,并不是保存在 cache 文件中的,这也是为什么 memcached 能够如此高效快速的原因。注意,这些对象并不是持久的,服务停止之后,里边的数据就会丢失。
  7. 如何使PHP作为memcached客户端?
    有两种方法可以使 PHP 作为 memcached 客户端,调用 memcached 的服务进行对象存取操作。第一种,PHP 有一个叫做 memcache 的扩展,Linux 下编译时需要带上 –enable-memcache[=DIR]选项,Window 下则在 php.ini 中去掉 php_memcache.dll 前边的注释符,使其可用。除此之外, 还有一种方法,可以避开扩展、重新编译所带来的麻烦,那就是直接使用php-memcached-client。本文选用第二种方式,虽然效率会比扩展库稍差一些,但问题不大。
  8. PHP memcached应用示例
    首先 下载 memcached-client.php,在下载了 memcached-client.php 之后,就可以通过这个文件中的类“memcached”对 memcached 服务进行操作了。其实代码调用非常简单,主要会用到的方法有add()、get()、replace() 和 delete(),方法说明如下:
    – PHPMORE
    add ($key, $val, $exp = 0)
    往 memcached 中写入对象,$key 是对象的唯一标识符,$val 是写入的对象数据,$exp 为过期时间,单位为秒,默认为不限时间;
    get ($key)
    从 memcached 中获取对象数据,通过对象的唯一标识符 $key 获取;
    replace ($key, $value, $exp=0)
    使用 $value 替换 memcached 中标识符为 $key 的对象内容,参数与 add()方法一样,只有 $key 对象存在的情况下才会起作用;
    delete ($key, $time = 0)
    删除 memcached 中标识符为 $key 的对象,$time 为可选参数,表示删除之前需要等待多长时间。 下面是一段简单的测试代码,代码中对标识符为 ‘mykey’ 的对象数据进行存取操作:
    // 包含 memcached 类文件
    require_once(‘memcached-client.php’);
    // 选项设置
    $options = array(
    ’servers’ => array(‘192.168.1.1:11211′), //memcached 服务的地址、
    端口,可用多个数组元素表示多个 memcached 服务
    ‘debug’ => true, //是否打开 debug
    ‘compress_threshold’ => 10240, //超过多少字节的数据时进行压缩
    ‘persistant’ => false //是否使用持久连接
    );
    // 创建 memcached 对象实例
    $mc = new memcached($options);
    // 设置此脚本使用的唯一标识符
    $key = ‘mykey’;
    // 往 memcached 中写入对象
    $mc->add($key, ’some random strings’);
    $val = $mc->get($key);
    echo “n”.str_pad(‘$mc->add() ’, 60, ‘_’).“n”;
    var_dump($val);
    // 替换已写入的对象数据值
    $mc->replace($key, array(’some’=>‘haha’, ‘array’=>‘xxx’));
    $val = $mc->get($key);
    echo “n”.str_pad(‘$mc->replace() ’, 60, ‘_’).“n”;
    var_dump($val);
    // 删除 memcached 中的对象
    $mc->delete($key);
    $val = $mc->get($key);
    echo “n”.str_pad(‘$mc->delete() ’, 60, ‘_’).“n”;
    var_dump($val); 在实际的应用中,通常会把数据库查询的结果集保存到memcache中,下次访问时直接从memcache中获取,而不在做数据库查询操作,这样就可以很大程度上减轻数据库的负担。通常会将sql语句MD5($sql)之后的值作为唯一的键值。
  9. 利用memcache来缓存数据库查询结果集的示例
    – PHPMORE
    $sql = ‘SELECT * FROM users’;
    $key = md5($sql); //memcached 对象标识符
    if ( !($datas = $mc->get($key)) ) {
    // 在 memcached 中未获取到缓存数据,则使用数据库查询获取记录集。
    echo “n”.str_pad(‘Read datas from MySQL.’, 60, ‘_’).“n”;
    $conn = mysql_connect(‘localhost’, ‘test’, ‘test’);
    mysql_select_db(‘test’);
    $result = mysql_query($sql);
    while ($row = mysql_fetch_object($result))
    $datas[] = $row;
    // 将数据库中获取到的结果集数据保存到 memcached 中,以供下次访问时
    使用。
    $mc->add($key, $datas);
    } else {
    echo “n”.str_pad(‘Read datas from memcached.’, 60, ‘_’).“n”;
    }
    var_dump($datas);
  10. 相关资源
    memcached 官方网站

使用基于文件的Session存取瓶颈可能都是在磁盘IO操作上,所以对付小数据量的Session没有问题,但是如果碰到大数据量的Sesstion,那么可能无法胜任,现在利用Memcache来保存Session数据,直接通过内存的方式,效率自然能够提高不少,并且如果结合PHP的Memcache扩展,能够支持分布式的Memcache服务器,那么这个性能就能够提到更高,负载更多更复杂的应用。

说明:以下代码基于Memcache来保存Session数据,客户端必须安装有PHP的Memcache扩展,否则无法运行,同时本代码没有经过严格测试,只是作为学习代码。

php
//===========================================
// 程序:Memcache-Based Session Class
// 功能:基于Memcache存储的 Session 功能类
// 作者: heiyeluren
// 博客: http://blog.csdn.net/heiyeshuwu
// 时间: 2006-12-23
//===========================================

/**
* 类名: FileSession Class
* 功能: 自主实现基于Memcache存储的 Session 功能
* 描述: 这个类就是实现Session的功能, 基本上是通过设置客户端的Cookie来保存SessionID,
*