Phpmore Vol7

  • August 2019
  • PDF

This document was uploaded by user and they confirmed that they have the permission to share it. If you are author or own the copyright of this book, please report to us by using this DMCA report form. Report DMCA


Overview

Download & View Phpmore Vol7 as PDF for free.

More details

  • Words: 5,659
  • Pages: 68
PHP前沿Top10...............................................................................................................................4 PHP & memcached .....................................................................................................................7 一、memcached 简介.........................................................................................................7 二、memcached 安装.........................................................................................................7 三、运行 memcached 守护程序.......................................................................................8 四、memcached 的工作原理.............................................................................................8 五、PHP 如何作为 memcached 客户端 .........................................................................9 六、PHP memcached 应用示例 .......................................................................................9 七、相关资源.........................................................................................................................11 使用 memcached 实现对象的自动缓存.................................................................................12 0. 前言...................................................................................................................................12 1. memcached ...................................................................................................................12 2. __destruct() ..................................................................................................................13 3. 示范...................................................................................................................................13 4. 缓存对象与使用 session 的区别 ................................................................................16 5. 总结...................................................................................................................................16 6. 示例代码...........................................................................................................................17 XCache:加速你的PHP ..............................................................................................................19 什么是XCache......................................................................................................................19 安装 XCache 扩展模块......................................................................................................19 使用操作系统包管理器安装.........................................................................................19 手工编译源代码安装(*nix).........................................................................................19 配置 ini 启用........................................................................................................................20 安装监控器.............................................................................................................................20 加速原理.................................................................................................................................20 性能.........................................................................................................................................21 特色.........................................................................................................................................21 运用 XCache 的缓冲 API .........................................................................................22 运用 XCache 的缓冲 API 之二:简单计数器 .......................................................23 编写 XCache 的心得..........................................................................................................24 引用与参考.............................................................................................................................24 扩展PHP.........................................................................................................................................26 为什么要扩展.........................................................................................................................26 写扩展的基础.........................................................................................................................26 什么是扩展.............................................................................................................................27 开发环境的搭建.....................................................................................................................27 编码实现.................................................................................................................................32 Mysql 集群技术简介 ...................................................................................................................37 一、MySQL Cluster概述 ...................................................................................................37 二、MySQL Cluster 基本概念 .........................................................................................38 三、开始准备.........................................................................................................................39 1、准备服务器 ..............................................................................................................39 2、注意事项及其他 ......................................................................................................40

1/65

四、开始安装.........................................................................................................................40 1、假定条件 ..................................................................................................................40 2、SQL节点和存储节点(NDB节点)安装(即 4 个机器重复执行以下步骤) ..........40 3、配置SQL节点 ..........................................................................................................40 4、配置存储节点(NDB节点)......................................................................................41 5、安装管理节点 ..........................................................................................................41 6、配置管理节点 ..........................................................................................................41 五、启动MySQL Cluster ...................................................................................................42 六、创建数据库表.................................................................................................................44 七、安全关闭.........................................................................................................................44 八、其他.................................................................................................................................45 LAMP系统优化实例分析 ..............................................................................................................46 一,php本身的优化 .............................................................................................................46 二, web服务器优化..............................................................................................................47 a,关闭apache的访问日志. .........................................................................................47 b, Timeout 和 KeepAlive参数................................................................................48 c,MPM参数设置 ............................................................................................................48 d, 压缩输出页面,提高系统吞吐量 ...........................................................................48 e,删除不必要的Alias和Directory参数 .....................................................................49 f,其他参数调整 ..............................................................................................................50 三,Linux服务器调整 ............................................................................................................50 a,使用tmpfs .................................................................................................................50 b,tcp优化.......................................................................................................................51 c,定时监控web服务器状态,负载过大时自动重启web服务器................................51 四,light httpd + fastcgi + php ...................................................................................52 a,编译fastcgi的php ....................................................................................................52 b,编译安装lighthttpd .................................................................................................53 c,配置light http...........................................................................................................53 d,启动和关闭light httpd ............................................................................................54 e,注意事项: ...................................................................................................................54 Subversion PHP Binding.........................................................................................................57 什么是Subversion? ..........................................................................................................57 谁在使用Subversion? ......................................................................................................57 为什么将Subversion和PHP Binding(捆绑)起来? ..................................................58 3.1 Subversion采用C/S架构的缺点 .......................................................................58 3.2 Subversion采用B/S架构的优点 .......................................................................59 如何使用Subversion PHP Binding? .............................................................................61 4.1 如何配置php-svn for Windows?..................................................................61 4.2 如何使用Subversion PHP Binding? ............................................................62 Subversion PHP Binding的函数API ..............................................................................64 a) 以svn_auth_set_parameter认证举例: ........................................................64 b) 以svn_diff版本差异举例:...................................................................................64 总结.........................................................................................................................................64

2/65

PHP&MORE Vol7 编辑: Avenger Binzy Easy Freeman Haohappy KnightE Nio RainX Shenkong Vicki

出品:

鸣谢:

. 感谢所有关心和支持 PHPMORE 的人,“感谢你们让我们梦想”。

卷首语 《PHP&MORE》又和大家见面啦。最近 More 编辑部发生了很多新的变化。首先是 More 终于有了自己的社 区,新版的 PHPMORE.com 上线了,PHPMore 社区致力于提供知识的分享和聚合服务,帮助大家获取新知 识、认识新朋友。当然,新版社区也有很多需要改进的地方,希望大家多提意见。然后是 More 在十月份 举办的国庆征文活动,在大家的积极响应和支持下圆满结束,但是在线投稿的人数不够多,投票人数未超 过十人,所以我们决定把在线投稿的奖项和邮件投稿一起发布,经过我们的评比,本次活动的获奖文章如 下 一等奖 《使用 memcached 实现对象的自动缓存》《PHP&Memcached》 二等奖 《XCache:加速你的 PHP》《LAMP 系统优化实例分析》 三等奖 《Mysql 集群》《扩展 PHP》 请文章作者赶快到 post.phpmore.com 上,选好自己想要的礼物,然后和自己地址、邮编、联系方式一 起发送给 [email protected],我们将送出总价值近千元的礼物。 另外,参与本次活动的前九位作者,可以得到 VeryCD 提供的电骡抱枕一个 :P 最后,More 祝大家快乐工作、开心编码,有空了多陪家人,有钱了多给老婆,有文章了多想着点 More:)

PHPMORE 编辑部 2006-12-5

3/65

PHP 前沿 Top10 Easy,Haohappy/文

我们很欣喜地看到 PHP 一直在前进。PHP 5.2.0 的发布,带来了新的输入 过滤扩展和提供 AJAX 支持的 JSON 扩展。PHP5.2.0 也在性能方面有了更 好的表现,同时修正了大量 BUG,推荐升级。 关于Input Filter扩展:PHP 已经自带有 Filter 了,之前是需要自己 手动安装扩展,现在是默认就提供了。手册中也已经提供了 Filter 函数 库的相关函数说明。Zend Developer Zone 上也已经有了一篇相关的教 程--PHP Built in Input filtering。 PHP是为WEB而生的一种编程语言,但是PHP的桌面应用也很有意思。虽然我 们现在还看不出其是否有应用前景(像Java Swing那么复杂和低效的界面 设计,不是一样有人用吗,谁能肯定地说PHP用户界面将来没有前途呢) 。如 果你需要开发C/S架构的程序,不妨看看,PHP GTK的发展还是相当快的。 http://www.php-gtk.eu/ 上面有很多PHP实现的漂亮的用户界面。 你也可以看看 Callicore(http://callicore.net/),一个 PHP-GTK2 的开发框架。 也许将来“软件即服务”的理念和 SOA 编程架构会更加深入人心。IBM 发起 的 PHP-SOA 项目为 PHP 在 SOA 方面的应用敞开了大门。该项目包含了 SDO 和 SCA 两部份。11 月底,IBM 将 1.1.0 Stable 版本的 SCA_SDO 通过 PHP PECL 发布。 http://pecl.php.net/package/SCA_SDO PHP 下的单元测试工具。3.0 版主要增加了 Mock Objects、Selenium Integration 和 Code-Coverage 等 3 种测试方式。 ChangeLog:http://www.phpunit.de/wiki/ChangeLog Bambalam PHP EXE 编译器/打包器是用于将 PHP 程序转化为独立的 Windows .exe 程序的免费命令行工具。生成的 exe 文件是完全独立的, 不需要任何 php 动态连接库。这使得 php 代码使用 Turck MMCache Encode 进行了编码,所以如果你想分发你的程序同时保护你的源代码,这 是最好的解决方法。 优秀的 PHP 加速器 eAccelerator0.9.5 发布,对 cache 目录下的文件进 行了 hash 分级,可以更好的支持大规模应用。 Zend Framework 是由 Zend 和 IBM 等公司发起的 PHP5 开发框架。由于 Zend 和 IBM 的号召力,再加上团队开发平台相当完善,越来越多 PHP 程序 员参与其中,使得 ZF 的开发社区呈现出一片欣欣向荣的景象。大批高水平 的 PHPER 的参与使 ZF 成为 CakePHP 和 Symphony 之外最受关注、最有前 途的 PHP5 开发框架。IBM 的 QEDWiki(即将发布)就是采用了 Zend Framework。

4/65

