PHP-FPM
PHP-FPM(PHP FastCGI Process Manager),是用于管理 PHP 进程池的软件,用于接收和处理来自 Web 服务器(如Nginx)的请求。PHP-FPM会创建一个主进程(通常以操作系统中根用户的身份运行),控制何时以及如何把 HTTP 请求转发给一个或多个子进程处理。PHP-FPM 主进程还控制着什么时候创建和销毁 PHP 子进程。PHP-FPM 进程池中的每个进程存在的时间都比单个 HTTP 请求长,可以处理10、50、100或更多的 HTTP 请求。
CGI、FastCGI 与 PHP-FPM 介绍
PHP-FPM 的全称是 PHP FastCGI Process Manager,即 PHP FastCGI 进程管理器,要了解 PHP-FPM ,首先要看看 CGI 与 FastCGI 的关系。
CGI 的英文全名是 Common Gateway Interface,即通用网关接口,是 Web 服务器调用外部程序时所使用的一种服务端应用的规范。
早期的 Web 通信只是按照客户端请求将保存在 Web 服务器硬盘中的数据转发过去而已,这种情况下客户端每次获取的信息也是同样的内容(即静态请求,比如图片、样式文件、HTML文档),而随着 Web 的发展,Web 所能呈现的内容更加丰富,与用户的交互日益频繁,比如博客、论坛、电商网站、社交网络等,这个时候仅仅通过静态资源已经无法满足 Web 通信的需求,所以引入 CGI 以便客户端请求能够触发 Web 服务器运行另一个外部程序,客户端所输入的数据也会传给这个外部程序,该程序运行结束后会将生成的 HTML 和其他数据通过 Web 服务器再返回给客户端(即动态请求,比如基于 PHP、Python、Java 实现的应用)。利用 CGI 可以针对用户请求动态返回给客户端各种各样动态变化的信息。
FastCGI 顾名思义,是 CGI 的升级版本,为了提升 CGI 的性能而生,CGI 针对每个 HTTP 请求都会 fork 一个新进程来进行处理(解析配置文件、初始化执行环境、处理请求),然后把这个进程处理完的结果通过 Web 服务器转发给用户,刚刚 fork 的新进程也随之退出,如果下次用户再请求动态资源,那么 Web 服务器又再次 fork
一个新进程,如此周而复始循环往复。而 FastCGI 则会先 fork
一个 master
进程,解析配置文件,初始化执行环境,然后再 fork
多个 worker
进程(与 Nginx 有点像),当 HTTP 请求过来时,master
进程将其会传递给一个 worker
进程,然后立即可以接受下一个请求,这样就避免了重复的初始化操作,效率自然也就提高了。而且当 worker 进程不够用时,master 进程还可以根据配置预先启动几个 worker 进程等着;当空闲 worker 进程太多时,也会关掉一些,这样不仅提高了性能,还节约了系统资源。
这样一来,PHP-FPM 就好理解了,FastCGI 只是一个协议规范,需要每个语言具体去实现,PHP-FPM 就是 PHP 版本的 FastCGI 协议实现,有了它,就是实现 PHP 脚本与 Web 服务器(通常是 Nginx)之间的通信,同时它也是一个 PHP SAPI,从而构建起 PHP 解释器与 Web 服务器之间的桥梁。
PHP-FPM 负责管理一个进程池来处理来自 Web 服务器的 HTTP 动态请求,在 PHP-FPM 中,master 进程负责与 Web 服务器进行通信,接收 HTTP 请求,再将请求转发给 worker 进程进行处理,worker 进程主要负责动态执行 PHP 代码,处理完成后,将处理结果返回给 Web 服务器,再由 Web 服务器将结果发送给客户端。这就是 PHP-FPM 的基本工作原理。
安装
# Ubuntu
sudo apt-get install python-software-properties
sudo add-apt-repository ppa:ondrej/php-5.6
sudo apt-get update
sudo apt-get install php5-fpm php5-cli php5-curl
php5-gd php5-json php5-mcrypt php5-mysqlnd
全局配置
在 Ubuntu 中,PHP-FPM 的主配置文件是 /etc/php5/fpm/php-fpm.conf
。
下面是 PHP-FPM 最重要的全局配置,建议把默认值改成下面的值:
emergency_restart_threshold = 10
在指定时间内,如果失效的 PHP-FPM 子进程数超过这个值,PHP-FPM 主进程就优雅重启emergency_restart_interval = 1m
设定emergency_restart_threshold
采用的时间跨度
这两个配置是 PHP-FPM 进程的基本安全保障,能解决简单的问题,但是不能解决由拙劣的 PHP 代码引起的重大问题。
注:PHP-FPM 的全局设置详细信息参见http://php.net/manual/zh/install.fpm.configuration.php
配置进程池
PHP-FPM 配置文件的其它配置内容是一个名为 Pool Definitions 的区域,这个区域里的配置用于设置每个 PHP-FPM 进程池,PHP-FPM 进程池是一系列相关的 PHP 子进程。通常,一个 PHP 应用有自己的一个 PHP-FPM 进程池。
在 Ubuntu 中,Pool Definitions 区域只有下面这行内容:
include=/etc/php5/fpm/pool.d/*.conf
这行代码的作用是让 PHP-FPM 加载 /etc/php5/fpm/pool.d
目录中的各个进程池定义文件。进入这个目录,应该会看到一个名为 www.conf
的文件,这是名为 www
的默认 PHP-FPM 进程池配置文件。
各个 PHP-FPM 进程池都以指定的操作系统用户和用户组的身份运行。我们要配置默认的 www
PHP-FPM 进程池,让它以 deploy
用户和用户组的身份运行:
user = deploy
group = deploy
listen = 127.0.0.1:9000
:PHP-FPM 进程池监听的IP地址和端口号,让 PHP-FPM 只接受 Nginx 从这里传入的请求,127.0.0.1:9000
让指定的 PHP-FPM 进程池监听本地端口 9000 进入的连接。listen.allowed_clients = 127.0.0.1
:可以向这个 PHP-FPM 进程池发送请求的IP地址(一个或多个),为了安全,我把这个设置为127.0.0.1
,即只有当前设备能把请求转发给这个 PHP-FPM 进程池。pm.max_children = 15
:这个设置设定任何时间点 PHP-FPM 进程池中最多能有多少个进程。这个设置没有绝对正确的值,你应该测试你的 PHP 应用,确定每个 PHP 进程需要使用多少内存,然后把这个设置设定为可用内存能容纳的 PHP 进程总数。pm.start_servers = 3
:PHP-FPM 启动时 PHP-FPM 进程池立即可用的进程数。pm.min_spare_servers = 2
:PHP 应用空闲时 PHP-FPM 进程池中可以存在的进程数量最小值。这个设置的值一般和 pm.start_servers 一样。pm.max_spare_servers = 4
:PHP 应用空闲时 PHP-FPM 进程池中可以存在的进程数量最大值。pm.max_requests = 1000
:回收进程之前,PHP-FPM 进程池中各个进程最多能处理的 HTTP 请求数量。这个设置有助于避免 PHP 扩展或库因为编写拙劣而导致内存不断泄露。slowlog = /path/to/slowlog.log
:这个设置的值是一个日志文件在文件系统中的绝对路径。这个日志文件用于记录处理时间超过n
秒的 HTTP 请求信息,以便找出 PHP 应用的瓶颈,进而进行调试。需要注意的是,PHP-FPM 进程池所属的用户和用户组要对这个日志文件有写权限。request_slowlog_timeout = 5s
:如果当前 HTTP 请求的处理时间超过指定的值,就把请求的回溯信息写入slowlog
设置指定的日志文件。
编辑之后保存文件,然后重启 PHP-FPM 主进程:
sudo service php5-fpm restart
注:更多配置信息参见http://php.net/manual/zh/install.fpm.configuration.php
Nginx
Nginx 是 Web 服务器,类似 Apache,不过更容易配置,而且运行时占用内存更少。这里我们不深入探讨 Nginx,只是告诉你如何安装,以及如何把相应的请求转发给 PHP-FPM 进程池。
安装
# Ubuntu
sudo add-apt-repository ppa:nginx/stable
sudo apt-get update
sudo apt-get install nginx
# CentOS
sudo yum install nginx
sudo systemctl enable nginx.service
sudo systemctl start nginx.service
虚拟主机
接下来,我们需要为 PHP 应用配置一个虚拟主机。虚拟主机是一系列设置,用于告知 Nginx PHP 应用的域名、在文件系统中的位置,已经如何把 HTTP 请求转发给 PHP-FPM 进程池:
server {
listen 80;
server_name example.com;
index index.php
client_max_body_size 50M;
error_log /home/deploy/apps/logs/example.error.log;
access_log /home/deploy/apps/logs/example.access.log;
root /home/deploy/apps/example.com/current/public;
location / {
try_files $uri $uri/ /index.php$is_args$args;
}
location ~ .php {
try_files $uri = 404;
fastcgi_split_path_info ^(.+.php)(/.+)$;
include fastcgi_params;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param SCRIPT_NAME $fastcgi_script_name;
fastcgi_index index.php;
fastcgi_pass 127.0.0.1:9000;
}
}
下面简要说明下每个虚拟主机设置:
listen
:设置 Nginx 监听哪个端口进入的 HTTP 请求。一般 HTTP 请求从80
端口进入,HTTPS 请求从443
端口进入。server_name
:用于识别虚拟主机的域名,这个要设置为你的应用使用的域名,而且域名要指向服务器的 IP 地址。如果 HTTP 请求头中Host
首部的值和虚拟主机中server_name
的值相匹配,Nginx 就会把这个 HTTP 请求发送给这个虚拟主机。index
:HTTP 请求 URI 没有指定文件时的默认文件。client_max_body_size
:对这个虚拟主机来说,Nginx 接受 HTTP 请求主体长度的最大值。如果请求主体长度超过这个值,Nginx 会返回 4XX 响应。error_log
:这个虚拟主机错误日志文件在文件系统中的路径。access_log
:这个虚拟主机访问日志文件在文件系统中的路径。root
:应用根目录路径。
除了上述设置外,还有两个 location
块,这两个 location
块的作用是告诉 Nginx 如何处理匹配指定 URL 模式的 HTTP 请求:
location / {}
使用 try_files 指令查找匹配所请求 URI 的文件,如果没有找到,再查找匹配所请求 URI 的目录,如果目录也没有找到,则把 HTTP 请求的 URI 重写为 /index.php
,如果有查询字符串的话,还会把查询字符串附加到 URI 的末尾,这个重写的 URL,以及所有以 .php
结尾的URI,都由 location ~ .php {}
块管理。
location ~ .php {}
块把 HTTP 请求转发给 PHP-FPM 进程池进行处理,在这个块中我们把 PHP 请求转发到端口 9000
让 PHP-FPM 处理请求。
注:location ~ .php {} 其它几行的作用是避免潜在的远程代码执行攻击:https://nealpoole.com/blog/2011/04/setting-up-php-fastcgi-and-nginx-dont-trust-the-tutorials-check-your-configuration/
在 Ubuntu 中我们必须执行以下命令,在 /etc/nginx/sites-enable
目录下创建虚拟主机配置文件的符号链接:
sudo ln -s /etc/nginx/sites-available/example.conf /etc/nginx/sites-enabled/example.conf;
最后,执行下述命令,重启 Nginx:
# Ubuntu
sudo service nginx restart
# CentOS
sudo systemctl restart nginx.service
现在服务器可以运行 PHP 应用了。Nginx 配置有很多种,这里只是最基本的配置,更多配置信息,请参考以下资源:
自动配置服务器
配置服务器是个很漫长的过程,而且很枯燥,如果手动配置很多服务器,这种感觉会更强烈。幸好,有些工具可以帮我们自动配置服务器,下面是几个流行的服务器配置工具:
- Puppet (https://puppet.com/)
- Chef (https://www.chef.io/)
- Ansible (https://www.ansible.com/)
- SaltStack (https://saltstack.com/)
各个工具之间有所差别,但是目标是一致的 —— 根据精确的规则自动配置新服务器。如果要管理多台服务器,我强烈建议研究使用配置工具,这样可以节省大量时间。
委托别人配置服务器
还有些在线服务可以代你配置服务器,如 Laravel 生态里的 Forge。