PHP中的“进程”系列1——PHP-FPM模型

PHP中的“进程”系列

这个系列会分几个部分,从PHP-FPM进程模式起,到Linux进程,最后回到PHP本身谈一谈如何设计一个PHP的进程池。整个系列会氛围大致5个主要部分,分别是:

①:PHP-FPM的多进程模型
②:Linux进程介绍
③:PHP中的多进程
④:进程间通讯
⑤:PHP的进程池设计

此篇为系列第一篇:PHP-FPM的多进程模型。那么,我们谈论PHP-FPM多进程模型的时候,作为PHPer的你,可能需要先看看下面一些关于PHP-FPM的多进程模型,是否都有所了解

①:PHP-FPM启动进程的方式主要有哪几种,区别是什么?
②:PHP-FPM,是主进程接收请求转给子进程,还是子进程单独接收请求并处理,如何验证?
③:为何在PHP-FPM模式下,PHP代码很少有人去做连接池?
④:PHP-FPM模式性能差的体现有哪些,如何优化?
⑤:PHP-FPM模式下的Yac为何无法和Cli模式无法共享内存?

1、PHP-FPM是多进程模式,master进程管理worker进程,进程的数量,都可以通过php-fpm.conf做具体配置,而PHP-FPM的进程,亦可以分为动态模式及静态模式。

①:静态(static):直接开启指定数量的php-fpm进程,不再增加或者减少;启动固定数量的进程,占用内存高。但在用户请求波动大的时候,对Linux操作系统进程的处理上耗费的系统资源低。
②:动态(dynamic):开始的时候开启一定数量的php-fpm进程,当请求量变大的时候,动态的增加php-fpm进程数到上限,当空闲的时候自动释放空闲的进程数到一个下限。动态模式,会根据max、min、idle children 配置,动态的调整进程数量。在用户请求较为波动,或者瞬间请求增高的时候,进行大量进程的创建、销毁等操作,而造成Linux负载波动升高,简单来说,请求量少,PHP-FPM进程数少,请求量大,进程数多。优势就是,当请求量小的时候,进程数少,内存占用也小。
③:按需模式(ondemand):这种模式下,PHP-FPM的master不会fork任何的子进程,纯粹就是按需启动子进程,这种模式很少使用,因为这种模式,基本上是无法适应有一定量级的线上业务的。由于php-fpm是短连接的,所以每次请求都会先建立连接,建立连接的过程必然会触发上图的执行步骤,所以,在大流量的系统上master进程会变得繁忙,占用系统cpu资源,不适合大流量环境的部署。这种模式,贴一个简单的网络上的图来说明:

6B91FC74FBF0FFD224D402C340C8CE92.png

需要注意2个点,“连接”,及“数据”到来。有连接进来再fork进程,同样可以达到子进程继承父进程上下文,然后子进程处理用户请求这个目的。

具体的,关于动态、静态进程模式的相关参数,可参考PHP官方文档,我们需要关注的是,对于我们自身的业务,如何选择PHP-FPM的模式为动态还是静态。

比较大内存的服务器来说,设置为静态的话会提高效率。因为频繁开关php-fpm进程也会有时滞,所以内存够大的情况下开静态效果会更好。数量也可以根据 内存/30M 得到。比如说2GB内存的服务器,可以设置为50;4GB内存可以设置为100等。高配机器选静态,低配机器(省内存)选动态,高配机器用动态不能充分利用内存资源和CPU资源,也无法及时应对瞬时高并发,甚至可能短时间造成5xx错误。

2、PHP-FPM,是主进程接收请求转给子进程,还是子进程单独接收请求并处理,如何验证

PHP-FPM的进程管理方式和Nginx的进程管理方式类似,在处理用于请求上,并非是主进程接受请求后转给子进程,而是子进程抢占式的接受用户的请求,本质上,其实PHP-FPM的多进程,以及Nginx的多进程,其实都是主进程监听的同一个端口(被动套接字)后,fork子进程达到多个进程监听同一个端口的目的。 Linux系统,所有的进程IO操作,都需要和操作系统打交道,也就是说,所有IO操作,操作系统都知道,而这个过程,也就是我们常说的“系统调用”。我们可以从系统调用入手解决这个问题。 系统调用的查看,可以使用strace。