ZF 最近发布了 0.2.0 预览版,大量与 WEB2.0 和 WEB Services 相关的 类库已经推出或正在开发当中,让人感觉 PHP 紧随互联网的发展而发展,真 正做到了与时俱进(哈,真像口号,下一步应该是构建 PHP 和谐社区 --Haohappy)。 Zend Framework 手册目前已经被翻译成 10 个国家的语言版本,简体中文 版由《PHP&More》编辑 Haohappy 组织翻译,目前已经有十一位 PHP 程序 员参与并完成了首轮翻译。但是 ZF 的发展速度实在太快,现在还有不少新 增的内容无人认领,欢迎大家踊跃参加。 Symfony 堪称现在最成熟的 PHP5 开发框架之一,其中涵盖很多独特的设计 思想和组件。Symfony 开发团队认为该框架已经完全可以用于企业级开发 (enterprise ready),包括 Yahoo! Bookmarks 在内的很多程序都使用了 Symfony 框架。Symfony 其实也才诞生一年出头,最近终于发布了 1.0.0 版本,虽然暂时只是 Beta 版本,但其团队声称不久之后将会发布 1.0 Stable,到时 Symfony 会更加成熟和稳定。 Hardened PHP(http://www.hardened-php.net)是一个研究 PHP 安全的专业团队,一直致力于发现和修正各种 PHP 安全漏洞,在业界相当有 名。他们最近发布了 Suhosin,一个用于保护 PHP 和 PHP 程序的强大的工 具包。

Xdebug

Xdebug 是一个 PHP 程序调试和性能剖析工具,可用来调试程序运行和分析 程序性能瓶颈。Xdebug 最近发布了 2.0.0 版,支持最新版本 PHP5.2.0, 提供了改进后的界面并修复了不少 BUG。http://xdebug.org/

5/65

专题导读 Easy/文 就像巴士大叔陈乙东说的,“我有压力,你有压力”,这个世界充满了压力。在这个一不小心就出名的 web2.0 时代,如何使你的系统能轻松运转,从容应对突如其来的访问压力呢?这就是我们本期讨论的专题,高压 力下的 LAMP 系统优化。

上边的图描述了当用户通过浏览器访问我们的服务器的情景。首先请求被发送到服务器,透过 tcp 协议访 问到 Apache 进程,Apache 从文件系统加载 PHP 代码后,进行解析,并运行其命令,可能的操作是查询 数据库得到数据,然后生成 html 返回给浏览器。 在这个过程中,最容易形成瓶颈的,就是 PHP 查询数据库并返回结果的部分,除了对 SQL 查询语句进行优 化,对数据表添加必要的索引外,将对数据库的查询缓存起来也是一个很有效的方法。本期中的《PHP & memcached》和《使用 memcached 实现对象的自动缓存》将告诉你如何将数据和对象缓存在 memcached 中,从而有效的降低数据库的压力。( 图中 1,2 ) 由于 PHP 是解释型语言,在执行效率上会不可避免的偏低,本期中的《XCache》和《扩展 PHP》将为 PHP 的解释特性带来的性能问题提供解决方案。Xcache 是我们国人自己开发的,在性能和稳定性上决不输给 APC 等同类软件,作者在文章中除了介绍 XCache 的原理和使用外,还和大家分享了他开发 XCache 的经 验。除了将 PHP 编译后的 opcode 进行缓存外,将公用模块写成 PHP Module 也可以极大的提高性能, 《扩展 PHP》将带你起步。( 图中 3,4 ) Mysql 是我们最常用的数据库,《Mysql 集群简介》是 imysql.cn 带给我们的最新的 Mysql 集群技术, 当你将各村的 Mysql 连成一片的时候,就会取得像《地道战》般的神奇胜利了。( 图中 5 ) 除了理论外,我们还邀请到了给 PPlive 编写社区程序的 Dalamar,让他给大家讲讲他怎么用一台服务器 抵抗三百万 PV 的实战经历。那么,让我们开始吧,Show Time 。

6/65

PHP & memcached Nio/文

一、memcached 简介 在很多场合,我们都会听到 memcached 这个名字,但很多同学只是听过,并没有用过或实际了解过,只 知道它是一个很不错的东东。这里简单介绍一下,memcached 是高效、快速的分布式内存对象缓存系统, 主要用于加速 WEB 动态应用程序。

二、memcached 安装 首先是下载

memcached 了 , 目 前 最 新 版 本 是

1.1.12 , 直 接 从 官 方 网 站 即 可 下 载 到

memcached-1.1.12.tar.gz 。 除 此 之 外 , memcached 用 到 了

libevent , 我 下 载 的 是

libevent-1.1a.tar.gz。 接下来是分别将 libevent-1.1a.tar.gz 和 memcached-1.1.12.tar.gz 解开包、编译、安装:

- PHPMORE # tar -xzf libevent-1.1a.tar.gz # cd libevent-1.1a # ./configure --prefix=/usr # make # make install # cd .. # tar -xzf memcached-1.1.12.tar.gz # cd memcached-1.1.12 # ./configure --prefix=/usr # make # make install 安装完成之后,memcached 应该在 /usr/bin/memcached。

7/65

三、运行 memcached 守护程序 运行 memcached 守护程序很简单,只需一个命令行即可,不需要修改任何配置文件(也没有配置文件给 你修改

):

- PHPMORE /usr/bin/memcached -d -m 128 -l 192.168.1.1 -p 11211 -u httpd 参数解释: - PHPMORE -d 以守护程序(daemon)方式运行 memcached; -m 设置 memcached 可以使用的内存大小,单位为 M; -l 设置监听的 IP 地址,如果是本机的话,通常可以不设置此参数; -p 设置监听的端口,默认为 11211,所以也可以不设置此参数; -u 指定用户,如果当前为 root 的话,需要使用此参数指定用户。 当然,还有其它参数可以用,man memcached 一下就可以看到了。

四、memcached 的工作原理 首先 memcached 是以守护程序方式运行于一个或多个服务器中,随时接受客户端的连接操作,客户端可 以由各种语言编写,目前已知的客户端 API 包括 Perl/PHP/Python/Ruby/Java/C#/C 等等。PHP 等 客户端在与 memcached 服务建立连接之后,接下来的事情就是存取对象了,每个被存取的对象都有一个 唯一的标识符 key,存取操作均通过这个 key 进行,保存到 memcached 中的对象实际上是放置内存中 的,并不是保存在 cache 文件中的,这也是为什么 memcached 能够如此高效快速的原因。注意,这些 对象并不是持久的,服务停止之后,里边的数据就会丢失。

8/65

五、PHP 如何作为 memcached 客户端 有两种方法可以使 PHP 作为 memcached 客户端,调用 memcached 的服务进行对象存取操作。 第一种,PHP 有一个叫做 memcache 的扩展,Linux 下编译时需要带上 –enable-memcache[=DIR] 选项,Window 下则在 php.ini 中去掉 php_memcache.dll 前边的注释符,使其可用。 除此之外,还有一种方法,可以避开扩展、重新编译所带来的麻烦,那就是直接使用 php-memcached-client。 本文选用第二种方式,虽然效率会比扩展库稍差一些,但问题不大。

六、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’ 的对象数据进行存取操作:

- PHPMORE // 包含 memcached 类文件 require_once(‘memcached-client.php’); // 选项设置 $options = array( ’servers’ => array(‘192.168.1.1:11211′), //memcached 服务的地址、 端口,可用多个数组元素表示多个 memcached 服务 ‘debug’ => true, //是否打开 debug

9/65

‘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); 是不是很简单,在实际应用中,通常会把数据库查询的结果集保存到 memcached 中,下次访问时直接从 memcached 中获取,而不再做数据库查询操作,这样可以在很大程度上减轻数据库的负担。通常会将 SQL 语句 md5() 之后的值作为唯一标识符 key。下边是一个利用 memcached 来缓存数据库查询结果集的 示例(此代码片段紧接上边的示例代码):

- 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);

10/65

} else { echo “n”.str_pad(‘Read datas from memcached.’, 60, ‘_’).“n”; } var_dump($datas);

可以看出,使用 memcached 之后,可以减少数据库连接、查询操作,数据库负载下来了,脚本的运行速 度也提高了。 之前我曾经写过一篇名为《PHP 实现多服务器共享 SESSION 数据》文章,文中的 SESSION 是使用数 据库保存的,在并发访问量大的时候,服务器的负载会很大,经常会超出 MySQL 最大连接数,利用 memcached,我们可以很好地解决这个问题,工作原理如下: 用户访问网页时,查看 memcached 中是否有当前用户的 SESSION 数据,使用 session_id() 作为 唯一标识符;如果数据存在,则直接返回,如果不存在,再进行数据库连接,获取 SESSION 数据,并将 此数据保存到 memcached 中,供下次使用; 当前的 PHP 运行结束(或使用了 session_write_close())时,会调用 My_Sess::write() 方法, 将数据写入数据库,这样的话,每次仍然会有数据库操作,对于这个方法,也需要进行优化。使用一个全 局变量,记录用户进入页面时的 SESSION 数据,然后在 write() 方法内比较此数据与想要写入的 SESSION 数据是否相同,不同才进行数据库连接、写入数据库,同时将 memcached 中对应的对象删除, 如果相同的话,则表示 SESSION 数据未改变,那么就可以不做任何操作,直接返回了; 那么用户 SESSION 过期时间怎么解决呢?记得 memcached 的 add() 方法有个过期时间参数 $exp 吗?把这个参数值设置成小于 SESSION 最大存活时间即可。另外别忘了给那些一直在线的用户延续 SESSION 时长,这个可以在 write() 方法中解决,通过判断时间,符合条件则更新数据库数据。

七、相关资源 memcached 官方网站 PHP memcached client 下载 memcached-client.php - PHPMORE nio msn [email protected] blog http://nio.infor96.com

11/65

使用 memcached 实现对象的自动缓存 cid73/文

0. 前言 本文将探讨如何利用 memcached 实现对象的自动缓存. 也就是说, 当一个对象的状态发生改变, 不需 要其它手段, 这个对象将被自动缓存, 不论用户访问什么页面, 当调用同一个对象时, 这个对象将带着 上一次被访问时的状态, 而这一切是自动发生的. 实际上本文披露的对象缓存技术并不局限于使用 memcached, 同样适用于其它对象缓存方式如文本或数据库. 也可以说是同时在探讨两种技术. 范例环境要求: - PHPMORE memcached PHP5 PHP5 的 memcache 扩展

1. memcached memcached 是一种分布式内存缓存系统, 最初由 Danga Interactive 公司开发, 用于维护它的 LiveJournal 服务. 它被用于加速由数据库驱动的动态网站, 它可以在内存中缓存数据或对象, 以此 降低对数据库的频繁读取. memcached 是开源软件, 以 BSD 授权方式发放. memcached 执行缓存时就像一个巨大的哈希表, 就如 PHP 用户所熟悉的关联数组. 我们可以想象有一 个巨大的关联数组长驻留在内存中, 通过 PHP 的 memcache 扩展, 我们可以随时向这个数组存入或取 出数据. 与共享内存不同的是, memcached 可以在一台机器上部属多个服务实例, 也可以把服务部属到 多台机器上, 这样多台机器上的应用程序也可以使用同一台机器的 memcached 服务. 虽然 memcached 被设计用于缓存数据库查询, 但是它并不限定被缓存的数据类型, 所以当然它也可以 用于缓存对象. 当 我 们 要 缓 存 一 个 对 象 时 , 不 可 避 免 地 必 须 先 将 对 象 序 列 化 , memcached 也 不 例 外 . 当 我 们 用 Memcache::set() 储存对象时实际上对象被序列化为字符串存入 memcached 服务的"哈希表"中; 当我们用 Memcache::get() 获取一个对象时, memcache 扩展将在 memcached 服务的"哈希表"中 读取出来的数据进行反序列化后返回. 如此说来, 用 memcached 缓存和普通的序列化并没有什么不同, 只是普通的序列化会储存在硬盘上,

12/65

而 memcached 储存在内存中. 但是, 通常对对象执行序列化的步骤是发生在对象外部, 而我们要达到的目的是: 不需要额外步骤, 当 一个类被指定为"有状态"时, 其实例的状态会被缓存服务保持着, 在缓存没有失效之前, 不论在那里调 用这个类, 被实例化的对象都会带着先前的状态. 这样的目的可以达到吗?

2. __destruct() 上面说过, 我们需要自动缓存对象而不是显式地调用某个函数, 告诉它: 嘿, 把这个对象放入内存. 那 么, 何谓自动? 何时才是自动执行缓存的最佳时机? 我们也可以这样想, 对象的状态什么时候才会固定 下来不再改变. 答案是当脚本结束的时候. PHP 5 引入了析构函数的概念, 析构函数会在到某个对象的所有引用都被删除或者当对象被显式销毁时 执行. 那么对一个对象的引用何时才全部被消除? 答案正是----当脚本结束的时候. 当我们在类中声明了析构函数, 脚本结束时这个函数会自动被执行. 利用这个特性, 我们可以在析构函 数中加入定义: 当脚本结束时, 把实例对象储存到内存中去.

3. 示范 通过上面的分析, 我们的动机和解决方案开始清晰起来了: 要使对象被自动缓存, 我们可以在类中加入 析构函数, 在脚本结束时调用 memcached 把对象放入内存中. 那接下来这一部分我要来一个简单示范, 看看这个解决方案能否帮我们达到目的. 将要示范的程序叫做"我知道你的名字", 开始时程序会要求你输入你的名字, 输入名字后每次访问页面 程序都会和你打招呼. 为了方便测试, 我把缓存的时效设为 1 分钟, 1 分钟内如果你没有重新输入名字 或者刷新页面, 过 1 分钟再访问程序会再次要求你输入名字.

"我知道你的名字"的核心逻辑非常简单:

