作者:轻易科技知行研发发 聂军瑞
PHP-FPM(FastCGI Process Manager)是一个PHPFastCGI进程管理器,从其英文名称和定义可以看出,FPM的核心功能就是进程管理。
FastCGI可以理解为一种协议,用于web服务器(nginx、Apache)和处理程序间进行通信,是一种应用层通信协议。
struct fpm_worker_pool_s {
struct fpm_worker_pool_s *next; //指向下一个worker pool
struct fpm_worker_pool_config_s *config; //conf配置:pm、max_children、start_servers...
...
struct fpm_child_s *children; //当前pool的worker链表
int running_children; //已启动的进程数
int idle_spawn_rate;//空闲率
int warn_max_children;//最大work值
struct fpm_scoreboard_s *scoreboard; //记录worker的运行信息,比如空闲、忙碌worker数
...
}
以上代码片段只保留了部分本文所需要的内容
worker的工作流程包含以下几个步骤
int main(int argc, char *argv[])
{
...变量定义,参数初始化
//注册SAPI
sapi_startup(&cgi_sapi_module);
...
//执行php_module_starup()
if (cgi_sapi_module.startup(&cgi_sapi_module) == FAILURE) {
return FPM_EXIT_SOFTWARE;
}
//初始化
if(0 > fpm_init(...)){
//记录日志并退出
return FPM_EXIT_CONFIG;
}
...
fpm_is_running = 1;//fpm运行状态标识
fcgi_fd = fpm_run(&max_requests);//进程初始化,调用fork()创建work进程
...
fcgi_init_request(&request, fcgi_fd); //初始化请求;
//此阶段的php_request_startup()会调用每个扩展的:PHP_RINIT_FUNCTION();
if (UNEXPECTED(php_request_startup() == FAILURE)) {
...
}
...
php_fopen_primary_script(&file_handle TSRMLS_CC); //打开脚本;
...
php_execute_script(&file_handle TSRMLS_CC); //执行脚本;
...
//worker进程退出
php_module_shutdown();
...
}
以上代码片段只保留了部分本文所需要的内容
static
模式:静态模式该模式比较简单,在启动时按照配置pm.max_children启动固定数量的的进程,这些进程阻塞进行请求的接收
方法执行流程:
fpm_run()->fpm_children_create_initial()->fpm_children_make()
启动fpm的时候会调用fpm_run
方法,而fpm_run
方法内部会调用子进程初始化方法fpm_children_create_initial
,
在该方法内部会调用fpm_children_make
方法创建worker进程。
ondemand
模式:按需分配模式fpm_pctl_on_socket_accept()
,部分代码如下:if (wp->config->pm == PM_STYLE_ONDEMAND) {
wp->ondemand_event = (struct fpm_event_s *)malloc(sizeof(struct fpm_event_s));
......
memset(wp->ondemand_event, 0, sizeof(struct fpm_event_s));
fpm_event_set(wp->ondemand_event,wp->listening_socket,FPM_EV_READ|FPM_EV_EDGE,fpm_pctl_on_socket_accept, wp);
......
}
以上代码片段只保留了部分本文所需要的内容
if (wp->running_children >= wp->config->pm_max_children) { //判断进程数是否超过最大限制
......
return;
}
for (child = wp->children; child; child = child->next) {
//fpm_request_is_idle函数返回return proc->request_stage == FPM_REQUEST_ACCEPTING
if (fpm_request_is_idle(child)) {
return; // FPM_REQUEST_ACCEPTING代表处于等待请求阶段
}
}
......
fpm_children_make(wp, 1, 1, 1);//创建work进程
以上代码片段只保留了部分本文所需要的内容
if (wp->config->pm == PM_STYLE_ONDEMAND) {
struct timeval last, now;
if (!last_idle_child) continue;//最后一个idle进程
......
// last.tv_sec为上次接收请求的时间
if (last.tv_sec < now.tv_sec - wp->config->pm_process_idle_timeout) {
last_idle_child->idle_kill = 1;
fpm_pctl_kill(last_idle_child->pid, FPM_PCTL_QUIT);
}
continue;
}
以上代码片段只保留了部分本文所需要的内容
dynamic
模式:动态模式dynamic模式,启动时分配固定数量的work进程,然后随着请求的增加会增加进程数,此模式下几个重要的配置项如下:
max_children
最大进程数
pm_max_spare_servers
允许最大的空闲进程数
min_spare_servers
允许最小的空闲进程数
start_servers
启动时的进程数
执行过程和ondemand模式类似,启动时主进程都会创建一个定时事件来定时检查work的运行状况,不同的是dynamic模式初始化的时候会创建一定数量的进程,而ondemand模式不会创建,部分代码如下:
if (idle > wp->config->pm_max_spare_servers && last_idle_child) {//空闲进程数大于配置的允许空闲最大进程数,则关闭进程
last_idle_child->idle_kill = 1;
fpm_pctl_kill(last_idle_child->pid, FPM_PCTL_QUIT);
......
}
if (idle < wp->config->pm_min_spare_servers) {//空闲进程数小于配置允许的最小进程数,则创建进程
if (wp->running_children >= wp->config->pm_max_children) {//如果达到最大上限,则不再创建
......
continue;
}
......
//此处计算出需要扩充的进程数,从wp->idle_spawn_rate, wp->config->pm_min_spare_servers – idle, wp->config->pm_max_children - wp->running_children三个中选出最小的一个作为本次要扩充的进程数进行扩充
if (wp->idle_spawn_rate >= 8) {
zlog(ZLOG_WARNING, "[pool %s] seems busy (you may need to increase pm.start_servers, or pm.min/max_spare_servers), spawning %d children, there are %d idle, and %d total children", wp->config->name, wp->idle_spawn_rate, idle, wp->running_children);
}
if (wp->idle_spawn_rate < FPM_MAX_SPAWN_RATE) {
wp->idle_spawn_rate *= 2;//当前进程分配基数小于配置值时候,会以2的倍数进行增长
}
......
}
以上就是本人对php-fpm以及php-fpm的三种运行方式的理解,三种运行方式中,
static
模式最简单,但是灵活性不够高
ondemand
模式相对static
模式比较复杂,会根据请求量的增加动态增加,但是处理完请求后不会立即释放,而是由定时事件定时的检测空闲到一定时间的进程才会释放
dynamic
模式类似于ondemand
模式,但进程的回收机制不同于ondemand
模式,会根据idle数量进行增加和减少worker数量
三种运行方式各有自己的优势,用哪种方式更合适,要根据自己的业务场景,选择合适的运行方式