PHP-FPM 是一个简单可靠的 FastCGI 进程管理器(FastCGI Process Manager),从 PHP 5.3.3 开始就成为了 PHP 的内置管理器。Apache 官方网站也提供了配置 Apache httpd 2.4.x 使用 mod_proxy_fcgi 和 PHP-FPM 运行 php 程序的基本方法和设置运行方式的简单介绍。然而,在实际操作过程中发现这个东西还挺麻烦的,因此汇总记录下查找的资料以备查阅。

为了省事,我们以官方文档为模板进行操作。基本的安装和配置已经在“更改 LAMP 执行方式为 PHP-FPM”一文中有记录。这里仅记录设置代理方式的问题。

使用 PHP-FPM 就意味着不用 Apache 内置的 mod_php,也就是要在 Apache 之外处理 php 程序的解释运行问题。虽然看起来是多引入了一个额外的程序 PHP-FPM,既占 CPU 又占内存,但实际上这样一来,因为 Apache 可以专心处理除 php 之外的静态网页及元素,反而 httpd 进程本身占用的 CPU 和内存可以显著降低,从而从整体上降低资源消耗。

快捷目录:

1. PHP-FPM 监听方式

官方文档提到的 PHP-FPM 监听方式(接收 Apache 转过去的处理 PHP 的请求的方式)有 2 个。这是在 PHP-FPM 的 pool 配置文件中设置的监听方式,例如:

```plaintext

listen = 127.0.0.1:9000

listen = /var/run/php-fpm/php-fpm.sock

```

2. Apache 发送 PHP 处理请求的方式

原来的 mod_php 采用 SetHandler 的方式处理 php 文件并不需要特别的设置,因为在安装 PHP 的时候会自动在 Apache 的配置文件目录写入一个 php.conf 的配置文件,里面有告诉 Apache 处理 php 需要的操作:

以下是重构后的内容:

Apache 的 mod_php 和 PHP-FPM 配置方式有很多种,其中一种简单的、全局的处理方式是通过 SetHandler 指令实现的。这种方式在收到每个请求之后才处理,适用于全局设置。然而,对于 PHP-FPM 来说,这种方式需要到 Apache 2.4.9 版本中才能使用。

在 Apache 2.4.10 版本中,关于 mod_php 和 PHP-FPM 的配置和冲突规避的逻辑判断已经默认设置好。目前 Apache 转发代理的方式有三种:ProxyPass、ProxyPassMatch 和带 [P] 参数的 mod_rewrite。这三种方式组合起来共有 6 种不同的配置方法。为了简化配置过程,官网提供了一个复杂度和灵活性都居中的解决方案作为例子(ProxyPassMatch)。

下面分别针对 SetHandler、ProxyPassMatch、ProxyPass 和 mod_rewrite 这四种处理方式进行举例说明。

3.1 SETHANDLER

SetHandler 是一种适应性最强的处理方式,部署一次之后,所有的虚拟主机 里的 PHP 文件都会由它来处理。这种方式相当于一劳永逸地解决了 PHP 文件处理问题,与 mod_php 的便利程度相当。然而,这种方式无法针对每个 VirtualHost 来控制 PHP-FPM 的运行用户和资源分配。需要注意的是,SetHandler 需要到 Apache 2.4.9 版本才能使用,或者可以自行打补丁。

首先,我们需要在 `/etc/php-fpm.d/www.conf` 文件中设置监听方式。在这个例子中,我们将使用 IP:Port 监听方式,即:

```

listen = 127.0.0.1:9000

```

接下来,我们需要在 `vhost.conf` 文件中设置一个全局的代理。在所有 `` 标签之前添加以下内容:

```

<FilesMatch \.php$> SetHandler "proxy:fcgi://127.0.0.1:9000"</FilesMatch>

```

或者,您可以将此设置直接放在 `php.conf` 文件中。

完成上述设置后,您的 PHP-FPM 就配置好了。这就是为什么水景一页非常喜欢这个简单的配置方法。

如果您希望采用 UDS(Unix Domain Socket)方式监听,例如:

```

listen = /var/run/php-fpm/php-fpm.sock

```

您可以按照以下步骤进行配置:

1. 在 `Proxy` 标签中声明一个参数(不关心具体是哪个),否则代理无法提前注册。示例代码如下:

```

<Proxy "unix:/var/run/php-fpm/php-fpm.sock|fcgi://php-fpm"> # we must declare a parameter in here (doesn't matter which) or it won't register the proxy ahead of time ProxySet disablereuse=off</Proxy># Redirect to the proxy<FilesMatch \.php$> SetHandler proxy:fcgi://php-fpm</FilesMatch>

```

