PHP 运行模式
CGI (Common Gateway Interface)
CGI 即通用网关接口(Common Gateway Interface),它是一段程序,通俗的讲 CGI 就象是一座桥,把网页和 WEB 服务器中的执行程序连接起来,它把 HTML 接收的指令传递给服务器的执行程序,再把服务器执行程序的结果返还给 HTML 页。CGI 的跨平台性能极佳,几乎可以在任何操作系统上实现。 CGI 已经是比较老的模式了,这几年都很少用了。
每有一个用户请求,都会先要创建 CGI 的子进程,然后处理请求,处理完后结束这个子进程,这就是 fork-and-execute 模式。 当用户请求数量非常多时,会大量挤占系统的资源如内存,CPU 时间等,造成效能低下。所以用 CGI 方式的服务器有多少连接请求就会有多少 CGI 子进程,子进程反复加载是 CGI 性能低下的主要原因。
如果不想把 PHP 嵌入到服务器端软件(如 Apache)作为一个模块安装的话,可以选择以 CGI 的模式安装。或者把 PHP 用于不同的 CGI 封装以便为代码创建安全的 chroot 和 setuid 环境。这样每个客户机请求一个 php 文件,Web 服务器就调用 php.exe(win 下是 php.exe,linux 是 php)去解释这个文件,然后再把解释的结果以网页的形式返回给客户机。这种安装方式通常会把 PHP 的可执行文件安装到 web 服务器的 cgi-bin 目录。
CERT 建议书
CA-96.11:建议不要把任何的解释器放到 cgi-bin 目录。
这种方式的好处是把 web server 和具体的程序处理独立开来,结构清晰,可控性强,同时缺点就是如果在高访问需求的情况下,cgi 的进程 fork 就会成为很大的服务器负担,想象一下数百个并发请求导致服务器 fork 出数百个进程就明白了。这也是为什么 cgi 一直背负性能低下,高资源消耗的恶名的原因。
CGI 模式安装
CGI 已经是比较老的模式了,这几年都很少用了,所以我们只是为了测试。
安装 CGI 模式需要注释掉这行
LoadModule php5_module modules/libphp5.so
如果不注释这行会一直走到 handler 模式。也就是模块模式。
然后在 httpd.conf 增加 action:
Action application/x-httpd-php /cgi-bin/
如果在 /cgi-bin/ 目录找不到 php-cgi,可自行从 php 的 bin 里面复制一个。
然后重启 apache,再打开测试页面发现 Server API 变成:CGI/FastCGI。说明成功切换为 cgi 模式。
问题
1、如果 cgi 程序放在 /usr/local/httpd/cgi-bin/ 里无法执行,遇到 403 或 500 错误的话
打开 apache 错误日志 有如下提示: Permission denied: exec of
可以检查 cgi 程序的属性,按 Linux contexts 文件 里定义的,/usr/local/httpd/cgi-bin/ 里必须是 httpd_sys_script_exec_t 属性。 通过 ls -Z 查看,如果不是则通过如下命令更改: chcon -t httpd_sys_script_exec_t /var/www/cgi-bin/*.cgi 如果是虚拟主机里的 cgi,则参考问题 2 使之能正常使用普通的功能后,再通过 chcon 设置 cgi 文件的 context 为 httpd_sys_script_exec_t 即可。
chcon -R -t httpd_sys_script_exec_t cgi-bin/
2、apache 错误提示:.... malformed header from script. Bad header=
根据提示说明有 header 有问题,查看文件输出的第一句话是什么,应该类似于如下
Content-type: text/plain; charset=iso-8859-1\n\n
或者 Content-type:text/html\n\n
注意:声明好 Content-type 后要输出两个空行。
3)apache 错误提示: Exec format error
脚本解释器设置错误。脚本第一行应该以 #!解释器路径 的形式,填写脚本解释器的路径,如果是 PERL 程序,常见的路径为:#!/usr/bin/perl 或 #!/usr/local/bin/perl,如果是 PHP 程序,不需要填写解释器路径,系统会自动找到 PHP。
fast-cgi 模式
fast-cgi 是 cgi 的升级版本,FastCGI 像是一个常驻(long-live)型的 CGI,它可以一直执行着,只要激活后,不会每次都要花费时间去 fork 一次,这是 CGI 最为人诟病的 fork-and-execute 模式。
FastCGI 的工作原理是
1、Web Server启动时载入 FastCGI 进程管理器,PHP 的 FastCGI 进程管理器是 PHP-FPM(php-FastCGI Process Manager)(IIS ISAPI 或 Apache Module)
2、FastCGI 进程管理器自身初始化,启动多个 CGI 解释器进程(在任务管理器中可见多个 php-cgi.exe)并等待来自 Web Server 的连接。
3、当客户端请求到达 Web Server 时,FastCGI 进程管理器选择并连接到一个 CGI 解释器。Web server 将 CGI 环境变量和标准输入发送到 FastCGI 子进程 php-cgi。
4、FastCGI 子进程完成处理后将标准输出和错误信息从同一连接返回 Web Server。当 FastCGI 子进程关闭连接时,请求便告处理完成。FastCGI 子进程接着等待并处理来自 FastCGI 进程管理器(运行在 WebServer 中)的下一个连接。在正常的 CGI 模式中,php-cgi.exe 在此便退出了。
在 CGI 模式中,你可以想象 CGI 通常有多慢。每一个 Web 请求 PHP 都必须重新解析 php.ini、重新载入全部 dll 扩展并重初始化全部数据结构。使用 FastCGI,所有这些都只在进程启动时发生一次。一个额外的好处是,持续数据库连接(Persistent database connection)可以工作。
Fastcgi 的优点
1、从稳定性上看,fastcgi 是以独立的进程池运行来 cgi,单独一个进程死掉,系统可以很轻易的丢弃,然后重新分配新的进程来运行逻辑
2、从安全性上看,Fastcgi 支持分布式运算,fastcgi 和宿主的 server 完全独立,fastcgi 怎么 down 也不会把 server 搞垮。
3)从性能上看,fastcgi 把动态逻辑的处理从 server 中分离出来,大负荷的 IO 处理还是留给宿主 server,这样宿主 server 可以一心一意作 IO,对于一个普通的动态网页来说,逻辑处理可能只有一小部分,大量的图片等静态
FastCGI 缺点
说完了好处,也来说说缺点。从我的实际使用来看,用 FastCGI 模式更适合生产环境的服务器。但对于开发用机器来说就不太合适。因为当使用 Zend Studio 调试程序时,由于 FastCGI 会认为 PHP进程超时,从而在页面返回 500 错误。这一点让人非常恼火,所以我在开发机器上还是换回了 ISAPI 模式。
安装 fastcgi 模式
安装 apache 路径是 /usr/local/httpd/
安装 php 路径是 /usr/local/php/
1)安装 mod_fastcgi
wget http://www.fastcgi.com/dist/mod_fastcgi-2.4.6.tar.gz tar zxvf mod_fastcgi-2.4.6.tar.gz cd mod_fastcgi-2.4.6 cp Makefile.AP2 Makefile
vi Makefile,编辑 top_dir = /usr/local/httpd
make make install
安装完后,/usr/local/httpd/modules/ 多出一个文件:mod_fcgid.so
2)重新编译 php
./configure --prefix=/usr/local/php --enable-fastcgi --enable-force-cgi-redirect --disable-cli make make install
这样编译后,在 PHP 的 bin 目录下的 php-cgi 就是 fastcgi 模式的 php 解释器了,安装成功后,执行 php -v 输出
PHP 5.3.2 (cgi-fcgi)
这里输出带了 cgi-fcgi
注意:
- 编译参数不能加 –with-apxs=/usr/local/httpd/bin/apxs 否则安装出来的 php 执行文件是 cli 模式的
- 如果编译时不加 --disable-cli 则输出
PHP 5.3.2(cli)
3)配置apache
需要配置 apache 来以 fastcgi 模式运行 php 程序
vi httpd.conf
我们使用虚拟机的方式实现:
#加载fastcgi模块 LoadModule fastcgi_module modules/mod_fastcgi.so #//以静态方式执行fastcgi 启动了10进程 FastCgiServer /usr/local/php/bin/php-cgi -processes 10 -idle-timeout 150 -pass-header HTTP_AUTHORIZATION <VirtualHost *:80> DocumentRoot /usr/local/httpd/fcgi-bin ServerName www.fastcgitest.com ScriptAlias /fcgi-bin/ /usr/local/php/bin/ # 定义目录映射 /fcgi-bin/ 代替 /usr/local/php/bin/ Options +ExecCGI AddHandler fastcgi-script .php .fcgi #.php 结尾的请求都要用 php-fastcgi 来处理 AddType application/x-httpd-php .php # 增加MIME类型 Action application/x-httpd-php /fcgi-bin/php-cgi # 设置 php-fastcgi 的处理器: /usr/local/php/bin/php-cgi <Directory /usr/local/httpd/fcgi-bin/> Options Indexes ExecCGI Order allow,deny allow from all </Directory> </VirtualHost>
或者
<IfModule mod_fastcgi> ScriptAlias /fcgi-bin/ "/usr/local/php/bin" # 定义目录映射 FastCgiServer /usr/local/php/bin/php-cgi -processes 10 # 配置 fastcgi server, <Directory "/usr/local/httpd/fcgi-bin/"> SetHandler fastcgi-script Options FollowSymLinks Order allow,deny Allow from all </Directory> AddType application/x-httpd-php .php # 增加MIME类型 AddHandler php-fastcgi .php #.php 结尾的请求都要用 php-fastcgi 来处理 Action php-fastcgi /fcgi-bin/php-cgi # 设置 php-fastcgi 的处理器 </IfModule>
4).restart 下 apache,查看 phpinfo,如果服务器信息是 Apache/2.2.11 (Unix) mod_fastcgi/2.4.6 之类的就说明安装成功了。
如果出现 403 的错误,查看下 /usr/local/httpd/fcgi-bin/ 是否有足够的权限。
或者将:
<Directory /> Options FollowSymLinks AllowOverride None Order deny,allow Deny from all </Directory>
改为:
<Directory /> Options FollowSymLinks AllowOverride None Order allow,deny Allow from all </Directory>
就可以了,运行 ps -ef|grep php-cgi 可以看见 10 个 fastcgi 进程在跑。
cli 模式
cli 是 php 的命令行运行模式,大家经常会使用它,但是可能并没有注意到(例如:我们在 linux 下经常使用 php -m 查找 PHP 安装了那些扩展就是 PHP 命令行运行模式;有兴趣的同学可以输入 php -h 去深入研究该运行模式)
模块模式
模块模式是以 mod_php5 模块的形式集成,此时 mod_php5 模块的作用是接收 Apache 传递过来的 PHP 文件请求,并处理这些请求,然后将处理后的结果返回给 Apache。如果我们在 Apache 启动前在其配置文件中配置好了 PHP 模块(mod_php5), PHP 模块通过注册 apache2 的 ap_hook_post_config 挂钩,在 Apache 启动的时候启动此模块以接受 PHP 文件的请求。
除了这种启动时的加载方式,Apache 的模块可以在运行的时候动态装载,这意味着对服务器可以进行功能扩展而不需要重新对源代码进行编译,甚至根本不需要停止服务器。我们所需要做的仅仅是给服务器发送信号 HUP 或者 AP_SIG_GRACEFUL 通知服务器重新载入模块。但是在动态加载之前,我们需要将模块编译成为动态链接库。
此时的动态加载就是加载动态链接库。 Apache 中对动态链接库的处理是通过模块 mod_so 来完成的,因此 mod_so 模块不能被动态加载,它只能被静态编译进 Apache 的核心。这意味着它是随着 Apache 一起启动的。
Apache 是如何加载模块的呢?我们以前面提到的 mod_php5 模块为例。首先我们需要在 Apache 的配置文件 httpd.conf 中添加一行:
该运行模式是我们以前在 Windows 环境下使用 apache 服务器经常使用的,而在模块化(DLL)中,PHP 是与 Web 服务器一起启动并运行的。(是 apache 在 CGI 的基础上进行的一种扩展,加快 PHP 的运行效率)
LoadModule php5_module modules/mod_php5.so
这里我们使用了LoadModule命令,该命令的第一个参数是模块的名称,名称可以在模块实现的源码中找到。第二个选项是该模块所处的路径。如果需要在服务器运行时加载模块,可以通过发送信号HUP或者AP_SIG_GRACEFUL给服务器,一旦接受到该信号,Apache将重新装载模块,而不需要重新启动服务器。
php 在 nginx 中运行模式(nginx+PHP-FPM )
使用 FastCGI 方式现在常见的有两种 stack:ligthttpd+spawn-fcgi,另外一种是 nginx+PHP-FPM(也可以用 spawn-fcgi)。
A、如上面所说该两种结构都采用 FastCGI 对 PHP 支持,因此 HTTPServer 完全解放出来,可以更好地进行响应和并发处理。因此 lighttpd 和 nginx 都有 small, but powerful 和 efficient 的美誉。
B、该两者还可以分出一个好坏来,spawn-fcgi 由于是 lighttpd 的一部分,因此安装了 lighttpd 一般就会使用 spawn-fcgi 对 php 支持,但是目前有用户说 ligttpd 的 spwan-fcgi 在高并发访问的时候,会出现上面说的内存泄漏甚至自动重启 fastcgi。即:PHP 脚本处理器当机,这个时候如果用户访问的话,可能就会出现白页(即 PHP 不能被解析或者出错)。
另一个:首先 nginx 不像 lighttpd 本身含带了 fastcgi(spawn-fcgi),因此它完全是轻量级的,必须借助第三方的 FastCGI 处理器才可以对 PHP 进行解析,因此其实这样看来 nginx 是非常灵活的,它可以和任何第三方提供解析的处理器实现连接从而实现对PHP的解析(在 nginx.conf 中很容易设置)。nginx 可以使用 spwan-fcgi(需要一同安装 lighttpd,但是需要为 nginx 避开端口,一些较早的 blog 有这方面安装的教程),但是由于 spawn-fcgi 具有上面所述的用户逐渐发现的缺陷,现在慢慢减少使用 nginx+spawn-fcgi 组合了。
C、由于 spawn-fcgi 的缺陷,现在出现了新的第三方(目前还是,听说正在努力不久将来加入到 PHP core 中)的 PHP 的 FastCGI 处理器,叫做 PHP-FPM(具体可以 google)。它和 spawn-fcgi 比较起来有如下优点:
- 由于它是作为 PHP 的 patch 补丁来开发的,安装的时候需要和 php 源码一起编译,也就是说编译到 php core 中了,因此在性能方面要优秀一些;
- 同时它在处理高并发方面也优于 spawn-fcgi,至少不会自动重启 fastcgi 处理器。具体采用的算法和设计可以 google 了解。
- 因此,如上所说由于 nginx 的轻量和灵活性,因此目前性能优越,越来越多人逐渐使用这个组合:nginx+PHP/PHP-FPM
总结
目前在 HTTPServer 这块基本可以看到有三种 stack 比较流行:
- Apache+mod_php5
- lighttp+spawn-fcgi
- nginx+PHP-FPM
三者后两者性能可能稍优,但是 Apache 由于有丰富的模块和功能,目前来说仍旧是老大。有人测试 nginx+PHP-FPM 在高并发情况下可能会达到 Apache+mod_php5 的 5~10 倍,现在 ginx+PHP-FPM 使用的人越来越多。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论