- PHPMORE class IGotYourName { ... private $name; private function __construct() {}

13/65

public function setName($name) { $this->name = $name; } public function getName() { return $this->name; } 这个类的构造函数是私有的, 因为我把它设计为单体类, 目的是不想客户代码使用 new 去实例化它. 事 实上被定义为"有状态"的类都不应该用 new 去实例化, 否则你必须在构造函数中获取被缓存的对象, 再迭代它为自身的属性赋值, 这样非常麻烦. 这并不意味着要求所有"有状态"的类都是单体类, 但对象 的实例化应该委托其它机制(比如容器)去完成. 获取对象的方法和普通的单体类获取方法有些不同:

- PHPMORE public static function getInstance($sessionId) { self::$instanceId = __CLASS__ . '_' . $sessionId; if ( empty(self::$cacheServer) ) { self::$cacheServer = new Memcache; self::$cacheServer->connect('localhost', 11211); } if ( empty(self::$instance) ) { $cachedInstance = self::$cacheServer->get(self::$instanceId); if ( !empty($cachedInstance) ) { self::$instance = $cachedInstance; } else { self::$instance = new IGotYourName; } } return self::$instance; } 有趣的地方就在于, 如果我们在 cache 中检索到已经缓存的对象, 就直接返回它, 检索不到的话就 new 一个. 程序会被多人使用, 因此我们需要一个唯一的 $sessionId, 用来区分属于不同用户的实例; 为防止和其它类型的对象搞浑, 我加上类名做标识, $instanceId 就是对象在 memcached 中的唯一 标记. cache 服务其实比较简单, 直接调用 Memcache 就可以. 因为我要在一个类中示范, 所以在这 里聚合了 Memcache, 实际应用中应该把这部分抽离出来, 实现一个完整的 CacheServer. 现在来看析构函数

- PHPMORE

14/65

public function __destruct() { self::$cacheServer->set(self::$instanceId, self::$instance, 0, 60); }

这里就是实现自动缓存的关键, 当脚本结束时对象会被缓存到内存中, 时效 60 秒. 由于篇幅所限, 我把所有功能集中到一个类中, 包括容器服务和缓存服务. 但实际应用中为每一个类实 现这样的服务是不必要的, 应该把容器服务和缓存服务独立出来, 而需要被缓存的类则其它普通类一模 一样, 根本不需要析构函数, 只有当你通过容器服务去实例化它才会被缓存. 但是客户代码不能直接实 例化对象, 而是要向容器请求对象, 通过容器去处理对象的实例化. 容器本身被实例化时则已经会从缓 存服务中载入了一组对象等待调用(检索不到需要的对象则执行实例化), 而析构函数被设在容器中, 当 脚本结束时容器会把这组对象送给缓存服务储存起来等待下一次调用. 客户代码如下

- PHPMORE $iGotYourName = IGotYourName::getInstance($sessionId); if ( !empty($_POST['name']) ) { $iGotYourName->setName($_POST['name']); header('Location: ' . $_SERVER['PHP_SELF']); } 如果用户提交了名字, 那么在执行了 $iGotYourName->setName($_POST['name']); 之后, 对象 $iGotYourName 的状态就改变了, 跳转回页面之后应该出现问候语. 看一下效果, 我们终于得偿所愿.(全部代码附在篇末)

15/65

4. 缓存对象与使用 session 的区别 看了上面的示例, 可能有人会说, 使用 session 储存 $_POST['name'] 也可以啊, 没看出有什么不 同. 事实上两者有很大的不同. session 是基于 Web 的, 它维护的是终端用户的数据, 而缓存对象维护的 不一定的终端用户的数据(虽然上面的示例确实可以代替 session). 比方说, 各种配置文件, 它们可 以封装成类, 长期驻留在内存中候命, 后台控制程序可以通过容器调用它们进行修改, 然后继续放入内 存(需要同时修改配置文件); 数据库持久层对象, 它们和终端用户一点儿关系没有, 缓存它们可以大幅 减少查询数据库的次数(实体对象的缓存和会话对象有些不同, 如果下次 PHPMore 开数据库持久层专题 我们再探讨). 其次, session 和对象缓存是完全不同的概念, 通过构造各种容器, 可以实现类似 Java EJB 容器的架构, 从而应用各种先进的开发理论.

5. 总结 memcached 是一个巨大的哈希表 使用 __destruct() 析构函数自动缓存对象 需要被缓存的对象不能直接用 new 实例化, 需要通过第三方机制去获取 被缓存的对象需要一个独立的标识符 独立出容器服务和缓存服务, 缓存服务通常只需要一个, 容器服务会有多种类型 缓存对象和 session 是两回事, 可以使用前者来代替后者, 从而使程序更加结构化

16/65

6. 示例代码 - PHPMORE function generateSessionId() { $chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz'; $sessionId = ''; for ( $i = 0; $i < 32; $i++ ) { $sessionId .= $chars{mt_rand(0, 61)}; } return $sessionId; } class IGotYourName { private static $instance; private static $instanceId; private static $cacheServer; public static function getInstance($sessionId) { self::$instanceId = __CLASS__ . '_' . $sessionId; if ( empty(self::$cacheServer) ) { self::$cacheServer = new Memcache; self::$cacheServer->connect('localhost', 11211); } if ( empty(self::$instance) ) { $cachedInstance = self::$cacheServer->get(self::$instanceId); if ( !empty($cachedInstance) ) { self::$instance = $cachedInstance; } else { self::$instance = new IGotYourName; } } return self::$instance; } private $name; private function __construct() {} public function setName($name) { $this->name = $name;

17/65

} public function getName() { return $this->name; } public function __destruct() { self::$cacheServer->set(self::$instanceId, self::$instance, 0, 60); } } if ( !empty($_COOKIE['IGotYourName_id']) ) { $sessionId = $_COOKIE['IGotYourName_id']; setcookie('IGotYourName_id', $_COOKIE['IGotYourName_id']); } else { $sessionId = generateSessionId(); setcookie('IGotYourName_id', $sessionId); } $iGotYourName = IGotYourName::getInstance($sessionId); if ( !empty($_POST['name']) ) { $iGotYourName->setName($_POST['name']); header('Location: ' . $_SERVER['PHP_SELF']); }

if ( $yourName = $iGotYourName->getName() ): Hello, echo $yourName , I got your name ;-) else: What's Your name, please? endif;



18/65

XCache:加速你的 PHP Static/文 作者按:由于工作的关系,截稿前几天才下笔开始写,时间仓卒了点儿,因而本文尽量采用简单容易理解 而又简短的文字向读者介绍 XCache 以及本人开发 XCache 的心得。也希望借此能够激起大家对 PHP 缓 冲器、PHP 扩展模块的兴趣,起到抛砖引玉的作用。有兴趣的朋友可以浏览一下 XCache 的网站[6]以 及在论坛[7]中一起讨论(如果 E-mail 的话别人就看不到了)。

什么是 XCache XCache 与 APC/eaccelerator/mmcache 等软件一样是 PHP 的编译缓冲器。能缓冲 PHP 的脚 本引擎 (ZendEngine) 编译所产生的 opcode 代码,提高 PHP 减少编译时间从而提高处理页面请求的 速度。

安装 XCache 扩展模块 使用操作系统包管理器安装 使用包管理器来安装、维护是推荐的方式,能减少很多头疼的操作。在 Gentoo 上安装的话,如果 您是 Gentoo 熟手,安装 XCache 相当简单。由于尚未进入 Gentoo 官方 Portage,您需要依照 Portage Overlay [4] 的方式引入非官方 XCache ebuild [5],然后执行 emerge –u xcache 即 可安装完毕。Debian 下则需要使用官方 sid 源并执行 apt-get install xcache。FreeBSD 可以 更新到官方最新 port 安装 xcache。这些操作都假设您已经熟悉响应操作系统发行版本的包管理器,因 此不再详细赘述。

手工编译源代码安装(*nix) ~/src $ wget http://... (发行版地址) ~/src $ tar -zxf xcache-*.tar.gz ~/src $ cd xcache ~/src/xcache $ phpize ~/src/xcache $ ./configure --enable-xcache --enable-xcache-coverager ~/src/xcache $ make ~/src/xcache $ su ~/src/xcache # make install ~/src/xcache # cat xcache.ini >> /etc/php.ini

19/65

~/src/xcache # $EDITOR /etc/php.ini

配置 ini 启用 要启用 XCache 需要修改 php.ini 配置。 由于每个发行版本可能指定不同的默认 php.ini 路径。因此首先要找到 php.ini 在哪里,通常 他在 /etc/php.ini /etc/php/apache2/php.ini /etc/php/cgi/php.ini 等地方,不过最简 单的办法就是使用 phpinfo() 来查看。 找到 php.ini 之后,参考源代码里的 xcache.ini,或者这个文件已经由包管理器安装在系统内。 将 xcache.ini 的内容放到 php.ini 内进行适当修改。主要修改 zend_extension 所指路径以及 xcache.size xcache.var_size 的大小。

安装监控器 即时配置启用了 XCache 可能您还担心到底是否生效了,可以在 phpinfo() 页面查看配置状态, 不过如果使用 XCache 自带的“XCache Administration”则更能使您对详细的缓冲状态了如指掌。 将 xcache/admin 目录整个拷贝到 web htdocs 目录下,与你以前安装的论坛、phpMyAdmin、 CMS 等软件类似。不过您需要修改 php.ini 配置 XCache 管理密码。密码需要经过 md5 生成。 自己写一个 md5pass.php: 运行 php md5pass.php 或者直接使用 shell 下的工具(不过需要注意清理 ~/.bash_history): ~/src $ echo -n "test" | md5sum 修改 ini 文件: xcache.admin.user = "mOo" xcache.admin.pass = "098f6bcd4621d373cade4e832627b4f6" 重启您的 php(或者 webserver),打开浏览器登录即可。有中文支持哦 ☺

加速原理 XCache 是如何实现加速的呢?这里先提一些相关的 PHP 历史。 最初 PHP 由 rasmus 创建,采用“解释执行”方式。后来由 Zeev/Andi 开发的 ZendEngine 引 擎,此时版本升级为 PHP4。ZendEngine 采用将源代码编译成 opcode 代码的方式,避免提重复解释 代码从而高性能。例如:
for ($i = 0; $i < 100; $i ++) { $a = $a + 1;

20/65

}

?> 以上代码在 PHP4 之前,for 循环语句以及其内部语句均需要解释 10 次运行 10 次。而在 PHP4 后则只需要编译 1 次运行 10 次。 即使如此,PHP4 在脚本运行结束之后会将 opcode 代码抛弃,因为引擎并没有实现对这些代码管 理的功能,因而每个新的请求引擎都会重新编译脚本,这将花费相当的时间。XCache 正是对这些 opcode 代码进行管理来达到加速的目的。第一次请求使用某个 PHP 脚本时 XCache 发现缓冲内并没有 opcode 代码,就调用 PHP 引擎原有的编译功能对脚本进行编译并将编译生成的 opcode 拷贝入缓冲内。后续的 请求则可以直接从缓冲内提取而避免重复编译。 XCache 之前:php XCache

zend_compile_file(频繁被调用)

之 后 : php

xcache_compile_file( 频 繁 存 取 缓 存 区 )

zend_compile_file(极少被调用) 显然,您将需要耗费额外的内存来存储这些 opcode 代码。为了节省空间,XCache 对不同进程采 用同一块物理内存来存储,即所谓的共享内存,并且避免多个进程同时操作共享内存导致混乱因而又引入 lock 的机制。

性能 了解了这么多,可是她性能到底如何?在Sascha A. Carlin 的“PHP on Fire: Five Opcode Caches compared.”[1] 的测试可以看到,XCache 的性能相当优秀,可以与同类软件媲美,他们对实 际运用程序的性能均能提高约 100%。要注意的是 XCache 在 SMP/并发请求下似乎更体现出优势。也许 您在另一份环境下做 benchmark 会跟这个结果有出入,这通常是正常现象,除非同一个环境下性能比其 他加速器明显低很多。☺ 该测试结果中 hello world 程序速度看起来并没有提高那么多,这可能是因为慢在 lock 上: XCache 采用最稳定可靠的 flock/fcntl 来实现 lock。这并不是说其他 lock 机制本身会出错、不 稳定、异常,而是在 PHP 这个强大而又复杂的脚本引擎、语言环境下,难免会出现各种异常情况,这包 括段错误、timeout 等各种 zend_bailout、进程被 kill 等情况,一旦发生则可能已经 lock 了却 没有 unlock。想象一下所有其他 PHP 进程都在等待这个 lock 而永远等不到会是什么情形。PHP group 迟迟未能保证 PHP 多线程版本的稳定性也是类似的道理:一个 PHP 请求死掉问题不大,但是如 果因此影响其他请求的话影响就很恶劣。 实际上在 XCache 之前作者已经大量阅读 phpa/apc/mmcache 的代码,并尝试使用他们。 apc/mmcache 选择使用 fcntl/flock 会相对稳定不少。不幸的是他们都更乐于声称自己性能更高, 而不是更稳定。

特色 既然稳定性这么重要,那么到底 XCache 如何保证稳定性?XCache 里采用了各种措施,这也正是 她的特色之一:XCache 使用了代码生成机制。采用机械生成代码能够尽量降低人为编码时发生的错误, 利用 awk/m4 [8] 等脚本自动从当前版本的 PHP 头文件里提取关键的信息,在编译、测试运行(需要指 定 --enable-xcache-test 编译后运行)期间能够识别出正在编译、测试的 XCache 版本是否与系统

21/65

