月度归档:2008 年十一月

让php以脚本的方式运行

在linux环境下,有时不想在php文件前里加上命令php的字样,让其以独立的脚本运行.
下面是注意点:
1.让文件有可执行权限, chmod +x abc.php;
2.开头加上#!/usr/bin/php;
3.如果提示: bad interpreter: No such file or directory 之类,表明不是以unix格式换的行,使用dos2unix *.php命令强行改过.

Httpclient和htmlunit实现401授权认证[原]

http状态码的含义:

1**:请求收到,继续处理
2**:操作成功收到,分析、接受
3**:完成此请求必须进一步处理
4**:请求包含一个错误语法或不能完成
5**:服务器执行一个完全有效请求失败

100——客户必须继续发出请求
101——客户要求服务器根据请求转换HTTP协议版本

200——交易成功
201——提示知道新文件的URL
202——接受和处理、但处理未完成
203——返回信息不确定或不完整
204——请求收到,但返回信息为空
205——服务器完成了请求,用户代理必须复位当前已经浏览过的文件
206——服务器已经完成了部分用户的GET请求

300——请求的资源可在多处得到
301——删除请求数据
302——在其他地址发现了请求数据
303——建议客户访问其他URL或访问方式
304——客户端已经执行了GET,但文件未变化
305——请求的资源必须从服务器指定的地址得到
306——前一版本HTTP中使用的代码,现行版本中不再使用
307——申明请求的资源临时性删除

400——错误请求,如语法错误
401——请求授权失败
402——保留有效ChargeTo头响应
403——请求不允许
404——没有发现文件、查询或URl
405——用户在Request-Line字段定义的方法不允许
406——根据用户发送的Accept拖,请求资源不可访问
407——类似401,用户必须首先在代理服务器上得到授权
408——客户端没有在用户指定的饿时间内完成请求
409——对当前资源状态,请求不能完成
410——服务器上不再有此资源且无进一步的参考地址
411——服务器拒绝用户定义的Content-Length属性请求
412——一个或多个请求头字段在当前请求中错误
413——请求的资源大于服务器允许的大小
414——请求的资源URL长于服务器允许的长度
415——请求资源不支持请求项目格式
416——请求中包含Range请求头字段,在当前请求资源范围内没有range指示值,请求
也不包含If-Range请求头字段
417——服务器不满足请求Expect头字段指定的期望值,如果是代理服务器,可能是下
一级服务器不能满足请求

500——服务器产生内部错误
501——服务器不支持请求的函数
502——服务器暂时不可用,有时是为了防止发生系统过载
503——服务器过载或暂停维修
504——关口过载,服务器使用另一个关口或服务来响应用户,等待时间设定值较长
505——服务器不支持或拒绝支请求头中指定的HTTP版本

==========================================================

英文版:

100:Continue
101:Switching Protocols
102:Processing

200:OK
201:Created
202:Accepted
203:Non-Authoriative Information
204:No Content
205:Reset Content
206:Partial Content
207:Multi-Status

300:Multiple Choices
301:Moved Permanently
302:Found
303:See Other
304:Not Modified
305:Use Proxy
306:(Unused)
307:Temporary Redirect

400:Bad Request
401:Unauthorized
402:Payment Granted
403:Forbidden
404:File Not Found
405:Method Not Allowed
406:Not Acceptable
407:Proxy Authentication Required
408:Request Time-out
409:Conflict
410:Gone
411:Length Required
412:Precondition Failed
413:Request Entity Too Large
414:Request-URI Too Large
415:Unsupported Media Type
416:Requested range not satisfiable
417:Expectation Failed
422:Unprocessable Entity
423:Locked
424:Failed Dependency

500:Internal Server Error
501:Not Implemented
502:Bad Gateway
503:Service Unavailable
504:Gateway Timeout
505:HTTP Version Not Supported
507:Insufficient Storage

