fpm worker 进程的生命周期

PM(FastCGI 进程管理器)用于替换 PHP FastCGI 的大部分附加功能,对于高负载网站是非常有用的。



它的功能包括:



支持平滑停止/启动的高级进程管理功能;



可以工作于不同的 uid/gid/chroot 环境下,并监听不同的端口和使用不同的 php.ini 配置文件(可取代 safe_mode 的设置);



stdout 和 stderr 日志记录;



在发生意外情况的时候能够重新启动并缓存被破坏的 opcode;



文件上传优化支持;



“慢日志” - 记录脚本(不仅记录文件名,还记录 PHP backtrace 信息,可以使用 ptrace或者类似工具读取和分析远程进程的运行数据)运行所导致的异常缓慢;



fastcgi_finish_request() - 特殊功能:用于在请求完成和刷新数据后,继续在后台执行耗时的工作(录入视频转换、统计处理等);



动态/静态子进程产生;



基本 SAPI 运行状态信息(类似Apache的 mod_status);



基于 php.ini 的配置文件。

标准的PHP单进程CLI和CGI生命周期。php进程启动,需要zend core、Module的Init(MINIT)、Request 的Init(RINIT) 这样的。一个进程只服务一次命令行或HTTP请求,就退出。



而FastCGI/php-fpm 就是改造后的多进程的 CGI,类似于资源池,预先启动 100个 php-fpm 进程,提前MINT,nginx的请求来了,直接进入 RINIT -> RSHUTDOWN 循环。请求结束,进程不退出,一个进程至少服务上万次请求才退出。为什么一定要退出?怕RINIT->RSHUTDOWN循环,有哪个代码写的不好,变量一直没释放,内存泄露GC又回收不了。php-fpm里的pm.max_requests配置就是设置RINT循环多少次,退出进程。再来看几个 TSF、swoole、workerman、php-pm,都是 php 启动cli进程,用php管理子进程,php解析HTTP协议。生命周期连 RINIT -> MINIT 循环都省了,没写在 类属性里的变量,裸写的变量都是 进程级全局变量,比 php-fpm 下的 $_GET、$_POST、$_SERVER、$_SESSION、$_COOKIE 这些全局变量范围还大,是进程级的。意味着你 写了个 a.php,里面定义了 $a = 1; 赋值之后,下次请求过来,只要正好分配到了这个进程,依然还能取到普通定义的 $a 变量。这意味着什么?像 Laravel 里的 $app 这些变量,只要写在最外面,因为没有触发 RSHUTDOWN,又没有主动 unset,GC引用计数器一直大于 0,变量不会消失。那怎么解决每次请求 $_GET 和 $_POST 不一样的问题?这些 swoole、workerman 进程管理器自己实现了小型化的 INIT -> SHUTDOWN 过程,维护一些引用计数呗,自己的 a.php 完成后,这种框架帮你 unset($_GET)。问题来了,稳定不稳定?swoole、workman框架本身稳定,但因为完全改变了php生命周期,业务开发人员不熟悉,一不小心写了 global、static 这样的变量,全局用了,内存越占越大,崩溃。又或者 写了个 exit,把整个进程 exit 而不是 request ext 了。



pm的基本实现
简单来说,fpm的实现就是创建一个master进程,在master进程中创建worker pool并监听socket,然后fork出多个子进程(work),这些worker在启动后阻塞在fcgi_accept_request()上,各自accept请求,有请求到达后worker开始读取请求数据,读取完成后开始处理然后再返回,在这期间是不会接收其它请求的,也就是说fpm的子进程同时只能响应一个请求,只有把这个请求处理完成后才会accept下一个请求。
fpm的master进程与worker进程之间不会直接进行通信,master通过共享内存获取worker进程的信息,比如worker进程当前状态、已处理请求数等,当master进程要杀掉一个worker进程时则通过发送信号的方式通知worker进程。
fpm可以同时监听多个端口,每个端口对应一个worker pool,而每个pool下对应多个worker进程,类似nginx中server概念, 在php-fpm.conf中可以配置多个,例如:
[web1]
listen:127.0.0.1:9000
[web2]
listen:127.0.0.1:9001



worker的工作流程包含以下几个步骤



等待请求:fcgi_accept_request()阻塞等待请求
接收请求:fastcgi请求到达后被worker接收并解析,一直到完全接收,然后将method、query、uri等信息保存到worker进程的fpm_scoreboard_proc_s结构中
初始化请求:php_request_startup()执行,此步骤会调用每个扩展的PHP_RINIT_FUNCTION方法,初始化一些操作
处理请求(编译、执行):php代码编译执行阶段,由 php_execute_script方法完成
关闭请求:返回响应,执行php_request_shutdown方法关闭请求,然后进入第一步继续等待请求,此步骤会执行每个扩展的PHP_RSHUTDOWN_FUNCTION进行一些收尾工作



php 缓存之 APC 和apcu
php opcode 缓存 apc.
php apc 缓存其实分两部分,



  一部分是 缓存 类似于 java 编译的中间的 字节码, 不同于c 语言编译之后的二进制的机器码。 php apc 来缓存php解释器解析



php产生的 opcode



  还有一部分是 data cache, (key / value map ), 也就是数据缓存, 这点类似于 memerched 和 redis 缓存, 用来存储数据, 将数据库或者文件中的数据暂时缓存起来。以



提高访问速度。
APC的介绍



The Alternative PHP Cache (APC) is a free and open opcode cache for PHP. Its goal is to provide a free, open, and robust framework for caching and optimizing PHP intermediate code.



apc 由于严重的bug ,php官方已经废弃了。 出现了一个 apcu , apcu的接口和apc 是一样的。


Category php