PHP-FPM进程模型

php-fpm

php-fpm master进程负责创建和管理woker进程,同时负责监听listen连接,master进程是多路复用的;woker进程负责accept请求连接,同时处理请求,一个woker进程可以处理多个请求(复用,不需要每次都fork一个woker进程),但一个woker进程一次只能处理一个请求(请区别理解和前一句话的意思)。以下伪代码说明一下关系:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$socket = stream_socket_server('tcp://127.0.0.1:8080');
for ($i=0; $i < 5; $i++) {
if(pcntl_fork() == 0) {
while (true) {
// woker进程负责accept请求,处理请求
$client = stream_socket_accept($socket);
if(!$client) {
continue;
}
$request = fread($client);
$response = do_request($request);
fwrite($client, $response);
fclose($client);
}
}
}

好处是,复用worker进程,woker进程可以处理多个请求,试想一下,我们使用mysql、redis长连接的时候,连接状态是保存在woker进程的,这样后面的请求就可以直接使用,不需要每次都经历TCP握手。

验证

为了证实,一个woker进程一次只处理一个请求,可以把php-fpm配置的pm.max_children改成1,即只有一个woker进程处理请求,在这种请求下实现一个计数器,每个请求都加1,数据保存在文件中,读写文件不上文件锁,使用ab进行压测,查看最后结果加上失败次数是否和压测总请求量相等。

1
2
3
$counter = file_get_contents('./counter.txt');
$counter = $counter? ++$counter: 1;
file_put_contents('./counter.txt', $counter);

结语

php-fpm.conf有个配置项events.mechanism = epoll,指定事件驱动模型,但是这个是配置指定master进程的,不是woker进程的,以上例子可以看出,woker进程是单进程单线程模型。复用woker进程,处理多个请求好处是挺多的,每个woker进程启动和销毁,都需要执行一遍php扩展的模块初始化、销毁的工作,这部分开销还是挺大的。还有就是mysql、redis长连接等,因为php没有连接池,每次都执行短连接,TCP三次握手耗时,频繁关闭连接还会造成大量TIME_WAIT,造成服务器端口等资源大量消耗。