的 PHP 兼容性。这些机制主要在开发 XCache 的时候发回重要的作用,它曾经发现过一些问题。这个机 制同时也协助作者最快地发现与新版本 PHP 的兼容问题并开发对其进行支持。这些也是为什么 XCache 轻松支持正在开发中的 PHP6 unicode 的原因。 采用 DisableOnFailure 机制。一旦发现缓冲区内存被损坏等情况导致 crash,则自动禁止 XCache 自己,这个 FailSafe 方式比不断的 dump core 文件要好得多。 采用 svn 对进行代码维护,实现多分支并行开发。稳定版本更成熟,发布极其谨慎,只修复严重问 题并避免进行大的手术;而开发版本的功能不断丰富,发布迅速。 SplitedCache 机制将缓存区切分开,在不增加额外内存需求的情况下,允许同一时间分别存取不 同的缓存区,这在 SMP 下性能进一步提高性能 [2]。 从最初就考虑对多线程、Windows 环境的支持,这种支持根基很重要,比起半路起家只能不断修复 这类 Bug 的软件,效果要好很多。如果缺乏这种根基,并不直接引发一个或者多个某个明显问题的出现, 而是导致在一些高压环境下出现稳定性问题而可能开发人员迟迟无法知道问题所在。 XCache 还提供了一些缓冲 API 给 PHP 程序使用,下面我们看看如何运用这些 API。

运用 XCache 的缓冲 API XCache 额外提供了一些缓冲数据的 API 函数给程序调用,程序员可以编写代码利用这些 API 来 存取共享内存,实现自己的高速缓冲器。这些函数包括: mixed xcache_get(string name) bool xcache_set(string name, mixed value [, int ttl]) bool xcache_isset(string name) bool xcache_unset(string name) int xcache_inc(string name [, int value [, int ttl]]) int xcache_dec(string name [, int value [, int ttl]]) 示范代码:
function call_cached_func_array($key, $callback, $arguments = array(), $ttl = 0) { if (xcache_isset($key)) { return xcache_get($key); } $data = call_user_func_array($callback, $arguments); xcache_get($key, $data, $ttl); return $data; }

?> 使用方法:
22/65

function my_load_blog_titles() { $db = new DB(); $db->query(...); .... return $rows; }

$tpl = new Smarty(); $tpl->assign('blogtitles', call_cached_func_array('blogtitles', 'my_load_blog_titles')); $tpl->display('index.tpl');

?> 这 个 示 范 代 码 利 用 XCache 的 API 实 现 了 简 单 而 方 便 的 缓 存 机 制 。 不 过 在 并 发 情 况 下 , call_cached_func_array 的实现机制会导致 my_load_blog_titles 在 reload 的时候出现性能 瓶颈。由于内容的关系,笔者日后将在另一篇文章内单独阐述。 btw: 有人反映,使用 XCache 来缓存数据要比 memcache 的快一些。不过如果要分布式缓存的话 就没戏了 ☺

运用 XCache 的缓冲 API 之二:简单计数器 示范代码:
if (!xcache_inc("blogvisits")) { // 从数据库载入 $count xcache_set("blogvisits", $count); } $count = xcache_inc("blogvisits"); echo $count; if (mt_rand(0, 100) == 0) { // 将 $count 保存到数据库 }

?> 这里示范的代码相当简单,不过您还可以对其进行加强,实现存入数据库以及从数据库中恢复的代码 以避免计数因为重启 PHP 而丢失。

23/65

编写 XCache 的心得 这里说说我编写 XCache 的心得,将所学到的东西共享给大家,希望能起到抛砖引玉的作用。注意 这段内容可能更合适与编写 PHP 扩展模块的 c 程序员而非 PHP 程序员。 代码生成器方式确实有用:我记得曾经发现 ea 对 PHP5 支持不够好是因为有些新的元素他并没有 对应处理。本已熟读 ea(以前是 mmcache) 的我尝试在代码里加入对该元素的处理,参考其他元素的处 理方式依葫芦画瓢分别在 calc/store/restore 等函数里对新的元素增加代码,却总是容易出现编写错 误的地方,编码 20 几行反复调试,算下来竟然花了我好个把小时的时间。听起来挺夸张,这其中也许部 分因为我只阅读而未曾写过他的代码或者是我把问题想的太简单而乱写一通,但是加上反复重编译连接 php 的时间算下来的确如此。当我们各方面都不去思考如何才能更简捷更高效率的时候,胡乱按感觉办事 很容易效率与质量都出奇地低下。而在 XCache 采用代码生成器的情况下,任何懂 PHP 的人只需参考新 元素出现的位置在代码生成器里找到合适的地方加入 1~5 行代码即可立即完工,而且如果错误会在编译期 间由代码生成器或者编译器发现问题。 代码生成器也需要维护:代码生成器的确大大地降低了开发成本、出问题的几率,但是维护代码生成 器本身也是需要代价的。捡起一段时间未动过的核心代码发现有些难读,不过幸运的是修改、调试完这些 核心代码其他所有利用这个核心机制的地方均随之升华了。 代码生成器并不深奥:其实他早就渗透到我们的编码中,比如 c 的 macro,比如 c++的 template, 都 是 利 用 机 器 完 成 重 复 劳 动 来 节 省 人 力 。 包 括 我 们 使 用 的 configure 脚 本 也 是 由 m4 编 写 的 autotools 工具来生成。 缓存原理、机制并不难:其中运用的原理、机制在其他地方应用都比较广泛,实现数据深层、浅层拷 贝,实现共享内存、锁。但是需要做好的地方也很多。1,自己用已经非常稳定,但是发布出来就水土不服, 比如 ini 配置别人改改也许就出 core 了,用户可能不小心写了 64 而不是 64M;2,移植性,除非特 大工程否则程序员往往没有测试资源去测各种硬件体系下的问题,幸好在 XCache 进入 debian 之后能 自动在各种 arch 上编译、发现编译等问题;3,写完一个东西不难,但是不断维护、修改、壮大代码的 情况下还要保持代码的整洁性、可控性非常难;4,hrm…挺多的。 权衡什么是最重要:本着提高能而寻找各种加速办法、寻找 PHP 缓冲器加速器的我们往往会优先看 中性能最高的那款,但是经过几个月使用,可能每个月、每周甚至第二天就发现 PHP 停止响应或者突然 不断的 coredump。如此惨痛代价才让我明白过来稳定必须优先于性能,一次故障是无论百分之几的性能 提高都无法挽回。

引用与参考 [1] PHP on fire, benchmark: http://itst.net/654-php-on-fire-three-opcode-caches-compared http://itst.net/wp-content/uploads/2006/10/PHP%20Bytecode%20Cacher%20Review. html

24/65

http://itst.net/wp-content/uploads/2006/10/PHP%20Bytecode%20Cacher%20Review. pdf [2] SMP 下性能爆增: http://www.vbulletin.com/forum/showthread.php?t=201257 [3] 支持 XCache 的系统发行版本: http://trac.lighttpd.net/xcache/wiki/XcacheRedist [4] Gentoo Portage Overlay 的配置: http://gentoo-wiki.com/HOWTO_Installing_3rd_Party_Ebuilds [5] XCache 的第三方 Ebuild: http://dev.hoffie.info/gentoo/dev-php5/xcache/ [6] 网站: http://xcache.lighttpd.net 在这里你能找到各种资料,不过目前由于 trac 不支持 多国语言轻松并存所以只写了英文版本的各种说明。 [7] 论坛: http://club.phpe.net/ http://forum.lighttpd.net/forum/4/ [8] m4 是一个宏语言。其实 c/c++ 所支持的宏功能也是由 cpp 宏语言来实现,这跟 m4 是同行,不 过 m4 的宏功能更强大。autoconf/libtool (用于 phpize 生成 configure 那个工 具)/sendmail 等工具都使用到 m4。我在编写 lighttpd 配置文件也采用 m4 宏。

25/65

扩展 PHP Ben/文

为什么要扩展 关于扩展 PHP,可谈的话题有很多,问题也有很多。在这无数的话题和问题中间,“为什么要对 PHP 进 行扩展,PHP 扩展可以干什么” 这个问题也是话题无疑是最常也是最先被提到的。因此,本文第一个要谈 的问题就是:为什么要对 PHP 进行扩展? 可以说,PHP 正是有了扩展才显得生机盎然。试想,如果 PHP 没有 GD 扩展、没有了 MySQL 扩展,那 将会变得多么无趣!因此,扩展 PHP 的一个主要目的就是想完成那些以前不可能完成的事情。想操作 Zip 文件?没问题![1](这个 Zip 扩展已经有人在进行了,PHP 5.2 当中会默认启用该扩展。)想开发 3D 游 戏?没问题![2](已经可以利用 PHP-GTK 开发出基本的程序)想对女朋友发短信?更没问题!:)只要 你想得到,利用 PHP 扩展就可以做得到! 除此之外,对 PHP 进行扩展的另一个主要目的就是用以提高 程序性能。PHP 中的 3 + 2 和 扩展二进制代码中的 3 + 2 的执行速度显然不在同一个数量级。而对于 那些应用更为复杂,运行条件更为苛刻的系统,将其某些逻辑和运算过程封装到一个 PHP 扩展就显得很 有必要了。

写扩展的基础 既然扩展是如此的“无所不能”,那么编写一个扩展是否需要一个很高的门槛呢? No!编写一个扩展是很容易的,只要具有一定的 C 语言基础就行。当然,若用其他语言进行开发也可以, 但由于 PHP 自身就是利用 C 编写的,因此无论是在代码兼容性还是可学习性(PHP 源码包中自带了很多 扩展的源代码,很有参考价值)上,C 语言都具有很大的优势。C++ 由于兼容 C,也可以作为一种选择, 但需要对代码的编译部分和其他一些地方做些技巧性的处理。 还有一个问题,跨平台这个问题怎么处理?在 Windows 和 Linux(或其他 xNix 平台)开发有什么不同? 没有什么不同。这是因为 C 语言本身就跨平台(当然跟 Java 那种跨平台的概念有所不同),采用 C 编 写出来的代码可以在绝大多数机器、绝大多数操作系统上编译运行。但很明显,你不能在 Linux 的环境 下去调用 Win32 API。所有的代码应该尽量采用 C 标准库去书写,若实在不可避免就应该采用一些宏定 义去分开处理。笔者喜欢在 Windows 环境下开发,因此本文所举诸例皆为在 Windows 环境下的情况。 但这并不碍大事,仅仅是习惯而已,代码还是一样的。 现在相信你已经对 PHP 进行扩展有了一个大致的认识了,下面我们就来具体谈谈 PHP 扩展以及如何开发 一个简单的扩展。

26/65

什么是扩展 对 PHP 进行的某项扩展(Extend)我们就称之为 PHP 的一个扩展(Extension) (有时也被人称之为模 块:Module)。扩展有两类四种(我认为 PHP 手册上只有三种的分法是值得商榷的)。 按其二进制代码相对于 PHP 自身的位置不同,可以分为内建的(Build-in)和外部的(External)。所 谓内建的是指该扩展在编译时被编译进了 PHP,调用该扩展的代码等就跟调用 PHP 原来自带的代码等毫 无二致。而“外部的”扩展就是指该扩展被单独编译成一个模块,若想使用就必须使用 dl() 函数或者在 php.ini 中利用类似 extension= xxx 的指令手动加载。两者各有优缺。内建扩展被自然编译进 PHP 代 码,调用时避免了加载过程,性能较外部扩展略强。其缺点就是与 PHP 代码结合度太高,一旦扩展有个 风吹草动,你就不得不重新对 PHP 进行编译。而外部扩展的优缺点则恰好与内建扩展相反。 按其所处语言层次的不同,扩展可分为 PHP 扩展和 Zend 扩展。自 PHP 4 开始,Andi Gutmans 和 Zeev Suraski 为 PHP 引入了 Zend 引擎(Zend Engine) 以便把 PHP 语言自身和 PHP 所提供一 些外部功能区分开来。Zend 引擎负责处理 PHP 语言本身,假如你想给 PHP 语法引入一个新的操作符(比 如“A bs B”表示变量 A 鄙视 变量 B)或者是想修改一下 PHP 本身的运行机制,那做一个 Zend 扩展 就很合适。如果你的扩展只是想让 PHP 在上传时可以自动生成一个进度条图片,那这个扩展我们就称之 为 PHP 扩展。用一句形象的话来说就是:Zend 扩展主内,PHP 扩展主外。 Zend 扩展的架构和 PHP 扩展的架构基本一样,只是处理层次的有所不同。由于 PHP 扩展不牵涉到 PHP 自身的内部架构,因此一般情况下,开发一个 PHP 扩展要比开发开发一个 Zend 扩展容易一些。MySQL 扩展,GD 扩展都是 PHP 扩展,本文所举的例子也是一个 PHP 扩展。Zend 扩展常见的有 APC、 ZendOptimizer 等。 由于开发一个 Zend 扩展需要一定的 PHP 内部结构的认识,我们将会在先讨论一些预备知识之后再来谈 Zend 扩展的开发。