2. 测试配置。但是,水景一页发现在 Apache 启动时出现错误,提示 `ProxySet URL must be absolute!`,而如果是 `unix:///var/run/php-fpm/php-fpm.sock|fcgi://php-fpm`,就没有问题。此外,网页出现白板,页面源文件显示 php 没有运行而是直接显示源代码。对于 Apache 2.4.10,您可以尝试使用上述配置方法。

重构内容:

## 3.2 PROXYPASSMATCH

在前面的文章中,我们已经介绍了如何将 mod_php 切换到 PHP-FPM。关键点在于:

1. 在每个 `` 中分别设置;

2. 即使使用相同的 pool,也需要在设置的时候修改 pool 后面的 webroot 路径;

3. 有些特殊的网站网页地址 url 与系统文件路径 dir 之间匹配的问题,简单的一条 ProxyPassMatch 可能还不够,比如那个设置 phpMyAdmin 的例子(见上文)。

假设采用 IP:Port 监听方式,同 3.1。然后,在每个 `` 里面加入以下内容:

```apache

ProxyPassMatch ^/(.*\.php(/.*)?)$ fcgi://127.0.0.1:9000/path/to/webroot/$1

```

特别注意的是,红色字体部分需要与每个 `` 中的 DocumentRoot 后的路径一致!

ProxyPassMatch 只满足特定正则模式的内容才会匹配并执行此规则。这里的模式是:

```regex

^/(.*\.php(/.*)?)$

```

从网站(虚拟主机 `` 的根目录开始,匹配任何以 .php 结尾,或者在 .php 之后紧跟一个 / 再跟别的内容的路径。)

括号里的内容可以用 `$1` 来表示,以方便后面引用它。通过 mod_proxy_fcgi 来转发的代理,使用 fastCGI 协议,转到 PHP-FPM 监听的端口。改变 IP 地址和/或端口号就可以要转到的不同的 pool。用这个可以实现服务器分流、均衡等。

在 Nginx 配置文件中,我们可以使用以下方法将 PHP 请求代理到 FastCGI 服务器:

1. 首先,确保你的虚拟主机配置正确,并且 DocumentRoot 路径与 FastCGI 服务器的路径匹配。例如:

```nginx

server {

listen 80;

server_name example.com;

root /path/to/your/documentroot/;

index index.php index.html index.htm;

location ~ \.php$ {

fastcgi_pass fcgi://127.0.0.1:9000;

fastcgi_index index.php;

fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

include fastcgi_params;

}

}

```

2. 如果你的服务器没有设置 `DirectoryIndex`,你需要手动设置它。例如:

```nginx

location ~ \.php$ {

fastcgi_pass fcgi://127.0.0.1:9000;

fastcgi_index index.php;

fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;

include fastcgi_params;

}

```

3. 如果你的服务器支持 UDS(Unix Domain Sockets),你可以使用 `ProxyPass` 和 `ProxyPassMatch`。例如:

```nginx

location ~ \.php$ {

ProxyPass uWSGI://127.0.0.1:9000;

ProxyPassReverse uWSGI://127.0.0.1:9000;

}

```

4. 如果以上方法都不支持持久连接(persistent connections),你可以尝试使用 `mod_rewrite`。但是需要注意的是,这种方法不支持持久连接。例如:

```nginx

location ~ \.php$ {

rewriteEngine On;

RewriteRule ^(.*)$ fastcgi://127.0.0.1:9000/path/to/your/documentroot/$1 [P,L] last;

}

```

水景一页无法使找到的几个示例正常工作,这会导致 PHP 源代码直接展示在网页上,或者在网页的源文件中。既然这样不好,就不再浪费版面了。如果大家有兴趣,可以查看以下几个示例:

1. http://serverfault.com/questions/398834/understanding-apache-2-4-mod-proxy-fcgi-and-rewriterules-in-htaccess

2. http://serverfault.com/questions/450628/apache-2-4-php-fpm-proxypassmatch

3. http://www.gossamer-threads.com/lists/apache/users/409168#409168

经过这么多的尝试和探索,我来给大家一个建议吧,以免大家像我一样走弯路。从性能和便利程度来看,目前还是建议使用 ProxyPassMatch,直到 Apache 2.4.10 版本,然后一次性切换到 SetHandler。这是一个比较好的过渡方案。但是,考虑到 CentOS 上游 RedHat 官方的 backporting 政策,Apache 2.4.10 可能需要等到下一次大的系统升级(例如 CentOS 8)时才会加入。

此外,关于 PHP-FPM 处理方式中的一个安全警告,也值得大家关注。