对于如何验证相对简单,有2种方式;其一,看php-fpm进程的日志,这需要配置好合适的php-fpm日志格式;其二,既然IO数据会通过内核态过度到用户态进程,那么,我们通过strace -p <pid>命令去跟踪系统调用即可。分别跟踪php-fpm的主进程id以及php-fpm子进程id,然后访问nginx,由nginx通过fast-cgi协议转到php-fpm进程上,看在哪个进程上发送了系统调用。

3、为何在PHP-FPM模式下,PHP代码很少有人去做连接池

首先,PHP-FPM模式下,注定一个请求的生命周期只有1次。也就是说,从FPM请求到请求,解析PHP脚本,FPM的Zend虚拟机分配资源执行,到最后的处理结束,PHP-FPM会回收这次请求的所有资源。

当然,PHP-FPM之所以这么做,①:目的是让开发不需要关心资源的回收的处理,所以可能你没怎么关心过网络的关闭、文件描述符的关闭等等。②:减少内存溢出的情况。

如果在这种模式下,你实现了连接池,也意味着请求结束,连接池消失,做了一次无用功而已。

“鸡肋的”pconnect。pconnect,持久化链接,也就是链接不释放。但问题在于,PHP-FPM是多进程模式,而持久化的链接,存在于进程中,也就意味着,如果一台机器有300个FPM进程,会一次性初始化300个持久化链接。 如果因为面临业务活动,冒然对机器扩容,很可能造成业务的数据库连接数直接打满。

4、PHP-FPM模式性能差的体现有哪些,如何优化

先思考为何性能差,一个应用的性能如果说差,往往会从2个方面来说,一个是IO性能,一个是计算性能。

IO上来说,PHP-FPM模式下,难以做连接池,所以高并发业务下,网络的处理会有劣势。 注意:我这里一直在说的,都是 PHP-FPM模式下,在CLI模式下,你还是可以做自己的连接池的,只不过这个连接池,仅限于CLI模式的单进程内,这个模式还不能用在处理网络请求(比如HTTP请求),因为PHP默认单进程模式,FPM、CLI都是默认单进程,即便CLI可以做连接池,也不方便做链接保活(不能同时做心跳检测)

计算性能上来说,其实PHP是C写的,单纯的论计算性能是不错的。 但问题在于,PHP在处理请求的时候,每次都要解析PHP脚本、翻译PHP代码为opcode、用Zend虚拟机执行opcode,处理结束,释放资源。因此算下来,也是PHP慢的最大原因之一。

如何优化:

①:对于计算性能来说,使用 Zend OPcache 扩展,缓存字节码。
②:对于IO性能来说,使用文件cache或者memcached减轻对网络Cache的压力;使用 Yac 减轻对 Cache层的压力;在同一次请求中;复用链接不要每次都用新的;合理设计日志组件类库,优化Logger减少对文件操作的次数来减少IO的压力。

关于设计一个合格的Logger组件,我们需要注意几个点:

①:每次请求,只做一次日志写操作,不要每次别人调用你的函数,你都去执行一次类似file_put_contents的操作。
②:兼容各种类似错误,换句话说,即使PHP fatal error了,你也得能把知名错误之前的日志记录下来。这个实现,可以借助PHP类的析构方法来做。也可以使用更好的 register_shutdown_function 来注册一个钩子,在PHP请求结束的时候,回调此钩子,完成做最后的日志操作。

5、PHP-FPM模式下的Yac为何无法和Cli模式无法共享内存

我们知道,PHP扩展开发中,首要执行的一个宏,便是 PHP_MINIT_FUNCTION,Yac扩展,需要在PHP-FPM进程启动的时候,便初始化一块共享内存,供各个进程来共享使用,因此,要能共享,关键就在于需要一个相同的标识,各个进程都知道才可以。Yac扩展的初始化流程为:

