0 目录
1 什么是lamp
lamp:linux + apache(httpd) + mysql(或mariadb) + php(或perl、python)
web服务端响应静态资源过程:
1、客户端向服务端发请求;
2、httpd从磁盘加载指定资源;
3、httpd把资源封装为http报文响应客户端。
由于httpd只能响应静态资源。如果有对动态资源的请求,需要其他应用程序执行,将执行结果返回给httpd,再由httpd返回给客户端浏览器。过程变为:
1、客户端向服务端发请求,请求一个动态资源;
2、httpd发现是动态资源,于是“转给”对应的CGI进程1;
3、CGI进程加载并运行对应的资源;
4、CGI进程把运行结果返回给httpd;
5、httpd把结果封装为http响应报文发给客户端。
如果请求的动态资源,需要访问数据库,则过程为:
这就是lamp了。
注意,不是CGI进程需要访问数据库,而是有的动态资源需要访问数据库(比如执行的php代码中有访问数据库的相关调用)。
2 CGI是什么
从上图可以看到httpd发现用户请求的是动态资源(通常是客户端上传需运行处理的表单内容),则会将请求转至CGI进程。那么httpd如何与CGI进程交互?通过CGI协议。
CGI:Common Gateway Interface,通用网关接口。是外部的进程(即CGI进程)与web服务进程通信的接口标准。可以看作是二者通信的协议。
反应在上图,第2、5步,就是通过CGI协议进行的。
3 httpd与CGI进程的交互模式及安装
下面CGI进程均以php来说明。
httpd与CGI进程通信有3种模式:CGI、php作为httpd的一个模块、fastCGI。
以下面代码,验证3种模式是否正常运行php代码:
[root@node1 ~]% cat /var/www/html/test.php
<h1>test.php</h1>
<?php
phpinfo();
?>
3.1 CGI模式
对于php,CGI进程就是php-cgi,它是php对于CGI协议的实现。
它的作用就是调用并初始化zend虚拟机(用于运行php的虚拟机程序)。不过这里不用关心这些细节。只要知道能够通过它运行php程序。
过程就是标题1中的:
不过这里更进一步说明第2、5步:
- 因为CGI进程不可能在没有请求的时候不停地运行,所以开始是不存在的。
- 到第2步,httpd首先创建一个子进程(fork),就是CGI进程。通过CGI协议,通知CGI进程要运行的资源。
- CGI进程运行对应的动态资源(第3步,或许也有第4步),而后把结果传给httpd(第5步)。最后CGI进程(也就是php-cgi)销毁。
对每个动态资源都是这样运行,如果请求多个动态资源,则创建多个CGI进程,运行完即销毁。这种方式效率很低。此处就不说安装了,在下一篇编译安装php时说明这种如何安装和配置。
3.2 作为httpd模块
令php作为httpd的一个模块,这样httpd就不用再创建cgi进程了。而是可自行处理对于php资源的请求。
过程变为:
使用yum安装php程序包,安装的就是这种模式:
[root@node1 ~]% yum install php -y
……
[root@node1 ~]% rpm -ql php
/etc/httpd/conf.d/php.conf
/usr/lib64/httpd/modules/libphp5.so
/var/lib/php/session
/var/www/icons/php.gif
可看到安装完成后,在/etc/httpd/conf.d下,有一个php.conf片段的配置文件。其中主要是说明,加载了php模块:
<IfModule prefork.c>
LoadModule php5_module modules/libphp5.so
</IfModule>
<IfModule worker.c>
LoadModule php5_module modules/libphp5-zts.so
</IfModule>
……
注意不同的MPM,加载的模块不一样。使用yum安装php,默认应当使用prefork。
安装前,访问页面为:
安装后,页面为:
注意,上述的安装和效果,都是在CentOS6.8实现。使用CentOS7.0根本不起作用,无法运行.php资源,不只为何。7.0上用yum装个httpd都不完整,还排查了半天。一口老血吐了一地。
3.3 fastCGI
3.3.1 机制
fastCGI也是一种协议,是改良的CGI。使得不必为每个动态请求创建CGI进程来处理,而是由一个主进程负责监听和分发请求,把请求交给子进程(真正的CGI进程)来处理。且在请求到来前会预创建几个子进程等待处理请求,类似httpd的prefork模式。而且也做成了服务,可以和httpd部署在不同服务器上。fastCGI服务默认监听在9000端口。
过程变为:
虽然它们也都可以放在同一主机(包括mysql服务),不过最好还是分开。
这样,前者都作为后者服务的客户端。
1、8步交互使用http协议;
2、7步交互使用fastCGI协议;
5步使用mysql协议。
注意,httpd要作为fastCGI的客户端,代理用户访问动态资源,要安装mod_fcgi_proxy模块。httpd2.2没有,如需使用fastCGI,需单独编译此模块安装;httpd2.4是收录此模块的。
且CentOS6的php版本是5.3.2之前的,如需使用fastCGI,也要打相应补丁;CentOS7的php是不需要的。
访问静态资源由httpd直接从磁盘获取资源响应,访问动态资源,就交由fastCGI响应给httpd,再由httpd给客户端。这就是所谓的动静分离。动态、静态资源分别放在不同的服务器。
CGI和fastCGI不可同时安装使用,常用的就是fastCGI。
3.3.2 安装、配置、效果
由于httpd2.2不支持,下面在两CentOS7上安装2,验证fastCGI效果。下一篇在CentOS6上使用编译安装。
php-fpm程序包就是fastCGI协议的实现。使用两台主机,192.168.0.171启动httpd,192.168.0.172启动php-fpm。
在主机192.168.0.171,可以看到httpd装载了proxy_fcgi_module模块:
[root@node4 ~]% httpd -M | grep proxy_fcgi_module
proxy_fcgi_module (shared)
说明httpd可以作为反向代理,访问后端的php-fpm服务器。
在主机192.168.0.172,安装php-fpm(注意光盘yum源没有):
[root@CloneNode4 ~]% yum install php-fpm
……
启动php-fpm服务,可以看到9000端口已监听:
[root@CloneNode4 ~]% systemctl start php-fpm.service
[root@CloneNode4 ~]% ss -tnl
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 128 *:22 *:*
LISTEN 0 128 127.0.0.1:9000 *:* # 注意php-fpm默认监听的IP是127.0.0.1,监听地址、端口在php-fpm的配置文件中设置
LISTEN 0 128 :::22 :::*
在192.168.0.172,创建一个php页面:
[root@CloneNode4 ~]% cat /var/www/html/test.php
<h1>test.php@192.168.0.172</h1>
<?php
phpinfo();
?>
此时,这两主机并没有什么联系。
因为目的是httpd主机上收到php资源的请求,就转给php-fpm主机。所以在httpd还要有如下配置:
ProxyRequests off # 表示关闭httpd正向代理。正向代理和反向代理不可同时启用。此处是利用httpd做反向代理请求后端php-fpm的资源,所以使用反向代理
ProxyPassMatch ^/(.*\.php)$ fcgi://192.168.0.172:9000/var/www/html/$1 # 指定httpd在收到何种URL请求时,转给后端php-fpm主机处理。这里指定的是请求的资源以.php结尾
# 前者使用正则表达式匹配URL。注意,匹配的不是完整的URL,而是相对于DocumentRoot的路径
# 后者表示把前者匹配到的URL请求,转换为请求php-fpm主机上的哪个资源。协议使用fcgi;$1表示前者括号中的内容,即(.*\.php)。因为往往要把php-fpm主机上的动态资源名与请求的资源名搞成一致的。
# 注意,fcgi协议指定的路径,是运行php-fpm主机上的文件系统路径。对于本例来说,正好也是把.php资源放在172主机的/var/www/html下了,所以就使用这个路径
上述内容写在httpd主配置文件或在/etc/httpd/conf.d下创建一个单独的文件都行,后者显得更清晰一些。
那么现在就配置完成了,分别在两主机编辑页面内容以验证效果。
在192.168.0.171主机,编辑一个静态资源页面:
[root@node4 ~]% cat /var/www/html/test.html
<h1>test page@192.168.0.171</h1>
在192.168.0.172主机,编辑一个php页面:
[root@CloneNode4 ~]% cat /var/www/html/test.php # 根据上述配置,应把这个页面放在/var/www/html
<h1>test.php@192.168.0.172</h1>
<?php
phpinfo();
?>
效果:
向192.168.0.171,请求test.html页面结果:
仍向192.168.0.171,请求test.php页面结果:
可以看到此时serverAPI是fastCGI。
由此,实现了静态、动态资源放置在不同主机。在客户端看来,httpd也有了“处理”动态资源的能力。
3.3.3 php-fpm配置文件
php-fpm作为一个服务,有自己的配置文件3。主配置文件/etc/php-fpm.conf和/etc/php-fpm.d/下所有以.conf结尾的文件。
以分号表示注释信息,分号后有空格的,表示纯说明信息;分号后无空格的,表示可启用。
对于响应httpd的反代请求php-fpm服务来讲,主要由/etc/php-fpm.d/下的/etc/php-fpm.d/www.conf设置。
/etc/php-fpm.d下各.conf文件生效,是因为主配置文件中定义了:
include=/etc/php-fpm.d/*.conf
还定义了pid文件和错误日志文件等。主配置文件一般不需要修改,此处不述了。
下面对/etc/php-fpm.d/www.conf需改动的地方进行说明:
1、指定监听的IP和端口:
listen = 192.168.0.172:9000 # 默认是127.0.0.1:9000。用于当httpd和php-fpm部署于同一主机时。
# 尤其注意,此处地址和端口是什么,在httpd配置文件中设置反向代理指向的IP就是什么。假设172主机上既部署了httpd也有php-fpm,则这里listen写的是127.0.0.1,那么ProxyPassMatch的指向也要是127.0.0.1,总之就是要一致。这也容易理解。
2、访问控制,设置哪些主机可以访问:
listen.allowed_clients = 192.168.0.171 # 默认也是只有127.0.0.1。这里当然是要允许前端的运行httpd的主机连接,此处为171主机
3、设置CGI进程数的管理方式:
pm = dynamic # 可以是static或dynamic,默认是dynamic
static:CGI主进程创建一定数量的子进程,不论httpd发来的请求数是多少。
子进程数量由参数pm.max_children指定。
如果请求数超出指定的数量,则需要等待前面的请求响应完成;
即便请求数远小于指定的数量,子进程数也不会变化,还是这么多。
dynamic:动态地创建子进程。类似于httpd的prefork模型的管理方式。
pm.max_children,用于设置最大并发请求;
pm.start_servers,用于设置php-fpm刚启动时,启动的CGI子进程数;
pm.min_spare_servers,最小空闲进程数
pm.max_spare_servers,最大空闲进程数
pm.max_requests,用于指定每个子进程最多处理多少个请求即销毁。默认是不限制的
4 CGI进程与mysql的交互
标题3中说明了httpd如何与CGI进程交互。如果请求的动态资源是要访问mysql的,则CGI进程还要与mysql交互。
不论httpd采用CGI、作为一个模块、还是fastCGI模式与CGI进程交互,CGI进程与mysql的交互是不受这个影响的。所以下面仍以httpd按fastCGI模式与CGI进程交互的情形,来验证CGI进程与mysql交互的效果。
最终要达到的效果就是使用fastCGI模式的lamp:
使用192.168.0.171作为httpd服务器;192.168.0.172作为php-fpm服务器;192.168.0.173作为mariadb服务器4。
三者都使用CentOS7。
httpd服务器和php-fpm服务器,配置同上述标题3.3.2中的。下面先在173主机启动mariadb:
[root@CloneNode42 ~]% yum install mariadb-server
……
[root@CloneNode42 ~]% systemctl start mariadb.service
[root@CloneNode42 ~]% ss -tnl
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 50 *:3306 *:*
LISTEN 0 128 *:22 *:*
LISTEN 0 100 127.0.0.1:25 *:*
LISTEN 0 128 :::22 :::*
LISTEN 0 100 ::1:25 :::*
可以看到3306端口监听了,mariadb已启动。
重点在于,php的CGI进程要能与mysql通信(即图中第5步),需要安装php-mysql程序包。它可看作php与mysql通信的驱动程序5。
于是在172主机,安装php-mysql:
[root@CloneNode4 ~]% yum install php-mysql
……
[root@CloneNode4 ~]% systemctl restart php-fpm.service # 注意,安装后要重启php-fpm服务,如果本来是开着的话。如果php是作为httpd的一个模块的方式,则要重启httpd
这样lamp环境就部署完毕了。下面验证是否能与mysql通信。
运行的php动态资源是否需要访问mysql,取决于代码中是否调用了访问mysql的函数。用下面的页面内容,验证172主机是否与mysql正常通信:
[root@CloneNode4 ~]% cat /var/www/html/test_mysql.php
<h1>test_mysql</h1>
<?php
$conn = mysql_connect('192.168.0.173','testuser','testpass'); # 连接mysql的函数。意思是使用用户testuser、密码testpass,访问192.168.0.173主机上的mysql
if ($conn)
echo OK; # 如能与mysql通信则输出OK
else
echo Failure; # 如不能与mysql通信则输出Failure
?>
因为是通过172主机访问173的mysql服务,所以在173上要有相应授权:
MariaDB [(none)]> GRANT ALL PRIVILEGES ON *.* TO testuser@192.168.0.172; # 为方便起见,授予由172主机访问的用户testuser对所有库的所有表所有权限
授权后可直接在172主机上手动登录下,以确保授权成功,这里直接验证上述页面效果。
在mysql启动的情况下:
在173主机关闭mysql服务后:
(完)
- CGI进程,是指专门用于接收并处理由web服务进程(httpd、nginx)发来的动态资源请求的进程。并不是说CGI作为一个程序来运行了,因为CGI不是程序,而是一种协议。
对于php资源来说,CGI进程指的就是php-cgi,用于执行CGI脚本。
即,把用户请求的php动态资源称为CGI脚本(需要被运行);用于运行CGI脚本的进程称为CGI进程。 ↩ - 笔者在CentOS7上使用yum安装httpd,启动时报错”Job for httpd.service failed because the control process exited with error code. See “systemctl status httpd.service” and “journalctl -xe” for details.”
使用httpd -t或-version等命令,提示”httpd: error while loading shared libraries: /lib64/libapr-1.so.0: file too short”
不论yum源是光盘镜像还是网络镜像站,都是这个问题。发现/lib64/libapr-1.so.0是空的,它是由apr安装生成的库文件。使用”yum reinstall apr”重装了apr,问题解决。所以,之前用yum安装httpd也会自动安装apr,但是错误的。
后来发现不是安装包的问题,可能是yum源的问题,装的很多包都有问题。 ↩ - 这里说的是php-fpm这个服务的配置文件,并不是php的配置文件。
php有自己的配置文件/etc/php.ini和/etc/php.d/*.ini,用于定义php程序的运行的各配置。 ↩ - CentOS7上是mariadb,CentOS6上是mysql。mariadb是mysql的一个衍生版,操作兼容mysql。 ↩
- 因为与mysql通信要使用mysql协议,所以要有程序来实现。php-mysql就是php与mysql通信协议的实现。 ↩