开发环境的搭建 首先我们需要一个 PHP 的源码包,这个可以到http://www.php.net/downloads.php 去下载,记得 我们是要源码包(Complete Source Code)而不是PHP 的二进制代码包(Windows Binaries)。本 文所采用是 PHP 5.1.6 的源码包。PHP 4 与 PHP 5 的PHP 扩展有稍许不同,在必要处我会提醒这一 点的,因此您也可以采用 PHP 4.4.x 系列的源码包。 除此之外我们还需要一个 php5ts.lib (若用 PHP 4 的源码包则需要的是 php4ts.lib)的文件。这 在 PHP 二进制代码包的 dev 目录(php4ts.lib 则是直接放在二进制代码包的根目录)下可以找到。 将该源码包解压到某个目录(假定是D:\Work\PHP\work\php5,以后我们以 $PHP 指代该源码根目录), 我 们 可 以 看 到 main 、 Zend 、 win32 、 TSRM 、 ext 等 目 录 。 在 ext 目 录 下 有 ext_skel 和 ext_skel_win32.php 两个文件。Ext_skel 是在 xNix 环境下的一个用于构建PHP 扩展,生成 PHP 扩展框架的自动化脚本。由于是在 xNix 环境下使用的,并且使用方法也比较简单,故本文不再赘述。具

27/65

体使用方法可参见源码包根目录(即 $PHP)下的README.EXT_SKEL 文件。ext_skel_win32.php 顾 名 思 义 是 用 来 创 建

Win32 环 境 下 扩 展 框 架 的 的 脚 本 。 这 个 脚 本 需 要

Cygwin