PHP_MINIT_FUNCTION->yac_storage_startup->yac_allocator_startup->create_segments

我们查看 create_segments 的具体实现:

static int create_segments(size_t requested_size, zend_shared_segment_posix ***shared_segments_p, int *shared_segments_count, char **error_in)
{
    zend_shared_segment_posix *shared_segment;
    char shared_segment_name[sizeof("/ZendAccelerator.") + 20];

    *shared_segments_count = 1;
    *shared_segments_p = (zend_shared_segment_posix **) calloc(1, sizeof(zend_shared_segment_posix) + sizeof(void *));
    if (!*shared_segments_p) {
        *error_in = "calloc";
        return ALLOC_FAILURE;
    }
    shared_segment = (zend_shared_segment_posix *)((char *)(*shared_segments_p) + sizeof(void *));
    (*shared_segments_p)[0] = shared_segment;

  // 这里打开共享内存块需要的Id,也就是 shared_segment_name
    sprintf(shared_segment_name, "/ZendAccelerator.%d", getpid());
    
    // 这里,打开一块共享内存
    shared_segment->shm_fd = shm_open(shared_segment_name, O_RDWR|O_CREAT|O_TRUNC, 0600);
    if (shared_segment->shm_fd == -1) {
        *error_in = "shm_open";
        return ALLOC_FAILURE;
    }

上面做了一些注释,最关键的是开启共享内存需要的系统ID,shared_segment_name,此值,包含了进程的ID。也就是php-fpm的主进程id。这就是,PHP-FPM模式所有进程间能够通信的奥秘所在(它们有相同的共享内存标识ID)。而,如果我们是想要通过PHP脚本,使用yac扩展读取这个共享内存,会这样做:

$yac = new Yac();
$key = "something"
$yac->get($key);

在CLI模式下,这样是不可能拿到PHP-FPM模式下设置的共享内存数据的因为,因为CLI模式下,执行php脚本,进程ID,和PHP-FPM模式下的进程ID,根本就不相同。

总结来说,在后边会讲到进程间通讯,会讲到基于共享内存的通讯。多进程要共享内存通信,必须要一开始就协调好一个唯一ID,这个ID,多个进程间都要知道,PHP-FPM是多进程,主进程fork子进程出来,子进程自然知道这个唯一ID是什么(因为Linux进程fork会把整个进程的堆栈内存都fork一遍)。 但是,php a.php 这样执行,其实是一个完全独立的进程,和php-fpm没任何关系,这样的进程,自然不能知道php-fpm进程里的那个唯一ID是什么。

至此,系列1——PHP-FPM模式已结束~

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 195,898评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,401评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,058评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,539评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,382评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,319评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,706评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,370评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,664评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,715评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,476评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,326评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,730评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,003评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,275评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,683评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,877评论 2 335

推荐阅读更多精彩内容

  • 需要清楚的几个概念: FPM进程:进程数在php-fpm.ini中设置。没有设置 max_requests ,那么...
    fujun_195a阅读 3,041评论 1 13
  • 1. Nginx的模块与工作原理 Nginx由内核和模块组成,其中,内核的设计非常微小和简洁,完成的工作也非常简单...
    rosekissyou阅读 10,170评论 5 124
  • 他说,“我的工作,就是欺骗小孩子”。 他用善意的与众不同的谎言欺骗孩子,他创作儿童文学。通过文学作品,为孩子打开一...
    多多洛的世界阅读 298评论 0 0
  • 不知不觉,我陪产的妈妈已经出月子了,而我这个拖延症症重症患者也终于提起了笔。这次没有打算很详细地写整个陪产过程,而...
    小君子_导乐母指阅读 212评论 0 0
  • 花妖这世初初满百岁,随带着春风逐流水而离枝渐远。游过护城河,游进黛粉湖。尽纳天地灵气,日月精华,化而为人。 小少爷...
    思昨非阅读 331评论 0 0