1.httpclient实现.
HttpClient client = new HttpClient();
GetMethod get = new GetMethod(“http://the.page.you.want.to.get”);
UsernamePasswordCredentials upc =
new UsernamePasswordCredentials(“username”, “password”);
client.getState().setCredentials(null, null, upc);
get.setDoAuthentication(true);
client.setConnectionTimeout(60000);
try{
client.executeMethod(get);
Header[] headers = get.getResponseHeaders();
System.out.println(get.getStatusCode());
for(int i=0; i<headers.length; i++){
System.out.println(headers[i].getName()+”=”+headers[i].getValue()+”\n”);
}
//              System.out.println(get.getResponseHeaders().toString());
System.out.println(get.getResponseBodyAsString());
}catch(Exception e){
e.printStackTrace();
}

2.htmlunit实现
WebClient webclient = new WebClient(BrowserVersion.FIREFOX_3);
try {
DefaultCredentialsProvider dcp = new DefaultCredentialsProvider();
dcp.addCredentials(“username”, “password”);
webclient.setCredentialsProvider(dcp);
HtmlPage page = (HtmlPage) webclient
.getPage(“http://the.page.you.want.to.get”);
System.out.println(page.asXml());
} catch (Exception e) {
e.printStackTrace();
}

模式和原则

设计模式介绍
“模式:每一个模式描述了一个在我们周围不断重复发生的问题,以及该问题的解决方案的核心。”
这是关于模式最经典的定义,作者是建筑大师Christopher Alexander。如果是第一次看到这句话,多数人会觉得有些抽象难懂。其实“模式”两个字只是一个代号,就像我的英文名字叫Justin,如果我改叫Tom也没什么问题,只是我更喜欢Justin这个名字,所以从Christopher开始,有了“模式”这个词,人们也都把关于“重复发生的问题的描述和解决办法”统称为模式。
“模式”这个词是不局限于软件开发行业的,它几乎无处不在,它其实就是一种经验的积累,就象大多数人的教育经历都是从小学到初中再到高中再到大学,这也是一种模式,是中国的教育模式;现在越来越火的出国热,也是另一种模式:海外留学模式。因为GOF的《设计模式:可复用面向对象软件的基础》一书描述的23种经典设计模式,奠定了模式在软件行业的地位,从此人们提到“设计模式”就是默指“面向对象设计模式”,但是如前文所述,模式绝对不局限于软件行业,即使在软件行业,也不局限于GOF描述的23种设计模式,例如最著名的Martin Flower的《企业架构模式》,还有我们常用的MVC、IOC等架构模式。
因为模式是一种经验的积累和总结,所以通过模式,我们可以站在巨人的肩膀上去思考问题、解决问题,熟练使用设计模式可以提高我们的工作效率,改善产品质量,最终带来经济效益。因此对于任何想开发出灵活高效、健壮的软件产品的个人或团体,熟练掌握并正确使用设计模式都是必须掌握的基本技能。

比设计模式更重要:GRASP (职责分配原则)
要学习设计模式,有些基础知识是我们必须要先知道的,设计模式是关于类和对象的一种高效、灵活的使用方式,也就是说,必须先有类和对象,才能有设计模式的用武之地,否则一切都是空谈,那么类和对象是从那冒出来的呢?这时就需要比23种设计模式更重要更经典的GRASP模式登场了,嘿嘿,原来这才是老大!
GRASP(General Responsibility Assignment Software Patterns),中文名称为“通用职责分配软件模式”,GRASP一共包括9种模式,它们描述了对象设计和职责分配的基本原则。也就是说,如何把现实世界的业务功能抽象成对象,如何决定一个系统有多少对象,每个对象都包括什么职责,GRASP模式给出了最基本的指导原则。初学者应该尽快掌握、理解这些原则,因为这是如何设计一个面向对象系统的基础。可以说,GRASP是学习使用设计模式的基础。
1. Information Expert (信息专家)
信息专家模式是面向对象设计的最基本原则,是我们平时使用最多,应该跟我们的思想融为一体的原则。也就是说,我们设计对象(类)的时候,如果某个类拥有完成某个职责所需要的所有信息,那么这个职责就应该分配给这个类来实现。这时,这个类就是相对于这个职责的信息专家。
例如:常见的网上商店里的购物车(ShopCar),需要让每种商品(SKU)只在购物车内出现一次,购买相同商品,只需要更新商品的数量即可。如下图:

针对这个问题需要权衡的是,比较商品是否相同的方法需要放到那里类里来实现呢?分析业务得知需要根据商品的编号(SKUID)来唯一区分商品,而商品编号是唯一存在于商品类里的,所以根据信息专家模式,应该把比较商品是否相同的方法放在商品类里。
2. Creator (创造者)
实际应用中,符合下列任一条件的时候,都应该由类A来创建类B,这时A是B的创建者:
a. A是B的聚合
b. A是B的容器
c. A持有初始化B的信息(数据)
d. A记录B的实例
e. A频繁使用B
如果一个类创建了另一个类,那么这两个类之间就有了耦合,也可以说产生了依赖关系。依赖或耦合本身是没有错误的,但是它们带来的问题就是在以后的维护中会产生连锁反应,而必要的耦合是逃不掉的,我们能做的就是正确地创建耦合关系,不要随便建立类之间的依赖关系,那么该如何去做呢?就是要遵守创建者模式规定的基本原则,凡是不符合以上条件的情况,都不能随便用A创建B。
例如:因为订单(Order)是商品(SKU)的容器,所以应该由订单来创建商品。如下图:

1.单一职责原则(SRP);
2.开放--封闭原则(OCP);
3.依赖倒置原则(DIP);
4.接口隔离原则(ISP);
5.替换原则(LSP);

Zend Framework V1.5 中的新特性[转]

2008 年 5 月 26 日

流行的开源软件 Zend Framework 刚刚实现了一些出色的增强。本文介绍 V1.5 中的新特性以及如何升级对 GData Web 服务的增强支持,包括 Zend_FormZend_Layout Zend_View,并且,改进的 Ajax 支持可以帮助 PHP 开发人员轻松地构建先进的 Web 应用程序。

软件框架指一组代码库集合,旨在以一种标准化方式处理应用程序中的所有基本需求,以便使开发人员集中精力进行开发,而不是进行重复劳动。目前有多个开源的 PHP 开发框架可供选择,在所有这些框架中,Zend Framework 也许是最受欢迎的。

Zend 收到欢迎的原因是它非常重视最佳实践,这一特性对于那些重视可持续性的开发人员来说十分有吸引力。Zend 以一种高度模块化的方式构造框架:大多数 Zend Framework 组件可以完全分离并单独使用,这一点非常吸引只需要用到可用库的其中一部分的开发人员。Zend 的灵活性以及重视最佳实践而产生的良好标准化,使它成为具有广泛用途的实用框架。

已经很强大的 Zend Framework 在 V1.5 中又增加了几个全新的组件和组件增强。这些升级使开发复杂的 PHP 应用程序更加简单、更加可维护,这是因为对诸如表单验证例程甚至是前端布局创建等内容实现了标准化。让我们首先看一下Zend_Form 组件以及它提供的全部功能。

Zend_Form

Zend Framework 的一个令人兴奋的新增特性就是 Zend_Form 组件。Web 应用程序如果不能接收输入数据并对其进行处理,那么就会变得毫无意义,获得用户数据的最简单方式就是使用表单。当然,在开发与维护一个 Web 应用程序时,表单是最单调乏味的工作:您必须验证收到的全部内容,确保它是您需要的内容,并需要对每个到来的数据片段进行单独处理。所有上述操作都假设您 不会生成错误消息并重复所有工作,当然,您肯定会遇到这些问题 — 如果不能重用代码,手工执行这些工作实在是非常让人畏惧的任务。

新的表单组件提供的功能不仅仅是允许您用编程的方法创建一个表单和表单元素。Zend_Form 能够完成复杂的验证并在表单验证失败时在表单中显示错误信息。

Zend_Form 的使用方法简单明了。首先,将 form 对象实例化,然后设置它的动作和方法。最后,将表单元素添加到 form 对象中,并且对单个表单元素应用表单元素验证器和过滤器。在应用程序中呈现和使用完整的 form 对象,对结果进行验证和过滤。查看清单 1 中的简单表单示例。
清单 1. 一个简单的表单

    require_once 'Zend/Form.php';
    $form = new Zend_Form(array(
        'method'   => 'post',
        'elements' => array(
            'name' => array('text', array(
                'required' => true,
                'label' => 'Name',
          'validators' => array('alpha')
            )),
            'age' => array('text', array(
                'required' => true,
                'label' => 'Age',
          'validators' => array('digits')
            )),

            'submit' => array('submit', array(
                'label' => 'Send'
            ))
        ),
    ));

清单 1 的结果如图 1 所示。
图 1. 表单
The form
Zend_Form 附带了下列元素:按钮、复选框、隐藏(hiden)、图片、多选框,多重选择、密码、单选、重置、选择、提交、文本、文本区。Zend_Form 不仅仅限制于附带的元素:内置的 Zend_Form_Element 类允许您创建自己的元素。

您可以手动对表单元素进行实例化并把它们连接到表单,或者让表单对象创建并实例化一个新表单元素,使它符合您要求的类型。在将单个元素附加到 form 对象之前或之后,每个元素都可根据需要进行单独配置,并且在呈现完成的表单之前,可以随时将单独的验证器和过滤器集合附加到它们的表单元素中。

可 以对您的表单元素使用 19 种标准验证类型。这些验证器可以检查您通常需要采集的所有数据类型 — 从简单的数据类型检验到正则表达式是否匹配电子邮件地址的验证(可配置为确保存在顶级域并有一个 MX 记录)。默认情况下,如果一个验证器返回 false,则剩余的验证检查都将取消,但这个参数也可以对单个验证器进行配置。

在清单 1 中,一个 alpha 验证器应用到名字字段中,数字验证器应用到年龄字段中。当您输入的数据未通过这些验证测试时,图 2 中显示了默认情况下发生的情况。
图 2 未通过验证测试的表单
未通过验证测试的表单
准 备好验证器之后,下一步将应用过滤器。如果说验证器的作用相当于俱乐部里的保镖,过滤器的作用更象一个造型师:过滤器取出表单元素中的数据,过滤掉您不希 望看到的内容并传递剩下的内容。有 12 种标准的内置过滤器,可用于去掉空格、从数值中去掉字母、把文本转换为大写、把特定字符转换为 HTML 项以及删除禁止的标记。如果您知道所需的数据位于字段中,但是这些数据需要进行大量删减和处理,这种情况下应使用过滤器。

为了在简单的表单中演示过滤器的作用,将对 elements 数组做一些修改。
清单 2. 把过滤器应用到 elements 数组

        'elements' => array(
            'name' => array('text', array(
                'required' => true,
                'label' => 'Name',
          'validators' => array('alpha'),
          'filters' => array('StringToUpper')

            )),
            'age' => array('text', array(
                'required' => true,
                'label' => 'Age',
          'validators' => array('digits'),
          'filters' => array('digits')
            )),

            'submit' => array('submit', array(
                'label' => 'Send'
            ))
        ),

正如您所见,我们增加了一个过滤器把名字字段全部改为大写,并使用另一个过滤器把年龄字段中的非数字内容全部去掉。现在,当我们提交这些值后,我们会看到如下的结果。
图 3. 应用过滤器后的表单
应用过滤器后的表单
当我们单击 Send 后,下列值将通过验证和过滤器,得到如下所示的结果。
图 4. 应用验证和过滤器后的表单
应用验证和过滤器后的表单
当 且仅当存在一个数字并且数字中没有插入错误内容,那么在传递到服务器时,年龄输入、过滤器和验证器以及该组合所需的属性才可以真正确保某个值会通过验证。 需要注意的是,名字字段通过验证的惟一方式是名字字段必须以 alpha-字符开头,原因是在这种情况下,过滤器不会帮助这个值通过验证器。

可以使用几种方式呈现完成的表单。表单有一种可以直接使用或回显的呈现方法。可是,大多数表单使用 Zend_View helper 进行呈现。


回页首

Zend_LayoutZend_View

Zend Framework V1.5 中新的 Zend_Layout 和增强的 Zend_View 组件经常一起提到,这是有原因的。这两个组件协同工作可以成功地在 PHP 应用程序中将表示从控制器中分离出来,并以一种支持快速开发和维护的方式对表示进行标准化。

所有这些意味着什么?

这意味着只要您坚持一些原则并保持代码模块化,那么就可以获得一种标准的、更轻松的方式维护一致的前端。

Zend_Layout 并不需要与 Model-View-Controller (MVC) 一同使用。与 Zend Framework 中的大部分组件一样,Zend_Layout 可以被取出并独立使用,可是,当它与 MVC 一同使用时,它就变成了在 Zend_View 和控制器之间的一个方便的适配器,可以有效地把表示划分为一个两步骤(two-step)视图并使站点布局更具普遍性。

无论您是否正使用 MVC 或把Zend_Layout 取出作为独立的组件,必须至少创建一个布局脚本。在其他的实现中有时也称作模板,布局脚本定义了一个 Web 页面的基本前端结构,它们把变量(而不是静态数据)放置在页面上,各个页面显示的数据不尽相同(或者更准确地说,不同的动作所显示的数据是不同的)。布局 脚本中占位符变量的填充方式是把布局脚本所需的值分配给布局,告诉布局您希望使用那个特定布局脚本并最终将它呈现为一个页面。

实际的呈现过程由 Zend_View 完成。因此就 Zend_Layout 而言,您可以免费获得 Zend_View 的全部功能。布局脚本应通过视图 helper 进行筛选,否则几乎肯定要做错。视图 helper 是集便利性与标准性于一体的重要组件。

当完成您的布局脚本后,您可以在您的引导程序中创建一个 Zend_Layout 实例并设置您的布局配置选项。在它以这种方式实例化后,您可以从动作中通过动作 helper 访问并重新配置 Zend_Layout 对象。在动作内部,您可以通过编程方式修改您希望 Zend_Layout 实例使用哪个布局脚本,并重新填充每个布局脚本需要的当前变量。从控制器的角度看,最后发送给用户的所有数据被放入一个适配器中,适配器把这些数据用于完全不同的范围。


回页首

OpenID 和 InfoCard (Web 2.0)

OpenID 是一个分散式身份识别协议,可跨多个网站使用。它不需要正确的用户名和密码组合,而是提示给用户一个从现有的 OpenID 提供者处得到的 URL 或 XRL。由提供者完成身份验证过程。提供者执行身份验证过程的一种方式是请求一个 InfoCard。当提供者成功地与请求站点完成身份验证通信后,用户就会认为是处于登录状态,并且可以从他的个人 URL 处获得额外的信息。

Zend Framework V1.5 不仅支持将 OpenID 和 InfoCard 作为一个客户机站点,还支持将它们作为 OpenID 提供者。这些组件完全分离,互相之间没有依赖性。这看起来很奇怪,通过 Zend Framework V1.5,您可以开发一个不支持 OpenID 的 OpenID 提供者,反之亦然(这更有意义)。框架中还有一个内置的 InfoCard 组件,它为与 OpenID 协同使用的 InfoCards 提供了依赖方(或支持 OpenID)支持。


回页首

Lucene 高级搜索

Lucene 是一个开源的信息检索搜索引擎,以它的全文本索引能力和搜索互联网能力而著名。Zend_Search_Lucene 组件为把 Zend Framework V1.5 交付了内置的全文本高级搜索引擎功能。需要注意的是,虽然 Lucene 是一个搜索引擎,但它没有爬行能力。您必须手动把全部相关内容添加到 Zend_Search_Lucene 的索引中(参见 参考资料 中关于 Lucene 的 developerWorks 文章)。

Zend_Search_Lucene 组件并非首先出现在 Zend Framework V1.5 中。这个搜索引擎的早期版本早已实现。可是,Zend Framework V1.5 的发布标志着它从 V1.9 到 V2.2 这一重要升级。通过这一升级, Lucene 组件获得了三种强大的高级搜索选项。

现在它已经支持一个或多个通配符,用于范围搜索或模糊搜索。范围搜索在一个指定上下界的范围内查找指定字段的值,而模糊搜索查找与用户提供的术语相似的单词。例如,对 “Mike” 的模糊搜索可能会返回 “bike” 和 “mire”。

虽然 Lucene 组件进行了升级,原有的功能仍然存在,并且所有以前存在的索引在增强的 Zend_Search_Lucene 组件第一次用到它们时会自动升级。


回页首

LDAP 授权

轻 量级目录访问协议(LDAP)在 Zend Framework 中刚刚得到支持。这对于很多开发人员来说是个好消息,比如那些需要创建使用一组现有的正式企业凭证的企业内部网应用程序的开发人员。在 Zend Framework 的文档中需要注意到,当前的支持仅仅是一个最基本的实现,仅仅交付了完成 Zend_Auth_Adapter_LDAP 工作所需的最少功能。

您的 config.ini 文件中指定了有关 LDAP 服务器的信息。当用户需要身份验证时,需要提供用户名和密码组合,这些内容和一些附加的 Zend_LDAP 选项将被发送到 .ini 文件指定的 LDAP 服务器上进行身份验证。就像所有其他的授权适配器一样,只要 Zend_Auth_Adapter_Ldap 从服务器中用下列方法得到一个授权后,它就立即返回一个 Zend_Auth_ResultgetCode() 将返回发生事件的代码(通过或失败); getIdentity() 将返回进行身份验证尝试的身份;getMessages() 将只包含失败了的身份验证消息;而如果成功通过身份验证则 isValid() 将返回真。


回页首

Nirvanix、Technorati 和 SlideShare Web 服务

三种新的 Web 服务组件也成功加入到 Zend Framework V1.5 中:Nirvanix、Technocrati 和 SlideShare。

Nirvanix 是一个基于互联网的文件存储系统,它允许注册用户通过自己的 API 存储、操作和访问文件。Nirvanix 的设计目的是处理大型的媒体文件(例如视频),并且在能够通过互联网身份验证的任何位置进行访问。

Technorati 是一个博客(blog)搜索引擎,它的 Web 服务允许您对其数据库进行多种不同的查询,包括常规关键字搜索、标记搜索、top 标记搜索、给定关键字的每日博客总数以及 “cosmos” 搜索(可返回包含特定 URL 链接的结果)。使用 Technorati,您可以快速了解到当天最热门的网络话题并随时保持关注。

Slideshare.net 是一个为注册用户托管幻灯片的网站。通过 Slideshare 组件,您可以在您的 PHP 应用程序中嵌入由 Slideshare 托管的幻灯片,观看可以共享的幻灯片,还可以为您的 Slideshare 帐户上传新的幻灯片。

这 三个组件加入到 Zend Framework 目前支持的 Web 服务列表中。不断增加的 Web 服务组件库使您可以快速、轻松地把来自多个流行网站的内容集成到一个 PHP 应用程序中。这些 Web 服务组件可以单独安装,无需安装其他的 Zend Framework 组件,这种特性非常受欢迎,适合于 PHP 开发新手以及经验丰富、喜欢使用不同框架工作的 PHP 开发人员。

接下来,我们将查看一些特性增强,这种增强是对现有特性和组件的重大更新,现有组件和特性需要进行修改,从而满足不断变化的互联网领域的需求。


回页首

Google GData

Zend_GData 组件本质上是面向支持 GData API 的各种 Google 服务的大型适配器。可被 Zend_GData 组件访问的更新后的 GData 服务列表包括:

  • Google Base
  • Google Blogger
  • Google Calendar
  • Google Codesearch
  • Google Documents List
  • Google Notebook
  • Google Provisioning
  • Google Spreadsheets
  • Picasa Web Albums
  • YouTube

也许在 Zend Framework V1.5 中人们最期待的新的 Web 服务支持是对 YouTube API 的支持。现在,通过使用 Zend_GData,您可以使用很多匹配条件搜索并访问 YouTube 视频、访问视频提要和订阅、查看用户个人信息,查看一个特定用户的全部视频和最受欢迎的视频并获取评论。通过现有的功能,可以找到适当的 YouTube 内容并把它们无缝集成到您的 PHP 应用程序中。

目前,Zend Framework 的 YouTube API 支持是只读的。

Adobe PDF 中的 UTF-8 字符支持
Zend Framework V1.5 的另一项改进功能是将 UTF-8 字符集添加到 Zend Framework 早期版本中已有的原生 PDF 支持。这一改进极大地增加了有效字符的数目,并扩展了 PDF 组件支持的语言的数量。另外,现在可以从装载的 PDF 文件中提取字体并更轻松地实现增量更新。

回页首

对 Ajax 支持的改进: ContextSwitch helper 和 REST

Asynchronous JavaScript + XML (Ajax) 对将信息传送到客户机的浏览器会话的方式产生了深远影响。非常简单:使用 Ajax 客户端 JavaScript 可以请求并接收来自外部信息源的信息,无需离开主窗口位置导航或重新装载。Ajax 大量应用于 Web 应用程序中,如 Gmail,后者在更新信息时保持基础窗口不变,且无需重新装载整个窗口。

Zend Framework V1.5 已经通过 ContextSwitch helper 和 Zend_REST 组件改进了原生 Ajax 支持。

ContextSwitch helper 是 Zend_Controller 的一部分,用于以不同的格式或上下文传送现有内容。例如,假设您已经创建了一个用于创建页面的控制器,它列出您在过去三周内吃过的全部早餐的详细信息。通 常,这将通过含有布局和视图(与浏览器友好的网站的其余内容保持一致)的页面显示。现在假设某个人问您是否能以 XML 的形式提供同样的列表。使用 ContextSwitch,您不需要再创建单独的控制器。只需告诉 helper 它可以显示 XML 并且创建 XML 视图脚本,而不是普通的上下文视图脚本。它将确保文档扩展和标题恰当且形式良好,并且以 Ajax 友好的 XML 形式显示您的全部早餐。

现在,事情甚至更加简单了:在 Zend Framework V1.5 中有一个 AjaxContext helper,它专门用来将上下文切换到 XML,从而构建 Ajax 响应。

对 Ajax 的支持继续扩展到 Zend_REST 组件,它包含 REST 客户机和全部的 REST 服务器功能。Zend_REST 可用于生成您自己的 Web 服务或访问和解析 Web 服务。当然,您发布的 XML Web 服务可通过 Ajax 轻松地进行访问。


回页首

结束语

我们已经了解到,Zend Framework V1.5 实现了很多令人兴奋的进展。某些更新可以简化一些日常任务的正确设置,而其他更新则使选项更容易访问。全部这些更新都为您提供了更加强大的功能,这永远不会是坏事。
参考资料

学习

获得产品和技术

讨论

关于作者

据 Katie Horn 的妈妈介绍,她在她 28 岁的生命中花费了太多时间在计算机上。她拥有 Chapman University 大学的计算机科学学士学位。她非常喜欢头衔中包含 “系统” 和 “工程师” 等字眼的工作。尽管在该领域非常精通,她宁愿永远不再被叫做 “网络管理员”。

Drupal中的节点(Node)

什么是节点(Node):

节点是构建Drupal的一个核心模块,“节点(Node)”并不是告诉你它是某个网络的一部分,相反地,你应该将一个节点想成是你网站一个具有魔
力的一部分,它可以由你网站的用户创建,当然你自己也可以创建,它可以是一个博客或者一份手册,通过Drupal内容管理工具箱(Content
Construction
Kit),你甚至可以创建无限多种你可以想像的类型的节点。你要牢记的是,任何一个节点都有它的类型,这种类型指向节点的内容类型,每一个节点都有自己唯
一的节点ID号、一个标题、主内容区、创作的内容、一位作者以及一些其它的属性,一个节点与其它所有节点一起被存在在数据库中的一份数据表之中。

Drupal拥有很多数据表,核心数据表在50份左右,或许你q希望将它们中的某些数据显示在你的网站上来实现某种功能。

用户模块也拥有自己的数据表,用户之中有很多又是某节点的作者,在这两者之间就产生了一种联系,要找到与某位用户相关联的节点的唯一途径就是搜索整个数据表想发现相匹配的项目,这是一个很大的工程,当然,感谢数据库服务器是很快速的。

任何一个节点都可以拥有无限数量的评论,评论又被存放在另一份数据表中,要找到某个节点所拥有的所有评论,我们需要搜索评论数据表中的所有记录。

节点是怎样工作的:

看一下正在访问Drupal站点的浏览器的地址栏,你或许可以看到这样的一个地址:“http:
//yourdomain.com/node/1”,这正是浏览器使用Drupal方式来向Drupal站点请求一次数据库查询,其内容包括:“从数据库
中查询ID为1的节点的所有查询允许的信息(如作者、评论等)并展示在页面中”,这叫数据查询请求。

大多数查询都是由相关模块来完成的,/tracker 会搜索所有节点并且按时间将结果存在在一份数据表中,只要你开启了Tracker 模块,那么它将一直运行。

当你将鼠标移到一个链接上面之后,察看浏览器的状态栏,你或许就可以更深的了解这个链接的文字所表达的意思,因为Drupal可以模仿文件夹路径一样的链接来表示内容的类型。

Drupal的模块(Modules)可以对数据进行很多操作,打个比方,当你打开一个你自己创建的文章节点后,你会在页面的最底部看到一个标签
栏,里面有查看/编辑(View/Edit)两个链接,但是这两个链接在不是你自己创建的页面却看不到,这就是通过用户权限设置后所拥有的功能。

节点类型:

Drupal将所有内容存储为节点,系统默认的节点类型有以下几种:

  • 博客文章:由网站用户发表在他开设于Drupal站点上的博客上的文章。
  • 网站文章:与博客文章类似,只不过它不是发表在用户的个人博客上,而是有相关权限的用户直接发表于网站上。
  • 手册页面:手册页面是为联合协作文档编辑而准备的,一份手册可以由很多人共同完成。
  • 评论:评论是每一个节点都有的,它是有评论权限的用户对某一个节点内容的看法,由用户提交。
  • 论坛:论坛是内建于Drupal系统之中的默认模块,通过它网站拥有者可以建立根据不同的话题建立不同的论坛,用户可以在不的同论坛中发起相关的讨论。
  • 页面:页面是一种简单的节点,它只是一个静态的页面并且可以生成一个在主导航栏的链接
  • 投票:一种特殊的 节点,可以发起对某个话题的投票与调查活动
  • 其它类型节点:由不同的模块生成的相关的节点

Memcache的使用和协议分析详解

Memcache是danga.com的一个项目,最早是为 LiveJournal 服务的,目前全世界不少人使用这个缓存项目来构建自己大负载的网站,来分担数据库的压力。(关于Memcache的更多信息请Google)
Memcache官方网站:http://www.danga.com/memcached

【安装Memcache服务器端】

我目前的平台,服务器是Fedora Core 1(内核:2.4.22),客户端是Windows XP SP2,需要安装的就是服务器的Memcached的守护进程和客户端的PHP扩展php_memcache两个东西。现在我分别来讲。

服务器端主要是安装memcache服务器端,目前的最新版本是 memcached-1.2.0 。
下载:http://www.danga.com/memcached/dist/memcached-1.2.0.tar.gz
另外,Memcache用到了libevent这个库用于Socket的处理,所以还需要安装libevent,libevent的最新版本是libevent-1.2。(如果你的系统已经安装了libevent,可以不用安装)
官网:http://www.monkey.org/~provos/libevent/
下载:http://www.monkey.org/~provos/libevent-1.2.tar.gz

我分别把两个东东下载回来,放到 /tmp 目录下:
# cd /tmp
# wget
http://www.danga.com/memcached/dist/memcached-1.2.0.tar.gz
# wget http://www.monkey.org/~provos/libevent-1.2.tar.gz

先安装libevent:
# tar zxvf libevent-1.2.tar.gz
# cd libevent-1.2
# ./configure –prefix=/usr
# make
# make install

然后看看我们的libevent是否安装成功:
# ls -al /usr/lib | grep libevent
lrwxrwxrwx    1 root     root          21 11?? 12 17:38 libevent-1.2.so.1 -> libevent-1.2.so.1.0.3
-rwxr-xr-x       1 root     root          263546 11?? 12 17:38 libevent-1.2.so.1.0.3
-rw-r–r–        1 root     root          454156 11?? 12 17:38 libevent.a
-rwxr-xr-x       1 root     root          811 11?? 12 17:38 libevent.la
lrwxrwxrwx    1 root     root          21 11?? 12 17:38 libevent.so -> libevent-1.2.so.1.0.3

还不错,都安装上了,再来安装memcache,同时需要安装中指定libevent的安装位置:

# cd /tmp
# tar zxvf memcached-1.2.0.tar.gz
# cd memcached-1.2.0
# ./configure –with-libevent=/usr
# make
# make install

如果中间出现报错,请仔细检查错误信息,按照错误信息来配置或者增加相应的库或者路径。
安装完成后会把memcached放到 /usr/local/bin/memcached ,我们看以下是否安装了:

# ls -al /usr/local/bin/mem*
-rwxr-xr-x    1 root     root       137986 11?? 12 17:39 /usr/local/bin/memcached
-rwxr-xr-x    1 root     root       140179 11?? 12 17:39 /usr/local/bin/memcached-debug

恩,安装完成了,现在我们看以下memcache的帮助:

# /usr/local/bin/memecached -h
memcached 1.2.0
-p <num>            port number to listen on
-s <file>               unix socket path to listen on (disables network support)
-l <ip_addr>        interface to listen on, default is INDRR_ANY
-d                          run as a daemon
-r                           maximize core file limit
-u <username> assume identity of <username> (only when run as root)
-m <num>          max memory to use for items in megabytes, default is 64 MB
-M                         return error on memory exhausted (rather than removing items)
-c <num>            max simultaneous connections, default is 1024
-k                          lock down all paged memory
-v                          verbose (print errors/warnings while in event loop)
-vv                        very verbose (also print client commands/reponses)
-h                         print this help and exit
-i                          print memcached and libevent license
-b                         run a managed instanced (mnemonic: buckets)
-P <file>             save PID in <file>, only used with -d option
-f <factor>          chunk size growth factor, default 1.25
-n <bytes>         minimum space allocated for key+value+flags, default 48

参数不算多,我们来启动一个Memcache的服务器端:
# /usr/local/bin/memcached -d -m 10  -u root -l 192.168.0.200 -p 12000 -c 256 -P /tmp/memcached.pid

-d选项是启动一个守护进程,-m是分配给Memcache使用的内存数量,单位是MB,我这里是10MB,-u是运行Memcache的用户,我
这里是root,-l是监听的服务器IP地址,如果有多个地址的话,我这里指定了服务器的IP地址192.168.0.200,-p是设置
Memcache监听的端口,我这里设置了12000,最好是1024以上的端口,-c选项是最大运行的并发连接数,默认是1024,我这里设置了
256,按照你服务器的负载量来设定,-P是设置保存Memcache的pid文件,我这里是保存在
/tmp/memcached.pid,如果要结束Memcache进程,执行:
# kill `cat /tmp/memcached.pid`

也可以启动多个守护进程,不过端口不能重复。

【安装Memcache的PHP扩展】

Memcache就是在服务器监听端口,通过一定的协议交互来写入数据到服务器内存中,或者获取一些值。如果你了解Memcache的交互协议,完
全可以自己构建Memcache的客户端,目前网上也有很多构建好的Memcache客户端的PHP
Class,可以直接用,不过我这里为了效率,还是决定使用PECL中Memcache的专用扩展,因为毕竟是用C写的,效率比较高,而且安装部署比较方
便。

下载PECL中的Memcache,因为我的客户端是Windows XP,所以需要下载dll版,我的PHP版本是PHP 5.1.4,必须下载PHP 5.1专用的扩展。
PECL官网:http://pecl.php.net (For Linux)
                       http://pecl4win.php.net(For Windows)
扩展下载: http://pecl4win.php.net/download.php/ext/5_1/5.1.2/php_memcache.dll

如果你的PHP是其他版本,请到 http://pecl4win.php.net/ext.php/php_memcache.dll 选择你相应的版本,如果是Linux下的PHP,请到 http://pecl.php.net/package/memcache 选择相应想要下载的版本。

下载完了以后,我把php_memcache.dll 拷贝到 c:\php5\ext 目录下,如果你的扩展目录是在是缺省路径,(就是没有修改过php.ini中的扩展路径) 请拷贝到 c:\windows\ 目录下,如果是Linux平台,请自己编译安装,可以在程序中使用dl()函数加载,或者在编译php的时候加载进去。最后重启Web服务器,IIS/Apache。

我的网站目录是在:d:\mysite 目录下,现在建立一个 phpinfo.php 文件在网站根目录下,代码是:
<?phpinfo()?>
看有没有成功加载 php_memcache.dll 扩展。如果显示了 Memcache 选项和相应的版本信息,则说明成功加载了,否则请仔细检查上面的步骤。

如果一切正确无误,那么说明安装成功。

 

【Memcache初试】

[ 接口介绍 ]
服务器端和客户端都安装配置好了,现在
我们就来测试以下我们的成果。Memcache客户端包含两组接口,一组是面向过程的接口,一组是面向对象的接口,具体可以参考PHP手册
“LXXV. Memcache Functions”
这章。我们为了简单方便,就使用面向对象的方式,也便于维护和编写代码。Memcache面向对象的常用接口包括:

Memcache::connect — 打开一个到Memcache的连接
Memcache::pconnect — 打开一个到Memcache的长连接
Memcache::close — 关闭一个Memcache的连接
Memcache::set — 保存数据到Memcache服务器上
Memcache::get — 提取一个保存在Memcache服务器上的数据
Memcache::replace — 替换一个已经存在Memcache服务器上的项目(功能类似Memcache::set)
Memcache::delete — 从Memcache服务器上删除一个保存的项目
Memcache::flush — 刷新所有Memcache服务器上保存的项目(类似于删除所有的保存的项目)
Memcache::getStats — 获取当前Memcache服务器运行的状态

[ 测试代码 ]
现在我们开始一段测试代码:

<?php
//连接
$mem = new Memcache;
$mem->connect(192.168.0.200, 12000);

//保存数据
$mem->set(key1, This is first value, 0, 60);
$val = $mem->get(key1);
echo Get key1 value:  . $val .<br>;

//替换数据
$mem->replace(key1, This is replace value, 0, 60);
$val = $mem->get(key1);
echo Get key1 value:  . $val . <br>;

//保存数组
$arr = array(aaa, bbb, ccc, ddd);
$mem->set(key2, $arr, 0, 60);
$val2 = $mem->get(key2);
echo  >Get key2 value: ;
print_r($val2);
echo <br>;

//删除数据
$mem->delete(key1);
$val = $mem->get(key1);
echo Get key1 value:  . $val . <br>;

//清除所有数据
$mem->flush();
$val2 = $mem->get(key2);
echo Get key2 value: ;
print_r($val2);
echo <br>;

//关闭连接
$mem->close();
?>

如果正常的话,浏览器将输出:
Get key1 value: This is first value
Get key1 value: This is replace value
Get key2 value: Array ( [0] => aaa [1] => bbb [2] => ccc [3] => ddd )
Get key1 value:
Get key2 value:

基本说明我们的Memcache安装成功,我们再来分析以下上面的这段程序。

[ 程序分析 ]

初始化一个Memcache的对象:
$mem = new Memcache;

连接到我们的Memcache服务器端,第一个参数是服务器的IP地址,也可以是主机名,第二个参数是Memcache的开放的端口:
$mem->connect(“192.168.0.200″, 12000);

保存一个数据到Memcache服务器上,第一个参数是数据的key,用来定位一个数据,第二个参数是需要保存的数据内容,这里是一个字符串,第三
个参数是一个标记,一般设置为0或者MEMCACHE_COMPRESSED就行了,第四个参数是数据的有效期,就是说数据在这个时间内是有效的,如果过
去这个时间,那么会被Memcache服务器端清除掉这个数据,单位是秒,如果设置为0,则是永远有效,我们这里设置了60,就是一分钟有效时间:
$mem->set(‘key1‘, ‘This is first value’, 0, 60);

从Memcache服务器端获取一条数据,它只有一个参数,就是需要获取数据的key,我们这里是上一步设置的key1,现在获取这个数据后输出输出:
$val = $mem->get(‘key1′);
echo “Get key1 value: “ .
$val;

现在是使用replace方法来替换掉上面key1的值,replace方法的参数跟set是一样的,不过第一个参数key1是必须是要替换数据内容的key,最后输出了:
$mem->replace(‘key1′, ‘This is replace value’, 0, 60);
$val = $mem->get(‘key1′);
echo “Get key1 value: ” . $val;

同样的,Memcache也是可以保存数组的,下面是在Memcache上面保存了一个数组,然后获取回来并输出
$arr = array(‘aaa’, ‘bbb’, ‘ccc’, ‘ddd’);
$mem->set(‘key2′, $arr, 0, 60);
$val2 = $mem->get(‘key2′);
print_r($val2);

现在删除一个数据,使用delte接口,参数就是一个key,然后就能够把Memcache服务器这个key的数据删除,最后输出的时候没有结果
$mem->delete(‘key1′);
$val = $mem->get(‘key1′);
echo “Get key1 value: ” . $val .
“<br>”;

最后我们把所有的保存在Memcache服务器上的数据都清除,会发现数据都没有了,最后输出key2的数据为空,最后关闭连接
$mem->flush();
$val2 = $mem->get(‘key2′);
echo “Get key2 value: “;
print_r($val2);
echo
“<br>”;

 

【Memcache协议分析】

如果你不喜欢 php_memcache.dll 扩展或者服务器器目前不支持这个扩展,那么就可以考虑自己构建,需要构建Memcahe的客户端,要先了解Memcache协议的交互,这样才能开发自己的客户端,我这里就简单的分析以下Memcache的协议。
(更详细的协议内容请在Memcache服务器端的源码的 doc/protocol.txt 文件中,本文基本来源于此)

Memcache既支持TCP协议,也支持UDP协议,不过我们这里是以TCP协议的协议作为主要考虑对象,想了解UDP协议的过程,请参考 doc/protocol.txt 文件。

[ 错误指令]
Memcache的协议的错误部分主要是三个错误提示之提示指令:
普通错误信息,比如指令错误之类的
ERROR\r\n

客户端错误
CLIENT_ERROR <错误信息>\r\n

服务器端错误
SERVER_ERROR <错误信息>\r\n

[ 数据保存指令]
数据保存是基本的功能,就是客户端通过命令把数据返回过来,服务器端接收后进行处理。
指令格式:
<命令> <键> <标记> <有效期> <数据长度>\r\n

<命令>command name
主要是三个储存数据的三个命令, set, add, replace
set 命令是保存一个叫做key的数据到服务器上
add 命令是添加一个数据到服务器,但是服务器必须这个key是不存在的,能够保证数据不会被覆盖
replace 命令是替换一个已经存在的数据,如果数据不存在,就是类似set功能

<键>key
就是保存在服务器上唯一的一个表示符,必须是跟其他的key不冲突,否则会覆盖掉原来的数据,这个key是为了能够准确的存取一个数据项目

<标记>flag
标记是一个16位的无符号整形数据,用来设置服务器端跟客户端一些交互的操作

<有效期>expiration time
是数据在服务器上的有效期限,如果是0,则数据永远有效,单位是秒,Memcache服务器端会把一个数据的有效期设置为当前Unix时间+设置的有效时间

<数据长度>bytes
数据的长度,block data 块数据的长度,一般在这个个长度结束以后下一行跟着block data数据内容,发送完数据以后,客户端一般等待服务器端的返回,服务器端的返回:

数据保存成功
STORED\r\n

数据保存失败,一般是因为服务器端这个数据key已经存在了
NOT_STORED\r\n

[ 数据提取命令]
从服务器端提取数据主要是使用get指令,格式是:
get <键>*\r\n

<键>*key
key是是一个不为空的字符串组合,发送这个指令以后,等待服务器的返回。如果服务器端没有任何数据,则是返回:
END\r\n

证明没有不存在这个key,没有任何数据,如果存在数据,则返回指定格式:
VALUE <> <标记> <数据长度>\r\n
<数据块>\r\n

返回的数据是以VALUE开始的,后面跟着key和flags,以及数据长度,第二行跟着数据块。

<键> -key
是发送过来指令的key内容

<标记> – flags
是调用set指令保存数据时候的flags标记

<数据长度> – bytes
是保存数据时候定位的长度

<数据块> – data block
数据长度下一行就是提取的数据块内容

 

[ 数据删除指令]
数据删除指令也是比较简单的,使用get指令,格式是:
delete <键> <超时时间>\r\n

<键> – key
key是你希望在服务器上删除数据的key键

<超时时间> – timeout
按照秒为单位,这个是个可选项,如果你没有指定这个值,那么服务器上key数据将马上被删除,如果设置了这个值,那么数据将在超时时间后把数据清除,该项缺省值是0,就是马上被删除

删除数据后,服务器端会返回:
DELETED\r\n
删除数据成功
NOT_FOUND\r\n
这个key没有在服务器上找到

如果要删除所有服务器上的数据,可以使用flash_all指令,格式:
flush_all\r\n

这个指令执行后,服务器上所有缓存的数据都被删除,并且返回:
OK\r\n

这个指令一般不要轻易使,除非你却是想把所有数据都干掉,删除完以后可以无法恢复的。

[其他指令]
如果想了解当前Memcache服务器的状态和版本等信息,可以使用状态查询指令和版本查询指令。

如果想了解当前所有Memcache服务器运行的状态信息,可以使用stats指令,格式
stats\r\n
服务器将返回每行按照 STAT 开始的状态信息,包括20行,20项左右的信息,包括守护进程的pid、版本、保存的项目数量、内存占用、最大内存限制等等信息。

如果只是想获取部分项目的信息,可以指定参数,格式:
stats <参数>\r\n
这个指令将只返回指定参数的项目状态信息。

如果只是想单独了解当前版本信息,可以使用version指令,格式:
version\r\n
将返回以 VERSION 开头的版本信息

如果想结束当前连接,使用quit指令,格式:
quit\r\n

将断开当前连接

另外还有其他指令,包括incr, decr 等,我也不太了解作用,就不做介绍了,如果感兴趣,可以自己去研究。

 

【Memcache在中型网站的使用】

使用Memcache的网站一般流量都是比较大的,为了缓解数据库的压力,让Memcache作为一个缓存区域,把部分信息保存在内存中,在前端能
够迅速的进行存取。那么一般的焦点就是集中在如何分担数据库压力和进行分布式,毕竟单台Memcache的内存容量的有限的。我这里简单提出我的个人看
法,未经实践,权当参考。

[ 分布式应用]
Memcache本来支持分布式,我们
客户端稍加改造,更好的支持。我们的key可以适当进行有规律的封装,比如以user为主的网站来说,每个用户都有User
ID,那么可以按照固定的ID来进行提取和存取,比如1开头的用户保存在第一台Memcache服务器上,以2开头的用户的数据保存在第二胎
Mecache服务器上,存取数据都先按照User ID来进行相应的转换和存取。

但是这个有缺点,就是需要对User ID进行判断,如果业务不一致,或者其他类型的应用,可能不是那么合适,那么可以根据自己的实际业务来进行考虑,或者去想更合适的方法。

[ 减少数据库压力]
这个算是比较重要的,所有的数据基
本上都是保存在数据库当中的,每次频繁的存取数据库,导致数据库性能极具下降,无法同时服务更多的用户,比如MySQL,特别频繁的锁表,那么让
Memcache来分担数据库的压力吧。我们需要一种改动比较小,并且能够不会大规模改变前端的方式来进行改变目前的架构。

我考虑的一种简单方法:
后端的数据库操作模块,把所有的Select操作提取出来(update/delete/insert不管),然后
把对应的SQL进行相应的hash算法计算得出一个hash数据key(比如MD5或者SHA),然后把这个key去Memcache中查找数据,如果这
个数据不存在,说明还没写入到缓存中,那么从数据库把数据提取出来,一个是数组类格式,然后把数据在set到Memcache中,key就是这个SQL的
hash值,然后相应的设置一个失效时间,比如一个小时,那么一个小时中的数据都是从缓存中提取的,有效减少数据库的压力。

缺点是数据不实时,当数据做了修改以后,无法实时到前端显示,并且还有可能对内存占用比较大,毕竟每次select出来的数据数量可能比较巨大,这个是需要考虑的因素。

上面只是我两点没有经过深思熟虑的简单想法,也许有用,那就最好了。

 

【Memcache的安全】

我们上面的Memcache服务器端都是直接通过客户端连接后直接操作,没有任何的验证过程,这样如果服务器是直接暴露在互联网上的话是比较危险,
轻则数据泄露被其他无关人员查看,重则服务器被入侵,因为Mecache是以root权限运行的,况且里面可能存在一些我们未知的bug或者是缓冲区溢出
的情况,这些都是我们未知的,所以危险性是可以预见的。

为了安全起见,我做两点建议,能够稍微的防止黑客的入侵或者数据的泄露。

[ 内网访问]
最好把两台服务器之间的访问是内网形态
的,一般是Web服务器跟Memcache服务器之间。普遍的服务器都是有两块网卡,一块指向互联网,一块指向内网,那么就让Web服务器通过内网的网卡
来访问Memcache服务器,我们Memcache的服务器上启动的时候就监听内网的IP地址和端口,内网间的访问能够有效阻止其他非法的访问。

# memcached -d -m 1024  -u root -l 192.168.0.200 -p 11211 -c 1024 -P /tmp/memcached.pid

Memcache服务器端设置监听通过内网的192.168.0.200的ip的11211端口,占用1024MB内存,并且允许最大1024个并发连接

[ 设置防火墙]
防火墙是简单有效的方式,如果却是两台服务器都是挂在网的,并且需要通过外网IP来访问Memcache的话,那么可以考虑使用防火墙或者代理程序来过滤非法访问。
一般我们在Linux下可以使用iptables或者FreeBSD下的ipfw来指定一些规则防止一些非法的访问,比如我们可以设置只允许我们的Web服务器来访问我们Memcache服务器,同时阻止其他的访问。

# iptables -F
# iptables -P INPUT DROP
# iptables -A INPUT -p tcp -s 192.168.0.2 –dport 11211 -j ACCEPT
# iptables -A INPUT -p udp -s 192.168.0.2 –dport 11211 -j ACCEPT

上面的iptables规则就是只允许192.168.0.2这台Web服务器对Memcache服务器的访问,能够有效的阻止一些非法访问,相应的也可以增加一些其他的规则来加强安全性,这个可以根据自己的需要来做。

 

【Memcache的扩展性】

Memcache算是比较简洁高效的程序,Memcache 1.2.0 的源代码大小才139K,在Windows平台上是不可想象的,但是在开源世界来说,这是比较正常合理的。
Memcache目前都只是比较简单的功能,简单的数据存取功能,我个人希望如果有识之士,能够在下面两方面进行扩展。

1. 日志功能
目前Memcache没有日志功能,只有一些命令在服务器端进行回显,这样是很不利于对一个服务器的稳定性和负载等等进行监控的,最好能够相应的加上日志的等功能,便于监控。

2. 存储结构
目前的数据形式就是: key => data 的形式,特别单一,只能够存储单一的一维数据,如果能够扩展的话,变成类似数据库的格式,能够存储二维数据,那样会让可以用性更强,使用面更广,当然相应的可能代码效率和存取效率更差一些。

3. 同步功能
数据同步是个比较重要的技术,因为谁都不
能保证一台服务器是持久正常的运行的,如果能够具有类似MySQL的 Master/Slave
的功能,那么将使得Memcache的数据更加稳定,那么相应的就可以考虑存储持久一点的数据,并且不用害怕Memcache的down掉,因为有同步的
备份服务器,这个问题就不是问题了。

以上三点只是个人拙见,有识之士和技术高手可以考虑。

 

【结束语】

我上面的内容都只是自己安装和使用的一些想法,不能保证绝对正确,只是给需要的人一个参考,一个推广Memcache的文章,希望更多的人能够认识和了解这个技术,并且为自己所用。

我花费了整整一个晚上的时间洋洋洒洒的写了这么长,无非是对于这项开源技术的热爱,我想开源世界能够繁荣起来,就是源于大家的热爱并且愿意做出贡献,开源世界才这么精彩。

希望本文能够给需要的人一些帮助,希望不会误导他们,呵呵。

附加:(我操作Memcache相应对应上面文章内容的图片) 

[ 启动Memcache]

Memcache启动


[ Memcache的PHP测试代码]

代码

 

[测试代码执行效果]

浏览器效果

 

[ 通过Telnet连接到Memcache ]

连接Memcache


[ 基本的Memcache的数据存取协议交互]

存取Memcache指令

 

[ Memcache状态信息协议交互]

获取Memcache信息

Memcache协议中文版

写在前头
偶然之间看到本文的中英文对照版本,感觉看起来不是很方便,于是花费了半个小时的时间,仔细整理出了独立的中文版本,并记录下来。

协议
memcached 的客户端使用TCP链接 与 服务器通讯。(UDP接口也同样有效,参考后文的 “UDP协议” )一个运行中的memcached服务器监视一些(可设置)端口。客户端连接这些端口,发送命令到服务器,读取回应,最后关闭连接。

结束会话不需要发送任何命令。当不再需memcached服务时,要客户端可以在任何时候关闭连接。需要注意的是,鼓励客户端缓存这些连接,而不是
每次需要存取数据时都重新打开连接。这是因为memcached
被特意设计成及时开启很多连接也能够高效的工作(数百个,上千个如果需要的话)。缓存这些连接,可以消除建立连接所带来的开销(/*/相对而言,在服务器
端建立一个新连接的准备工作所带来的开销,可以忽略不计。)。

在memcache协议中发送的数据分两种:文本行 和 自由数据。
文本行被用于来自客户端的命令和服务器的回应。自由数据用于客户端从服务器端存取数据时。同样服务器会以字节流的方式传回自由数据。/*/服务器不用关心
自由数据的字节顺序。自由数据的特征没有任何限制;但是通过前文提到的文本行,这项数据的接受者(服务器或客户端),便能够精确地获知所发送的数据库的长
度。

文本行固定以“\r\n”(回车符紧跟一个换行符)结束。 自由数据也是同样会以“\r\n”结束,但是
\r(回车符)、\n(换行符),以及任何其他8位字符,均可出现在数据中。因此,当客户端从服务器取回数据时,必须使用数据区块的长度来确定数据区块的
结束位置,而不要依据数据区块末尾的“\r\n”,即使它们固定存在于此。

键值
存储在memcached中的数据通过键值来标识。键值是一个文本字符串,对于需要存取这项数据的客户端而言,它必须是唯一的。键值当前的长度限制设定为250字符(当然,客户端通常不会用到这么长的键);键值中不能使用制表符和其他空白字符(例如空格,换行等)。

命令
所有命令分为3种类型:
存储命令(有3项:’set’、’add’、’repalce’)指示服务器储存一些由键值标识的数据。客户端发送一行命令,后面跟着数据区块;然后,客户端等待接收服务器回传的命令行,指示成功与否。
取回命令(只有一项:’get’)指示服务器返回与所给键值相符合的数据(一个请求中右一个或多个键值)。客户端发送一行命令,包括所有请求的键值;服务
器每找到一项内容,都会发送回客户端一行关于这项内容的信息,紧跟着是对应的数据区块;直到服务器以一行“END”回应命令结束。
/*?*/其他的命令都不能携带自由数据。在这些命令中,客户端发送一行命令,然后等待(由命令所决定)一行回应,或最终以一行“END”结束的多行命令。

一行命令固定以命令名称开始,接着是以空格隔开的参数(如果有参数的话)。命令名称大小写敏感,并且必须小写。一些客户端发送给服务器的命令会包含
一些时限(针对内容或客户端请求的操作)。这时,时限的具体内容既可以是Unix时间戳(从1970年1月1日开始的秒钟数),或当前时间开始的秒钟数。
对后者而言,不能超过 60*60*24*30(30天);如果超出,服务器将会理解为Unix时间戳,而不是从当前时间起的秒偏移。

错误字串
每一个由客户端发送的命令,都可能收到来自服务器的错误字串回复。这些错误字串会以三种形式出现:
- “ERROR\r\n”
意味着客户端发送了不存在的命令名称。
- “CLIENT_ERROR <error>\r\n”
意味着输入的命令行里存在一些客户端错误,例如输入未遵循协议。<error>部分是人类易于理解的错误解说……
- “SERVER_ERROR <error>\r\n”
意味着一些服务器错误,导致命令无法执行。<error>部分是人类易于理解的错误解说。在一些严重的情形下(通常应该不会遇到),服务器将在发送这行错误后关闭连接。这是服务器主动关闭连接的唯一情况。
在后面每项命令的描述中,这些错误行不会再特别提到,但是客户端必须考虑到这些它们存在的可能性。

存储命令
首先,客户端会发送一行像这样的命令:
<command name> <key> <flags> <exptime> <bytes>\r\n
- <command name> 是 set, add, 或者 repalce

  • set 意思是 “储存此数据”
  • add 意思是 “储存此数据,只在服务器*未*保留此键值的数据时”
  • replace意思是 “储存此数据,只在服务器*曾*保留此键值的数据时”

- <key> 是接下来的客户端所要求储存的数据的键值
- <flags> 是在取回内容时,与数据和发送块一同保存服务器上的任意16位无符号整形(用十进制来书写)。客户端可以用它作为“位域”来存储一些特定的信息;它对服务器是不透明的。
- <exptime> 是终止时间。如果为0,该项永不过期(虽然它可能被删除,以便为其他缓存项目腾出位置)。如果非0(Unix时间戳或当前时刻的秒偏移),到达终止时间后,客户端无法再获得这项内容。
- <bytes> 是随后的数据区块的字节长度,不包括用于分野的“\r\n”。它可以是0(这时后面跟随一个空的数据区块)。

在这一行以后,客户端发送数据区块。
<data block>\r\n
- <data block> 是大段的8位数据,其长度由前面的命令行中的<bytes>指定。

发送命令行和数据区块以后,客户端等待回复,可能的回复如下:
- “STORED\r\n”
表明成功.
- “NOT_STORED\r\n”
表明数据没有被存储,但不是因为发生错误。这通常意味着add 或 replace命令的条件不成立,或者,项目已经位列删除队列(参考后文的“delete”命令)。

取回命令
一行取回命令如下:
get <key>*\r\n
- <key>* 表示一个或多个键值,由空格隔开的字串
这行命令以后,客户端的等待0个或多个项目,每项都会收到一行文本,然后跟着数据区块。所有项目传送完毕后,服务器发送以下字串:
“END\r\n”
来指示回应完毕。
服务器用以下形式发送每项内容:
VALUE <key> <flags> <bytes>\r\n
<data block>\r\n
- <key> 是所发送的键名
- <flags> 是存储命令所设置的记号
- <bytes> 是随后数据块的长度,*不包括* 它的界定符“\r\n”
- <data block> 是发送的数据

如果在取回请求中发送了一些键名,而服务器没有送回项目列表,这意味着服务器没这些键名(可能因为它们从未被存储,或者为给其他内容腾出空间而被删除,或者到期,或者被已客户端删除)。

删除
命令“delete”允许从外部删除内容:
delete <key> <time>\r\n
- <key> 是客户端希望服务器删除的内容的键名
- <time>
是一个单位为秒的时间(或代表直到某一刻的Unix时间),在该时间内服务器会拒绝对于此键名的“add”和“replace”命令。此时内容被放入
delete队列,无法再通过“get”得到该内容,也无法是用“add”和“replace”命令(但是“set”命令可用)。直到指定时间,这些内容
被最终从服务器的内存中彻底清除。
<time>参数 是可选的,缺省为0(表示内容会立刻清除,并且随后的存储命令均可用)。
此命令有一行回应:
- “DELETED\r\n”
表示执行成功
- “NOT_FOUND\r\n”
表示没有找到这项内容

参考随后的“flush_all”命令使所有内容无效

增加/减少
命令 “incr” 和 “decr”被用来修改数据,当一些内容需要 替换、增加
或减少时。这些数据必须是十进制的32位无符号整新。如果不是,则当作0来处理。修改的内容必须存在,当使用“incr”/“decr”命令修改不存在的
内容时,不会被当作0处理,而是操作失败。

客户端发送命令行:
incr <key> <value>\r\n

decr <key> <value>\r\n
- <key> 是客户端希望修改的内容的建名
- <value> 是客户端要增加/减少的总数。

回复为以下集中情形:
- “NOT_FOUND\r\n”
指示该项内容的值,不存在。
- <value>\r\n ,<value>是 增加/减少 。
注意”decr”命令发生下溢:如果客户端尝试减少的结果小于0时,结果会是0。”incr” 命令不会发生溢出。

状态
命令”stats” 被用于查询服务器的运行状态和其他内部数据。有两种格式。不带参数的:
stats\r\n
这会在随后输出各项状态、设定值和文档。另一种格式带有一些参数:
stats <args>\r\n
通过<args>,服务器传回各种内部数据。因为随时可能发生变动,本文不提供参数的种类及其传回数据。

各种状态
受到无参数的”stats”命令后,服务器发送多行内容,如下:
STAT <name> <value>\r\n
服务器用以下一行来终止这个清单:
END\r\n
在每行状态中,<name> 是状态的名字,<value> 使状态的数据。 以下清单,是所有的状态名称,数据类型,和数据代表的含义。
在“类型”一列中,”32u”表示32位无符号整型,”64u”表示64位无符号整型,”32u:32u”表示用冒号隔开的两个32位无符号整型。

名称 类型 含义
pid 32u 服务器进程ID
uptime 32u 服务器运行时间,单位秒
time 32u 服务器当前的UNIX时间
version string 服务器的版本号
rusage_user 32u:32u 该进程累计的用户时间
(秒:微妙)
rusage_system 32u:32u 该进程累计的系统时间
(秒:微妙)
curr_items 32u 服务器当前存储的内容数量
total_items 32u 服务器启动以来存储过的内容总数
bytes 64u 服务器当前存储内容所占用的字节数
curr_connections 32u 连接数量
total_connections 32u 服务器运行以来接受的连接总数
connection_structures 32u 服务器分配的连接结构的数量
cmd_get 32u 取回请求总数
cmd_set 32u 存储请求总数
get_hits 32u 请求成功的总次数
get_misses 32u 请求失败的总次数
bytes_read 64u 服务器从网络读取到的总字节数
bytes_written 64u 服务器向网络发送的总字节数
limit_maxbytes 32u 服务器在存储时被允许使用的字节总数

其它命令
“flush_all”命令有一个可选的数字参数。它总是执行成功,服务器会发送“OK\r\n”回应。它的效果是使已经存在的项目立即失效(缺省),或
在指定的时间后。此后执行取回命令,将不会有任何内容返回(除非重新存储同样的键名)。flush_all
实际上没有立即释放项目所占用的内存,而是在随后陆续有新的项目被储存时执行。flush_all
效果具体如下:它导致所有更新时间早于flush_all所设定时间的项目,在被执行取回命令时命令被忽略。
“version”命令没有参数:
version\r\n
在回应中,服务器发送:
“VERSION <version>\r\n”
<version> 是服务器的版本字串。
“quit”命令没有参数:
quit\r\n
接收此命令后,服务器关闭连接。不过,客户端可以在不再需要时,简单地关闭连接就行,并不一定需要发送这个命令。

UDP 协议
当来自客户端的连接数远大于TCP连接的上限时,可以使用基于UDP的接口。UDP接口不能保证传输到位,所以只有在不要求成功的操作中使用;比如被用于一个“get”请求时,会因不当的缓存处理而发生错误或回应有遗失。

每个UDP数据包都包含一个简单的帧头,数据之后的内容与TCP协议的描述类似。在执行所产生的数据流中,请求必须被包含在单独的一个UDP数据包中,但是回应可能跨越多个数据包。(只有“get”和“set”请求例外,跨越了多个数据包)

帧头有8字节长,如下(均由16位整数组成,网络字节顺序,高位在前):

  • 0-1 请求ID
  • 2-3 序号
  • 4-5 该信息的数据包总数
  • 6-7 保留位,必须为0

请求ID有客户端提供。一般它会是一个从随机基数开始的递增值,不过客户端想用什么样的请求ID都可以。服务器的回应会包含一个和请求中的同样的
ID。客户端使用请求ID来区分每一个回应。任何一个没有请求ID的数据包,可能是之前的请求遭到延迟而造成的,应该被丢弃。序号的返回是从0到n-
1,n是该条信息的数据包数量。

结束的话
原文最早的出处可能是在http://www.alee2002.com/1这里,然后博乐转载了,再然后我在车东的blog看到了。如果你对Memcache有更多的兴趣,可以参考下列文章:
Linux下的Memcache安装:http://www.ccvita.com/257.html
Windows下的Memcache安装:http://www.ccvita.com/258.html
Memcache基础教程:http://www.ccvita.com/259.html
Discuz!的Memcache缓存实现:http://www.ccvita.com/261.html
Memcache协议中文版:http://www.ccvita.com/306.html

Zend Framework 入门(1)——快速上手

这个系列是我的学习笔记。主要内容来自 Zend Framework 的[程序员参考手册],结合了自己的学习和开发过程。

1. 安装

从 Zend Framework 的网页上下载最新版本。解压后,把整个目录拷贝到一个理想的地方,比如:/php/library/Zend。

打开 php.ini 文件,确认包含 Zend 目录的路径在 include_path 里定义了。以上面的配置为例,php.ini 中应有类似下面的条目:

include_path = “.:/php/library”

注意:Windows 下的写法略有不同,应该类似于 include_path = “.;C:\php\library”

初始的安装就这么简单。Zend Framework 的一些组件会用到 php 的一些附加模块。具体的要求请参考这里

 

2. 项目的目录结构

如果你的项目不包含多个模块,可以用下面的目录结构:

<span style="color: rgb(0, 0, 0);">application/<br />    controllers/<br />        IndexController.php<br />    models/<br />    views/<br />        scripts/<br />            index/<br />                index.phtml<br />        helpers/<br />        filters/<br />html/<br />    .htaccess<br />    index.php<br /></span></pre>
<p>如果你的项目要包含多个模块(比如:博客,社区,等等),那么建议使用<a href="http://framework.zend.com/manual/en/zend.controller.modular.html">模块化的目录结构</a>。</p>
<p> </p>
<p><strong>3. 网页的根目录</strong></p>
<p>网页的根目录应指向上述目录结构中的 html 文件夹。</p>
<p> </p>
<p><strong>4. 重写规则</strong></p>
<p>编辑 html/.htaccess 文件,加入下面两行:</p>
<pre class="programlisting"><span style="color: rgb(0, 0, 0);">RewriteEngine on<br />RewriteRule !\.(js|ico|gif|jpg|png|css)$ index.php<br /></span></pre>
<p><em><strong>注意:</strong></em>上述是针对 apache 的配置。如果是其他的服务器,请参考<a href="http://framework.zend.com/manual/en/zend.controller.router.html#zend.controller.router.introduction">这里</a>。</p>
<p> </p>
<p><strong>5. 引导程序</strong></p>
<p>编辑 html/index.php 文件,敲入下面代码:</p>
<pre><span style="color: rgb(0, 0, 0);"><br /><span style="color: rgb(29, 83, 193);"><?php<br /></span><span style="color: rgb(35, 45, 48);">require_once </span><span style="color: rgb(127, 30, 101);">'Zend/Controller/Front.php'</span><span style="color: rgb(35, 45, 48);">;<br />$rootPath = dirname(dirname(__FILE__));<br /></span><span style="color: rgb(29, 83, 193);">Zend_Controller_Front</span><span style="color: rgb(35, 45, 48);">::</span><span style="color: rgb(29, 83, 193);">run</span><span style="color: rgb(35, 45, 48);">(</span></span><span style="color: rgb(0, 0, 0);"><span style="color: rgb(35, 45, 48);">$rootPath . </span></span><span style="color: rgb(0, 0, 0);"><span style="color: rgb(127, 30, 101);">'/application/controllers'</span><span style="color: rgb(35, 45, 48);">);</span><br /></span><br /></pre>
<p>上面代码的作用是实例化前端控制器(Front Controller)并运行它。</p>
<p> </p>
<p><strong>6. 默认的动作控制器(Action Controller)</strong></p>
<p>Zend Framework 的默认路由规则是 http://域名/控制器名/动作(方法)名。例如:</p>
<p>http://example.com/user/show 会被解析到名为 User 的控制器以及该控制器中定义的 show 方法。如果该方法没有定义,则默认转到 index 方法。</p>
<p><em><strong>注意:</strong></em>在代码中,控制器名的后面要加上 Controller,而动作名的后面要加上 Action。</p>
<p>编辑 application/controllers/IndexController.php 文件,输入:</p>
<pre class="programlisting"><span style="color: rgb(0, 0, 0);"><br /><span style="color: rgb(29, 83, 193);"><?php<br /></span><span style="color: rgb(123, 141, 154);">/** Zend_Controller_Action */<br /></span><span style="color: rgb(35, 45, 48);">require_once </span><span style="color: rgb(127, 30, 101);">'Zend/Controller/Action.php'</span><span style="color: rgb(35, 45, 48);">;<br /><br />class </span><span style="color: rgb(29, 83, 193);">IndexController </span><span style="color: rgb(35, 45, 48);">extends </span><span style="color: rgb(29, 83, 193);">Zend_Controller_Action<br /></span><span style="color: rgb(35, 45, 48);">{<br />    public function </span><span style="color: rgb(29, 83, 193);">indexAction</span><span style="color: rgb(35, 45, 48);">()<br />    {<br />    }<br />}</span><br /></span><br /></pre>
<p><strong>7. 视图(页面)脚本</strong></p>
<p>编辑 application/views/scripts/index/index.phtml,输入:</p>
<pre class="programlisting"><span style="color: rgb(0, 0, 0);"><!DOCTYPE html<br />PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"<br />"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><br /><html><br /><head><br />  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><br />  <title>My first Zend Framework App</title><br /></head><br /><body><br />    <h1>Hello, World!</h1><br /></body><br /></html></span></pre>
<p> </p>
<p><strong>8. 错误控制器</strong></p>
<p>默认情况下,Zend Framework 的错误处理插件是被注册的。它需要一个错误控制器来处理错误。缺省的错误控制处理被假定为 ErrorController 以及其中定义的 errorAction。</p>
<p>编辑 application/controllers/ErrorController.php,输入:</p>
<pre class="programlisting"><span style="color: rgb(0, 0, 0);"><br /><span style="color: rgb(29, 83, 193);"><?php<br /></span><span style="color: rgb(123, 141, 154);">/** Zend_Controller_Action */<br /></span><span style="color: rgb(35, 45, 48);">require_once </span><span style="color: rgb(127, 30, 101);">'Zend/Controller/Action.php'</span><span style="color: rgb(35, 45, 48);">;<br /><br />class </span><span style="color: rgb(29, 83, 193);">ErrorController </span><span style="color: rgb(35, 45, 48);">extends </span><span style="color: rgb(29, 83, 193);">Zend_Controller_Action<br /></span><span style="color: rgb(35, 45, 48);">{<br />    public function </span><span style="color: rgb(29, 83, 193);">errorAction</span><span style="color: rgb(35, 45, 48);">()<br />    {<br />    }<br />}</span><br /></span><br /></pre>
<p>下面是对应的视图脚本。编辑 application/views/scripts/error/error.phtml,输入:</p>
<pre class="programlisting"><span style="color: rgb(0, 0, 0);"><!DOCTYPE html<br />PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"<br />"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"><br /><html><br /><head><br />  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /><br />  <title>Error</title><br /></head><br /><body><br />    <h1>An error occurred</h1><br />    <p>An error occurred; please try again later.</p><br /></body><br /></html></span>

 

9. 运行

好,现在运行网站。在浏览器中键入下面三个地址,得到的结果应该是一样的——就是最最常见的“Hello, World!“。

  • http://域名
  • http://域名/index
  • http://域名/index/index

如果是这样,那么恭喜你!

A content management system(CMS) is a computer application used to create, edit, manage, and publish content in a consisitently organized fashion. CMSs are frequently used for storing,controlling,versioning, and publishing industry-specific documentation such as news articles,operators’ manuals, technical manuals,sales guides, and marketing brochures(小册子).The content managed may include computer files,image media, audio files, video files, electronic documents, and Web content.
A CMS may support the following features:
identification of all key users and their content management roles;
the ability to assign roles and responsibilities to different content categories or types;
definition of workflow tasks for collaborative creation, often coupled with event messaging so that content managers are alerted to changes in content(For example, a content creator submits a story, which is published only after the copy editor revises it and the editor-in-chief approves it.);
the ability to track and manage multiple versions of a single instance of content;
the ability to capture content(e.g. scanning);
the ability to publish the content to a repository to support access to the content(Increasingly(越来越多地), the repository is an inherent part of the sytem, and incorporates(包含,加上) enterprise search and retrieval.);