(http://www.cygwin.com/) 的支持。使用方法和ext_skel 大同小异。本文所采用的是第三种方法: 使用 VC 的向导手动创建一个项目文件。这种方法好处就是不需要 Cygwin 的支持,但在编译该扩展的 xNix 版本时仍然需要通过ext_skel 来创建一个相应的框架。 我们在这里使用的 IDE 是 VC++ 2005 Express Edition 。如果你的扩展将来需要分发到更多的地 方,建议你使用 VC++ 6.0,这样可增加一定的兼容性。PHP 扩展在 VC++ 6.0 和 VC++ 2005 里面的 操作都差不多,但在 VC++ 2005 中需要进行一些额外的设置。 现在让我们打开 VC++ 2005,在菜单中选择 【File】 -> 【New】 -> 【Project】 来创建这个扩 展的项目文件(见图一)。

在【项目类型(Project Types)】中选择“Virual C++”,项目【模版(Templates)】为“Win32 Project”。在本例中我们的扩展名字为 phpmore,位于 $PHP\ext 目录下。 - PHPMORE 虽然一个扩展项目的存放位置并没有具体规定,但放到 $PHP\ext 目录下是一个 惯例,这会避免很多不必要的麻烦。 在出现的 【Win32 应用程序向导(Win32 Application Wizard)】中点击左面的【应用程序设置 (Application Settings)】,设置【程序类型(Application Type)】为“DLL”,并且将其设置为 【空项目(Empty Project)】(见图二)。

28/65

点击【完成(Finish)】就创建了该扩展的项目文件。此时你应该会在 $PHP\ext\phpmore 目录下找 到该扩展的“解决方案(solution)”文件。在$PHP\ext\phpmore\ phpmore 目录下找到扩展的“项目 (Project)”文件。 - PHPMORE 按照 VS 2005 的说法,一个“解决方案(solution)”是由多个“项目(Project)” 组成的,因此产生这样的目录结构是十分合理的。但对 PHP 扩展而言,由于需要 在各个系统平台下运行,如果把所有平台的项目文件都放在扩展的根目录下面,就 会给人一种非常凌乱的感觉。一个值得推荐的解决方法就是为每个平台都建立一个 目录,各自包含相应的项目文件,而把源代码文件(*.c 和 *.h 等)放在扩展根 目录或其他一个单独的目录。本文为了简单叙述起见,不再额外处理,但在实际应 用过程中请注意源代码目录的合理分配。

现在我们将扩展的源代码文件(phpmore.c 和 phpmore.h ,当然此时是空文件)新建/添加到 phpmore 扩展的项目文件当中。如图三所示:

29/65

- PHPMORE 在 VC++ 2005 中添加源代码文件时默认的后缀名为 .cpp,此时需要主动为文 件添加上 .c 的扩展名。否则 VC 的编译器会将其默认为 C++ 代码而进行编译 (当然这种设置也是可以改变的),这样就可能会产生一些编译错误。 为了能够很方便的引用 PHP 代码的头文件以及对项目进行编译,我们还需要对项目文件进行一些设置。 请通过菜单【项目(Project)】-> 【phpmore 属性(Properties)】进入项目的属性设置页。这里我 们先对项目的【Release】版进行配置。见图四。

30/65

先 转 到 【 C++ 】 属 性 的 【 General 】 页 填 入 “Additional Include Directories” : $PHP;$PHP\main;$PHP\win32;$PHP\TSRM;$PHP\Zend。我们这里输入的绝对路径,但实际开发过 程中最好填入相对路径。 再 转 到 【 C++ 】 属 性 的 【 Preprocessor 】 页 补 充 一 些 “Preprocessor Definitions” : ZEND_WIN32;PHP_WIN32;ZTS=1; ZEND_DEBUG=0; COMPILE_DL_PHPMORE 。前面 3 个是在 Win32 环境下开发所必加的预定义;ZEND_DEBUG=0 表示扩展不创建为 Debug 版本(因为现在是在配置 Release 版本嘛~);COMPILE_DL_PHPMORE 用于是否将本扩展编译为一个“外部扩展(定义见文首)”。 在【C++】属性里面还需要设置的有:【Code Generation】页的“运行库(Runtime Library)”请设 置为“Multi-threaded DLL (/MD)”; 【Advanced】页的“编译方式(Compile As)”请设置为“Compile as C Code (/TC)”

此外还需要在【连接器(Linker)】属性的【Input】页添加一个“Additional Dependencies”: php5ts.lib 。你可以把 php5ts.lib 放到一个 VC++ 能找到的地方,比如项目文件的目录。当然你 若采用的是 PHP 4 的源码包,请相应地把 php5ts.lib 替换为 php4ts.lib 。 这样,整个扩展项目文件的 Release 版本就配置好了。对于 Debug 版本可以有针对性的作一些改动。 不过需要注意,一般的 PHP 二进制代码包不允许加载 Debug 版本的扩展,只有将 PHP 编译为 Debug 版本才能加载 Debug 版本的扩展。

31/65

- PHPMORE 如果需要扩展在多种 PHP 版本中都可布署,那可以先设置一个基本配置(就像上 例不设置 php5ts.lib),然后再创建一个继承自基本配置的新的配置-比如 Release_PHP5-在这个 Release_PHP5 中额外设置一下 php5ts.lib 就可以 了。有的扩展还不事先预定义 ZTS,而是额外再创建一个 Release_TS 的配置, 道理是一样的。

OK,现在万事俱备,只欠编码了,让我们这就开始吧!

编码实现 所有的扩展都大致由 4 个部分组成:引用相关的头文件、Zend 模块的声明与相关函数实现、get_module() 函数的实现以及导出函数的声明和实现。关于这几部分的详细说明,请看 PHP 手册中《Zend API:深入 PHP 内核》一章。笔者正在试译这几章,对英文理解有困难的朋友可以先到笔者的站点去看一下译文。 为了简单叙述起见,我先列出本文例子的代码:

phpmore.h : - PHPMORE #ifndef PHPMORE_H #define PHPMORE_H extern zend_module_entry phpmore_module_entry; #define phpext_phpmore_ptr &phpmore_module_entry /* declaration of functions to be exported */ ZEND_FUNCTION(welcome_to_phpmore); PHP_MINFO_FUNCTION(phpmore); #define PHPMORE_VERSION "0.1.0" #endif

phpmore.c : - PHPMORE #define _USE_32BIT_TIME_T 1 #include "php.h" #include "phpmore.h" zend_function_entry phpmore_functions[] =

32/65

{ ZEND_FE(welcome_to_phpmore, NULL) {NULL, NULL, NULL} }; zend_module_entry phpmore_module_entry = { STANDARD_MODULE_HEADER, "PHP&More", phpmore_functions, NULL, NULL, NULL, NULL, PHP_MINFO(phpmore), PHPMORE_VERSION, STANDARD_MODULE_PROPERTIES }; #if COMPILE_DL_PHPMORE ZEND_GET_MODULE(phpmore) #endif PHP_MINFO_FUNCTION(phpmore) { php_info_print_table_start(); php_info_print_table_header(2, "PHP&More", "enabled"); php_info_print_table_row(2, "Version", PHPMORE_VERSION); php_info_print_table_end(); } ZEND_FUNCTION(welcome_to_phpmore) { zend_printf("Welcome to PHP&More!"); } 所有扩展都必须至少包含有 php.h ,这是一切的基础,因此必须首先在代码中引用(添加#define _USE_32BIT_TIME_T 1 这一行是为了去掉 VC++ 2005 中 64 位时间格式的支持,在 VS.NET 2003 或 VC++ 6.0 中均无需这样做)。 接下来是扩展的 Zend 函数块的声明。定义了一个名为 phpmore_functions ,每一个元素都是一个

33/65

zend_function_entry 结构的 Zend 函数数组。该数组用来声明本扩展一共对外(即PHP 脚本)提供 了多少可用的(导出)函数。由于没有其他地方可以主动提供(导出)函数的个数,因此数组的最后一个 元素必须为 {NULL, NULL, NULL},以便 Zend Engine 可以获知函数数组的元素列表是否结束。 然后就是整个扩展模块的声明。这是一个扩展“最高”层次的声明。全方位地提供了 Zend Engine 所需 要的各种信息。上面所声明的导出函数列表也仅仅是用来填充它的一个字段而已。除此之外,这个模块声 明还负责提供扩展名称(就是将来在 phpinfo() 函数中出现的那个扩展的名字,本例为“PHP&More”)、 导出函数列表(本例为phpmore_functions)、模块启动函数(PHP_MINIT_FUNCTION,在模块第一 次加载时被调用,本例为 NULL)、模块关闭函数(PHP_MSHUTDOWN_FUNCTION,在模块卸载关闭时被 调用,本例为 NULL)、请求启动函数(在每个请求启动时被调用,本例为 NULL)、请求关闭函数 (PHP_RINIT_FUNCTION,在每个请求关闭时被调用,本例为 NULL)、模块信息函数 (PHP_RSHUTDOWN_FUNCTION,用于在 phpinfo() 中显示扩展的信息,本例为 “PHP_MINFO_FUNCTION(phpmore)”)和模块版本(本例为 PHPMORE_VERSION ,定义在 phpmore.h )等其他信息。这几个模块函数的调用关系及顺序见图七:

WEB 服务器进程启动,载入 PHP 解释器

PHP 解释器载入扩展, 扩展调用模块起始函数(MINIT) 然后开始等待每个 WEB 请求

调用请求起始函数

调用请求起始函数

……

调用请求起始函数

执行 WEB 请求 1

执行 WEB 请求 2

……

执行 WEB 请求 n

调用请求关闭函数

……

调用请求关闭函数

调用请求关闭函数

所有 WEB 请求处理完毕 扩展调用模块关闭函数(MSHUTDOWN) PHP 解释器卸载扩展

PHP 解释器关闭,WEB 服务器进程关闭 图五 PHP 扩展的生存期

34/65

模块声明后面就是 get_module() 函数的实现。这个函数的声明没有手动写出,而是使用了一个宏 ZEND_GET_MODULE(phpmore) 来声明。这也是在扩展开发中常用的一种手段,我们应该尽力地去使用 宏。get_module() 函数用于向 Zend Engine 报告这是个外部扩展,这也可以使得我们能够通过 dl() 函数来手动加载它。 剩下的两段代码便是我们前面声明函数的具体实现。一个是模块信息函数,一个是对外导出的 welcome_to_phpmore 函数。模块信息函数对外输出了本扩展的启用状态和版本号,而 welcome_to_phpmore 函数则在 PHP 脚本调用 welcome_to_phpmore() 时对外输出字符串 “Welcome to PHP&More!”。 一个扩展的大致结构就是这样。简单编译后我们就得到了一个 phpmore.dll 的文件。相应更改更改 php.ini 及重新启动 Web 服务器后,就可以启用这个扩展了(见图六、图七)。

35/65

- PHPMORE Ben 联系方式:[email protected] 个人网站:http://www.yAnbiN.org 自由的程序员,喜好并在使用 PHP、Delphi 进行日常开发,同时也在学习 dotNET。侧重于 PHP 的性能与安全方面的研究,欢迎就本文或者其他相关问题 联系和交流。

36/65

Mysql 集群技术简介 叶金荣/文 MySQL Cluster 是 MySQL 适合于分布式计算环境的高实用、高冗余版本。它采用了 NDB Cluster 存 储引擎,允许在 1 个 Cluster 中运行多个 MySQL 服务器。在 MyQL 5.0 及以上的二进制版本中、以及 与最新的 Linux 版本兼容的 RPM 中提供了该存储引擎。(注意,要想获得 MySQL Cluster 的功能,必 须安装 mysql-server 和 mysql-max RPM)。目前能够运行 MySQL Cluster 的操作系统有 Linux、 Mac OS X 和 Solaris(一些用户通报成功地在 FreeBSD 上运行了 MySQL Cluster ,但 MySQL AB 公司尚未正式支持该特性)。

一、MySQL Cluster 概述 MySQL Cluster 是一种技术,该技术允许在无共享的系统中部署“内存中”数据库的 Cluster 。通过 无共享体系结构,系统能够使用廉价的硬件,而且对软硬件无特殊要求。此外,由于每个组件有自己的内 存和磁盘,不存在单点故障。 MySQL Cluster 由一组计算机构成,每台计算机上均运行着多种进程,包括 MySQL 服务器,NDB Cluster 的数据节点,管理服务器,以及(可能)专门的数据访问程序。关于 Cluster 中这些组件的关系,请参 见下图:

37/65

所有的这些节点构成一个完成的 MySQL 集群体系。数据保存在“NDB 存储服务器”的存储引擎中,表(结 构)则保存在“MySQL 服务器”中。应用程序通过“MySQL 服务器”访问这些数据表,集群管理服务器通过 管理工具(ndb_mgmd)来管理“NDB 存储服务器”。 通过将 MySQL Cluster 引入开放源码世界,MySQL 为所有需要它的人员提供了具有高可用性、高性能 和可缩放性的 Cluster 数据管理。

二、MySQL Cluster 基本概念 “NDB” 是一种“内存中”的存储引擎,它具有可用性高和数据一致性好的特点。 MySQL Cluster 能够使用多种故障切换和负载平衡选项配置 NDB 存储引擎,但在 Cluster 级别上的 存储引擎上做这个最简单。MySQL Cluster 的 NDB 存储引擎包含完整的数据集,仅取决于 Cluster 本

38/65

身内的其他数据。 目前,MySQL Cluster 的 Cluster 部分可独立于 MySQL 服务器进行配置。在 MySQL Cluster 中, Cluster 的每个部分被视为 1 个节点。



管理(MGM)节点:这类节点的作用是管理 MySQL Cluster 内的其他节点,如提供配置数据、启 动并停止节点、运行备份等。由于这类节点负责管理其他节点的配置,应在启动其他节点之前首 先启动这类节点。MGM 节点是用命令“ndb_mgmd”启动的。



数据节点:这类节点用于保存 Cluster 的数据。数据节点的数目与副本的数目相关,是片段的 倍数。例如,对于两个副本,每个副本有两个片段,那么就有 4 个数据节点。不过没有必要设置 多个副本。数据节点是用命令“ndbd”启动的。



SQL 节点:这是用来访问 Cluster 数据的节点。对于 MySQL Cluster,客户端节点是使用 NDB Cluster 存储引擎的传统 MySQL 服务器。通常,SQL 节点是使用命令“mysqld –ndbcluster” 启动的,或将“ndbcluster”添加到“my.cnf”后使用“mysqld”启动。 注释:在很多情况下,术语“节点”用于指计算机,但在讨论 MySQL Cluster 时,它表示的是进 程。在单台计算机上可以有任意数目的节点,为此,我们采用术语“ Cluster 主机”。

管理服务器(MGM 节点)负责管理 Cluster 配置文件和 Cluster 日志。 Cluster 中的每个节点从管理 服务器检索配置数据,并请求确定管理服务器所在位置的方式。当数据节点内出现新的事件时,节点将关 于这类事件的信息传输到管理服务器,然后,将这类信息写入 Cluster 日志。 此外,可以有任意数目的 Cluster 客户端进程或应用程序。它们分为两种类型:



标准 MySQL 客户端:对于 MySQL Cluster,它们与标准的(非 Cluster 类)MySQL 没有区 别。换句话讲,能够从用 PHP、Perl、C、C++、Java、Python、Ruby 等编写的现有 MySQL 应用程序访问 MySQL Cluster。



管理客户端:这类客户端与管理服务器相连,并提供了启动和停止节点、启动和停止消息跟踪(仅 调试版本)、显示节点版本和状态、启动和停止备份等的命令。

三、开始准备 1、准备服务器 现在,我们计划建立有 5 个节点的 MySQL CLuster 体系,因此需要用到 5 台机器,分别做如下用途: 节点(用途)

IP 地址(主机名)

管理节点(MGM)

192.168.0.1(db1)

SQL 节点 1(SQL1)

192.168.0.2(db2)

SQL 节点 2(SQL2)

192.168.0.3(db3)

数据节点 1(NDBD1)

192.168.0.4(db4)

数据节点 2(NDBD2)

192.168.0.4(db5)

39/65

2、注意事项及其他 每个节点的操作系统都是 Linux,下面的描述中将使用主机名,不再使用 IP 地址来表示。由于 MySQL Cluster 采用 TCP/IP 方式连接,并且节点之间的数据传输没有加密,因此这个体系最好只在单独的子网 中运行,并且考虑到传输的速率,强烈建议不要跨越公网使用这个体系。所需的 MySQL 软件请事先在 http://dev.mysql.com/downloads 下载。 实际上整个体系可以在一个单独的实体计算机上运行成功,当然了,必须设定不同的目录以及端口等,只 能作为测试时使用。

四、开始安装 1、假定条件 在每个节点计算机上都采用 nobody 用户来运行 Cluster,因此执行如下命令添加相关用户(如果已经 存在则略过,且用 root 用户执行): root# /usr/sbin/groupadd nobody root# /usr/sbin/useradd nobody -g nobody 假设已经下载了 mysql 可直接使用的二进制安装包,且放在 /tmp 下了。

2、SQL 节点和存储节点(NDB 节点)安装(即 4 个机器重复执 行以下步骤) root# cd /tmp/ root# tar zxf mysql-max-5.0.24-linux-i686.tar.gz root# mv mysql-max-5.0.24-linux-i686 /usr/local/mysql/ root# cd /usr/local/mysql/ root# ./configure --prefix=/usr/local/mysql root# ./scripts/mysql_install_db root# chown -R nobody:nobody /usr/local/mysql/

3、配置 SQL 节点 root# vi /usr/local/mysql/my.cnf 然后输入如下内容:

40/65

[mysqld] basedir

= /usr/local/mysql/

datadir

= /usr/local/mysql/data

user

= nobody

port

= 3306

socket

= /tmp/mysql.sock

ndbcluster ndb-connectstring=db1 [MYSQL_CLUSTER] ndb-connectstring=db1

4、配置存储节点(NDB 节点) root# vi /usr/local/mysql/my.cnf 然后输入如下内容: [mysqld] ndbcluster ndb-connectstring=db1 [MYSQL_CLUSTER] ndb-connectstring=db1

5、安装管理节点 root# cd /tmp/ root# tar zxf mysql-max-5.0.24-linux-i686.tar.gz root# mkdir /usr/local/mysql/ root# mkdir /usr/local/mysql/data/ root# cd mysql-max-5.0.24-linux-i686/bin/ root# cp ndb_mgm* /usr/local/mysql/ root# chown -R nobody:nobody /usr/local/mysql

6、配置管理节点 root# vi /usr/local/mysql/config.ini 然后输入如下内容: [NDBD DEFAULT]

41/65

NoOfReplicas=1

[TCP DEFAULT] portnumber=3306

#设置管理节点服务器 [NDB_MGMD] hostname=db1 #MGM 上保存日志的目录 datadir=/usr/local/mysql/data/

#设置存储节点服务器(NDB 节点) [NDBD] hostname=db4 datadir=/usr/local/mysql/data/ #第二个 NDB 节点 [NDBD] hostname=db5 datadir=/usr/local/mysql/data/

#设置 SQL 节点服务器 [MYSQLD] hostname=db2 #第二个 SQL 节点 [MYSQLD] hostname=db3 注释: Cluster 管理节点的默认端口是 1186,数据节点的默认端口 2202。从 MySQL 5.0.3 开始,该 限制已被放宽, Cluster 能够根据空闲的端口自动地为数据节点分配端口。如果你的版本低于 5.0.22, 请注意这个细节。

五、启动 MySQL Cluster 较为合理的启动顺序是,首先启动管理节点服务器,然后启动存储节点服务器,最后才启动 SQL 节点服务 器:



在管理节点服务器上,执行以下命令启动 MGM 节点进程:



root# /usr/local/mysql/ndb_mgmd -f /usr/local/mysql/config.ini 必须用参数“-f”或“--config-file”告诉 ndb_mgm 配置文件所在位置,默认是在 ndb_mgmd 相同目录下。

42/65



在每台存储节点服务器上,如果是第一次启动 ndbd 进程的话,必须先执行以下命令:



root# /usr/local/mysql/bin/ndbd --initial 注意,仅应在首次启动 ndbd 时,或在备份/恢复数据或配置文件发生变化后重启 ndbd 时使用 “--initial”参数。因为该参数会使节点删除由早期 ndbd 实例创建的、用于恢复的任何文件, 包括用于恢复的日志文件。 如果不是第一次启动,直接运行如下命令即可: root# /usr/local/mysql/bin/ndbd



最后,运行以下命令启动 SQL 节点服务器:



root# /usr/local/mysql/bin/mysqld_safe --defaults-file=/usr/local/mysql/my.cnf & 如果一切顺利,也就是启动过程中没有任何错误信息出现,那么就在管理节点服务器上运行如下 命令: root# /usr/local/mysql/ndb_mgm -- NDB Cluster -- Management Client -ndb_mgm> SHOW Connected to Management Server at: localhost:1186 Cluster Configuration --------------------[ndbd(NDB)]

2 node(s)

id=2

@192.168.0.4 (Version: 5.0.22, Nodegroup: 0, Master)

id=3

@192.168.0.5 (Version: 5.0.22, Nodegroup: 0)

[ndb_mgmd(MGM)] 1 node(s) id=1

@192.168.0.1 (Version: 5.0.22)

[mysqld(SQL)]

1 node(s)

id=2

(Version: 5.0.22)

id=3

(Version: 5.0.22)

具体的输出内容可能会略有不同,这取决于你所使用的 MySQL 版本。 注意:如果你正在使用较早的 MySQL 版本,你或许会看到引用为‘[mysqld(API)]’的 SQL 节点。这是 一种早期的用法,现已放弃。 现在,应能在 MySQL Cluster 中处理数据库,表和数据。

43/65

六、创建数据库表 与没有使用 Cluster 的 MySQL 相比,在 MySQL Cluster 内操作数据的方式没有太大的区别。执行这 类操作时应记住两点:



表必须用 ENGINE=NDB 或 ENGINE=NDBCLUSTER 选项创建,或用 ALTER TABLE 选项更改,以 使用 NDB Cluster 存储引擎在 Cluster 内复制它们。如果使用 mysqldump 的输出从已有数 据库导入表,可在文本编辑器中打开 SQL 脚本,并将该选项添加到任何表创建语句,或用这类选 项之一替换任何已有的 ENGINE(或 TYPE)选项。



另外还请记住,每个 NDB 表必须有一个主键。如果在创建表时用户未定义主键,NDB Cluster 存储引擎将自动生成隐含的主键。(注释:该隐含 键也将占用空间,就像任何其他的表索引一样。 由于没有足够的内存来容纳这些自动创建的键,出现问题并不罕见)。

下面是一个例子: 在 db2 上,创建数据表,插入数据: [db2~]root# mysql -uroot test [db2~]mysql> create table city( [db2~]mysql> id mediumint unsigned not null auto_increment primary key, [db2~]mysql> name varchar(20) not null default '' [db2~]mysql> ) engine = ndbcluster default charset utf8; [db2~]mysql> insert into city values(1, 'city1'); [db2~]mysql> insert into city values(2, 'city2'); 在 db3 上,查询数据: [db3~]root# mysql -uroot test [db2~]mysql> select * from city; +-----------+ |id | name | +-----------+ |1 | city1 | +-----------+ |2 | city2 | +-----------+

七、安全关闭 要想关闭 Cluster,可在 MGM 节点所在的机器上,在 Shell 中简单地输入下述命令: [db1~]root# /usr/local/mysql/ndb_mgm -e shutdown

44/65

运行以下命令关闭 SQL 节点的 mysqld 服务: [db2~]root# /usr/local/mysql/bin/mysqladmin -uroot shutdown

八、其他 关于 MySQL Cluster 更多详细的资料以及备份等请参见 MySQL 手册的“MySQL Cluster(MySQL 集 群)”章节。 参考资料:《MySQL 5.1 中文手册》

- PHPMORE 叶金荣 联系方式:[email protected] 个人网站:http://imysql.cn

45/65

LAMP 系统优化实例分析 Dalamar /文 编者案:Dalamar 最近对一台服务器进行了优化,使其能够支持 300 万 PV/天的压力,我们就请他来分 享下优化的 LAMP 的经验。 我们假设,php 应用已经充分缓冲了数据库查询结果,数据库不再成为瓶颈。 我们使用的试验系统是 - PHPMORE Red Hat Enterprise Linux AS release 3 PHP 4.4.2 Apache 2.0.59

一,php 本身的优化 安装 eAccelerator 是首选,对于包含文件很多的 php 应用提高效率非常明显。 目前已经出到 0.9.5 正式版了,强烈建议 0.9.4 以下的用户升级到此版本。 因为,0.9.5 以下版本的缓冲文件没有目录分级直接存放在单一目录下,当同一目录缓冲文件比较多到 达数万级别的时候,文件系统性能下降会影响整体的性能, 0.9.5 通过 hash 缓冲文件路径解决了这个问题。 安装方法很简单

- PHPMORE export PHP_PREFIX="php 安装路径" $PHP_PREFIX/bin/phpize ./configure --enable-eaccelerator=shared --with-php-config=$PHP_PREFIX/bin/php-config make make install 然后把这段内容加到 php.ini 里,然后重起 web 服务器就生效了

- PHPMORE zend_extension="php 安装路径/lib/php/extensions/eaccelerator.so" eaccelerator.shm_size="16"

46/65

eaccelerator.cache_dir="/tmp/eaccelerator" eaccelerator.enable="1" eaccelerator.optimizer="1" eaccelerator.check_mtime="1" eaccelerator.debug="0" eaccelerator.filter="" eaccelerator.shm_max="0" eaccelerator.shm_ttl="0" eaccelerator.shm_prune_period="0" eaccelerator.shm_only="0" eaccelerator.compress="1" eaccelerator.compress_level="9"

- PHPMORE 1, zend_extension="php 安装路径/lib/php/extensions/eaccelerator.so" 这个路径一般为 /php 安装路径 /lib/php/extensions/no-debug-non-zts-20020429/eaccelerator.so 具体 要自己去找一下 2, 如果 php 编译为 thread safe 的,要用 zend_extension_ts 替代 zend_extension 3, 如果 php 编译参数没指定 '--prefix=' 的路径参数的话 $PHP_PREFIX/bin/phpize 这条命令可能会执行出错,解决方法是重新编译 php,指定 php 安装路径

二, web 服务器优化 这里以编译为 apache 模块运行的 php 系统为例

a,关闭 apache 的访问日志. 如果不需要分析 apache 的访问日志或者网站统计数据的话,关闭 apache 的日志会带来一定的性能 提升。 关闭方法: 1, 注释 相关 CustomLog 的配置 2, 把 ErrorLog 的路径定为 /dev/null (就算注释此项,error log 默认还是会写的, 因此把 他定义到/dev/null 减少磁盘的 IO) 3, 修改 LogLevel warn 为 LogLevel emerg (降低日志级别,减少 error log 输出)

47/65

b, Timeout 和 KeepAlive 参数 默认的 Timeout 300 太大了,一般可以看情况改小到 Timeout 30 或者 Timeout 10 都可以. KeepAlive 默认为 On, 建议设置为 Off。

c,MPM 参数设置 这里用的 prefork,以下参数根据系统实际情况调整测试了。这里只是个例子 - PHPMORE StartServers

5

MinSpareServers

50

MaxSpareServers

250

ServerLimit

2048

MaxClients

1000

MaxRequestsPerChild 10000
如果不会引起 php 一些扩展模块错误的话,也可以考虑使用 worker.c 的 MPM

d, 压缩输出页面,提高系统吞吐量 这 里 需 要 安 装 mod_deflate 和 mod_headers (apache2 以 后 取 消 了 mod_gzip 用 mod_deflate 代替了 ) 如果 apache 是以 DSO 模式安装的话可以动态增加模块

- PHPMORE cd httpd-2.0.59/modules/filters /usr/local/apache2/bin/apxs -I . -i -a -c -o mod_deflate.so mod_deflate.c cd ../metadata/ /usr/local/apache2/bin/apxs -I . -i -a -c -o mod_headers.so mod_headers.c . -i -a -c -o mod_headers.so mod_headers.c

然后在 httpd.conf 最后增加

- PHPMORE AddOutputFilterByType DEFLATE text/html text/plain text/xml application/x-httpd-php-so

48/65

urce application/x-httpd-php # Insert filter SetOutputFilter DEFLATE # Netscape 4.x has some problems... BrowserMatch ^Mozilla/4 gzip-only-text/html # Netscape 4.06-4.08 have some more problems BrowserMatch ^Mozilla/4.0[678] no-gzip # MSIE masquerades as Netscape, but it is fine BrowserMatch bMSIE !no-gzip !gzip-only-text/html # Don't compress images SetEnvIfNoCase Request_URI .(?:gif|jpg|jpe?g|png)$ no-gzip dont-vary

# Make sure proxies don't deliver the wrong content Header append Vary User-Agent env=!dont-vary


e,删除不必要的 Alias 和 Directory 参数 比如以下都可以删除

- PHPMORE Alias /icons/ "/usr/local/apache2/icons/" Options Indexes MultiViews AllowOverride None Order allow,deny Allow from all # Make sure proxies don't deliver the wrong content Header append Vary User-Agent env=!dont-vary 如果 web 路径下不需要使用符号链接的可以删除 FollowSymLinks

49/65

f,其他参数调整 - PHPMORE UseCanonicalName Off HostnameLookups Off ServerSignature Off ServerTokens Prod

三,Linux 服务器调整 a,使用 tmpfs 可以把 session 的目录定义到 /dev/shm/session 下 同时把上面的 eaccelerator 参数 为

eaccelerator.cache_dir="/tmp/eaccelerator" 改

eaccelerator.cache_dir="/dev/shm/eaccelerator" 这样的话经过 eaccelerator 预编译和缓冲的代码相当于直接放在内存中运行,大大提高了 php 程

序的性能。 tmpfs 的缺点是重起以后 tmpfs 下的内容会消失,所以有必要开始的时候做一个 shell 来自动建立 需要的文件夹 可以在/etc/rc.local 里加入以下内容 - PHPMORE mkdir /dev/shm/eaccelerator chmod 777 /dev/shm/eaccelerator mkdir /dev/shm/session chmod 777 /dev/shm/session 这里再提供一个 shell 来清理/dev/shm/eaccelerator 下缓冲的文件,超过三小时没有读写的缓 冲文件将会被删除。 可以把这个加入到 crontab 里定时运行,这样就可以有效控制 tmpfs 的尺寸了。

- PHPMORE #! /bin/bash #clear /dev/shm/eaccelerator/ #by dalamar 2006-10-12

50/65

TMP_FILE_NUM=`find /dev/shm/eaccelerator/ -type f -name "eaccelerator*" -amin +180 | wc -l`; find /dev/shm/eaccelerator/ -type f -name "eaccelerator*" -amin +180 -exec rm -f {} ; echo "`date` cleared eaccelerator tmp directory at $TMP_FILE_NUM files" >> /root/clear_l og.log;

b,tcp 优化 - PHPMORE echo 1800 > /proc/sys/net/ipv4/tcp_keepalive_time

c,定时监控 web 服务器状态,负载过大时自动重启 web 服务 器 如果能在 apache 接近崩溃的时候重启,虽然不是件太好的事情,但是总比系统 down 掉好。 下面的 shell 可以在 apache 超过一定进程数的时候重启服务器,可以加入 crontab 中定时执行

- PHPMORE #! /bin/bash MAXPRONUM=1200 #get apache thread PRONUM=`ps aux | grep apache | wc -l | awk '{print $1}'` #get 80 port cosadfnnection CONNECTEDNUM=`netstat -nap | grep -c :80` #get phy memory used rate phy_mem_rate=`free | awk 'NR == 2 { print int($3/$2*100) "%"}'`; #get total memory used number total_mem_used=`free | awk 'NR >= 2 { sum+=$3}END{print sum}'`; #get total memeory number total_mem=`free | awk 'NR==2||NR>3 { sum+=$2 }END{print sum}'`; mem_tmp=`expr $total_mem_used \* 100`;

51/65

#get total memory used rate total_mem_rate=`expr $mem_tmp / $total_mem`; #get avg_load number export avg_load=`uptime | awk '{ print " avg_load: " $10 " " $11 " " $12}'`; if [ $PRONUM -gt $MAXPRONUM ] then #echo "PRONUM:$PRONUM" echo "[ERROR]`date` apache restarted at $PRONUM process Connected:$CONNECTEDN UM Phy Mem Used rate $phy_mem_rate , Total Mem Used rate "$total_mem_rate"%" $av g_load >> /root/apache.log kill -HUP `cat /usr/local/apache2/logs/httpd.pid` else #echo "PRONUM:$PRONUM" echo "[OK]`date` apache normal at $PRONUM process Connected:$CONNECTEDNUM P hy Mem Used rate $phy_mem_rate , Total Mem Used rate "$total_mem_rate"%" $avg_loa d >> /root/apache.log fi

四,light httpd + fastcgi + php 如果以上的优化还不能使系统稳定运行的话. 可以尝试 light httpd + fastcgi + php 的方案

a,编译 fastcgi 的 php - PHPMORE copy apache 模块下运行的 php 的./configure 参数 删 除 '--with-apxs2=/usr/local/apache2/bin/apxs' 增加 '--enable-fastcgi' '--enable-force-cgi-redirect' configure make make install 然后修改 php.ini,增加 doc_root="" cgi.force_redirect="0"

52/65

cgi.fix_pathinfo="1" 命令行下运行 /home/php4/bin/php -v 如果显示 PHP 4.4.4 (cgi-fcgi) (built: Oct 13 2006 12:20:04) 说明 fastcgi 的 php 安装正确

b,编译安装 lighthttpd - PHPMORE ./configure --prefix='/home/lighthttp' make make install

c,配置 light http 从源代码 lighttpd-1.4.13/doc/里把 lighttpd.conf copy 出来修改 把需要的模块从 server.modules 里去掉注释 这里去掉 mod_fastcgi 和 mod_rewrite 的注释 修改配置

- PHPMORE accesslog.filename server.errorlog

= /dev/null = "/dev/null"

server.document-root = "/home/www" server.pid-file server.username server.groupname

= "/home/lighthttpd/lighttpd.pid" = "www" = "www"

增加 - PHPMORE fastcgi.server = ( ".php" => (( "socket"

=> "/dev/shm/php-fastcgi.socket",

"bin-path" => "/home/php4/bin/php", "min-procs" => 2,

53/65

"max-procs" => 10, "bin-environment" => ( "PHP_FCGI_CHILDREN" => "16", "PHP_FCGI_MAX_REQUESTS" => "1000" ), "idle-timeout" => 20 )) ) apache 基本的 rewrite 规则和 light http 的转换非常相似: light http 规则

- PHPMORE url.rewrite

= ( "^/home/www/thread/([0-9]+)/([0-9]+)/([0-9]+)\.html$" => "/thread.p

hp?forum=$1&topic=$2&page=$3", "^/home/www/view/([0-9]+)/([0-9]+)\.html$" => "/view.php?forum=$1&page=$ 2" )

apache 规则

- PHPMORE rewriteengine on rewriterule ^home/www/thread/([0-9]+)/([0-9]+)/([0-9]+).html$ /thread.php?forum= $1&topic=$2&page=$3 rewriterule ^home/www/thread/([0-9]+)/([0-9]+).html$ /view.php?forum=$1&page=$ 2

d,启动和关闭 light httpd 启动方法: sbin/lighttpd -D -f lighthttpd.conf 关闭方法: kill -TERM `cat /home/lighthttpd/lighttpd.pid` 启动好以后,会有非常多的 php 进程和一个 ligthttpd 进程出现,这个是正常现象。

e,注意事项: 编译的 fastcgi 的 php 一定要配置 eaccelerator,否则执行 fastcgi 的 php 执行效率比较差,

54/65

大访问量的时候可能会死住。 目前 light http + fastcgi 的 php 还不是特别成熟,建议是系统在高负荷的情况下使用,负载 降下来以后切换回 apache。 小结,以上优化方案在一台双至强 4G 内存的 1U web 服务器上,大致可以满足一个 15 分钟在线万人以上 的论坛系统。 每天 300 万左右的页面点击数。 如果访问量再大的话,可以考虑增加 web 服务器进行负载均衡了。下次有机会我们可以讲一下多 web 服务 器的负载均衡和数据共享问题。

55/65

56/65

Subversion PHP Binding sirtoozee/文

什么是 Subversion? 就像 Subversion 中文站(http://www.subversion.org.cn/)介绍的:Subversion 是新一代的 版本控制工具,不仅可以管理程序源代码,也可以应用于其他协作管理数据的工作,例如使用 Subversion 来合作写乐谱,美工用来共同作图,如图 1 所示:

图 1 团队协同工作

谁在使用 Subversion? Zend Framework Zend

Framework

是 开 源 项 目 , 匿 名 用 户 可 以 直 接 从 它 的

Subversion

库 里

(http://framework.zend.com/svn/framework/)导出下载到所有的源代码(岂止此文档最后更 新,它的 trunk 版本已经提交了 1557 次!)。

LCUC LCUC(http://www.lcuc.org)是面向中国学生的 Linux 校园联盟,采用了 DST(Drupal + Subversion + Trac 的组合)。注册用户(基于 Apache 的 AuthMySQL 模组认证)可以参与 LCUC 的 开 源 项 目 ( 例 如 SVN Commit 、 Trac New Ticket ), 匿 名 用 户 可 以 从 它 的 Subversion 库 里 (http://svn.freesys.cn/public/svn-lcuc/)导出下载到所有的源代码、文档(岂止此文档最 后更新,它的 trunk 版本提交了 113 次)。

57/65

为什么将 Subversion 和 PHP Binding(捆绑)起来? 3.1 Subversion 采用 C/S 架构的缺点 不统一的客户端 在 Unix/Linux、Windows 下可以使用 Subversion 的 svn 命令行模式,如图 3.1 所示:

图 3.1 svn 命令行模式 在 Windows 下可以使用 TortoiseSVN 的应用程式模式,如图 3.2 所示:

58/65

图 3.2 TortoiseSVN 应用程式模式 不方便的服务端 Subversion 基于 Apache 的 SVNDAV 认证模式需要手工调用 htpasswd 命令编辑认证文件; Subversion 库目录分组认证需要手工调用 svnserver 命令编辑 svnserver.conf。

3.2 Subversion 采用 B/S 架构的优点 统一的客户端 采用 Subversion Python Binding,Edgewall 公司开发了项目管理软体 Trac。需要在客户端通过 svn-diff 查看不同修订版本之间的差异,现在通过统一、方便的 Web 界面呈现给协同开发的项目组成员, 如图 3.2 所示:

59/65

图 3.2 Trac 的 Web 界面呈现 svn-diff 借助 Subversion PHP Binding、PEAR:package:XML_XUL(构建 Mozilla XUL 应用程式的类), Subversionary 组织开发了 FlexySVN。需要在客户端通过 svn-ls、svn-log、svn-diff 浏览 Subversion 库、查看日志,现在通过统一、方便的 RIA 界面呈现给协同开发的项目组成员如图 3.3 所 示:

图 3.3 FlexySVN 的 RIA 界面呈现 svn-ls、svn-log、svn-diff 方便的服务端 借助 Subversion PHP Binding,Archermind 公司开发了统一、方便的软件外包项目管理软体 DST, 如图 3.4 所示:

60/65

图 3.4 DST 整体架构 a) 管理 Subversion 库 轻松创建项目 Subversion 库,无须登陆服务器调用 svnadmin create your-project。 b) 管理 Subversion 认证 与项目组所有注册成员帐号绑定,无须登陆服务器调用 htpasswd your-svn-users。 c) 管理 Subversion 目录访问 与项目组成员的权限绑定,无须登陆服务器调用 svnserver you-svnserver.conf。

如何使用 Subversion PHP Binding? 4.1 如何配置 php-svn for Windows? PHP 5.2.0 稳定版本发布了,虽然修订了前面发行版本 200 多个 BUG、带来了许多新特性,但是 Windows 下的 go-pear.bat 还是有 BUG(Fixed by http://go-pear.org/),开源社区的工作还需要踏踏实 实的 step by step ^_^ 下载针对 PHP 5.2.0 Windows Binaries 的 PECL 模组集; 将 zip 包里面的 php_svn.dll 释放到/ext 下; 编辑 php.ini,在 Windows Extensions 处添加 extension=php_svn.dll; 重启 Apache。 Subversion PHP Binding 开启,如图 4.1 所示:

61/65

图 4.1 Subversion PHP Binding 开启

4.2 如何使用 Subversion PHP Binding? Subversion

PHP

Binding 没 有 Manual , 从 PECL 官 方 下 载 svn-0.2

的 tarball 包

http://pecl.php.net/get/svn-0.2.tgz,查看php_svn.h和svn.c。

- PHPMORE Subversion PHP Binding 函数原型 Subversion 的基本功能:导出、浏览、认证…… PHP_FUNCTION(svn_checkout); PHP_FUNCTION(svn_cat); PHP_FUNCTION(svn_ls); PHP_FUNCTION(svn_log); PHP_FUNCTION(svn_auth_set_parameter); PHP_FUNCTION(svn_auth_get_parameter); PHP_FUNCTION(svn_client_version); PHP_FUNCTION(svn_diff); PHP_FUNCTION(svn_cleanup); Subversion 的变更功能:提交、添加、更新、导入…… PHP_FUNCTION(svn_commit); PHP_FUNCTION(svn_add); PHP_FUNCTION(svn_status); PHP_FUNCTION(svn_update); PHP_FUNCTION(svn_import); Subversion 库的管理功能:创建、拷贝……

62/65

PHP_FUNCTION(svn_repos_create); PHP_FUNCTION(svn_repos_recover); PHP_FUNCTION(svn_repos_hotcopy); PHP_FUNCTION(svn_repos_open); PHP_FUNCTION(svn_repos_fs); PHP_FUNCTION(svn_repos_fs_begin_txn_for_commit); PHP_FUNCTION(svn_repos_fs_commit_txn); PHP_FUNCTION(svn_fs_revision_root); PHP_FUNCTION(svn_fs_check_path); PHP_FUNCTION(svn_fs_revision_prop); PHP_FUNCTION(svn_fs_dir_entries); PHP_FUNCTION(svn_fs_node_created_rev); PHP_FUNCTION(svn_fs_youngest_rev); PHP_FUNCTION(svn_fs_file_contents); PHP_FUNCTION(svn_fs_file_length); PHP_FUNCTION(svn_fs_txn_root); PHP_FUNCTION(svn_fs_make_file); PHP_FUNCTION(svn_fs_make_dir); PHP_FUNCTION(svn_fs_apply_text); PHP_FUNCTION(svn_fs_copy); PHP_FUNCTION(svn_fs_delete); PHP_FUNCTION(svn_fs_begin_txn2); PHP_FUNCTION(svn_fs_is_dir); PHP_FUNCTION(svn_fs_is_file); PHP_FUNCTION(svn_fs_node_prop); PHP_FUNCTION(svn_fs_change_node_prop); PHP_FUNCTION(svn_fs_contents_changed); PHP_FUNCTION(svn_fs_props_changed); PHP_FUNCTION(svn_fs_abort_txn); Subversion 版本管理功能:合并、分支、删除…… PHP_FUNCTION(svn_blame); PHP_FUNCTION(svn_merge); PHP_FUNCTION(svn_revert); PHP_FUNCTION(svn_resolved); PHP_FUNCTION(svn_copy); PHP_FUNCTION(svn_move); PHP_FUNCTION(svn_propset); PHP_FUNCTION(svn_propget); PHP_FUNCTION(svn_proplist); PHP_FUNCTION(svn_export); PHP_FUNCTION(svn_url_from_path); PHP_FUNCTION(svn_uuid_from_url);

63/65

PHP_FUNCTION(svn_uuid_from_path); PHP_FUNCTION(svn_switch); PHP_FUNCTION(svn_mkdir); PHP_FUNCTION(svn_delete);

Subversion PHP Binding 的函数 API

a) 以 svn_auth_set_parameter 认证举例: 通过看 svn_auth_set_parameter(SVN_G(ctx)->auth_baton, apr_pstrdup(SVN_G(pool), key), apr_pstrdup(SVN_G(pool), value)),在 PHP 脚本里调用 svn_auth_set_parameter 时 , 需 要 指 明 认 证 的 键 值 apr_pstrdup(SVN_G(pool), key) , 需 要 给 出 认 证 的 值 apr_pstrdup(SVN_G(pool), value)。

- PHPMORE svn_auth_set_parameter(SVN_AUTH_PARAM_DEFAULT_USERNAME, 'sirtoozee'); svn_auth_set_parameter(SVN_AUTH_PARAM_DEFAULT_PASSWORD, '123456');

b) 以 svn_diff 版本差异举例: 通过看 svn_diff(string path1, int rev1, string path2, int rev2),在 PHP 脚本里调用 svn_diff 时,需要指明 Subversion 库的路径 string path1 和 string path2,需要指明比较的 不同版本 int rev1 和 int rev2。

- PHPMORE $taxonomy_module_url = 'http://svn.freesys.cn/public/svn-lcuc/trunk/modules/taxonomy .module'; print_r(svn_diff($taxonomy_module_url, 81, $taxonomy_module_url, 79));

总结 Subversion 为很多编程语言提供了 Binding,有前面提及的 Python,还有 Ruby、Java、C/C++……,

64/65

因此整合 Subversion 开发的 C/S、B/S 架构的商业产品、开源项目在 Google 里一搜就分 N 页。 PHP fans 如果感兴趣可以加入到 LCUC 的开源项目中来,也可以和其他的 PHP fans 协同开发一套整合 Subversion 的好东东哦 ^_^

65/65

Related Documents

Phpmore Vol7
August 2019 25
Ampersand Vol7
June 2020 18
Kuchingtalk-vol7
May 2020 21
Vol7-3-2002-8
November 2019 7