PHP进程模式进化论

  • 1、传统架构php-fpm 进程模型
  • 2、IO 多路复用
  • (1)select/poll
  • (2)epoll
  • 3、高效的事件处理模式Reactor 模式


1、传统架构php-fpm 进程模型
属于预派生子进程模型,来一个请求就 fork(复刻) 一个子进程来处理,进程的开销比较大。

详情:
程序启动后就会创建N个进程。每个子进程进入Accept,等待新的连接进入。当客户端连接到服务器时,其中一个子进程会被唤醒,开始处理客户端请求,并且不再接受新的TCP连接。当此连接关闭时,子进程会释放,重新进入Accept,参与处理新的连接。(通俗来说,就是提前安排好了固定的数量的子进程来处理)

缺点:
这种模型严重依赖进程的数量解决并发问题,一个客户端连接就需要占用一个进程,工作进程的数量有多少,并发处理能力就有多少。
操作系统可以创建的进程数量是有限的。

PHP框架初始化会占用大量的计算资源,每个请求都需要初始化。

启动大量进程会带来额外的进程调度消耗。数百个进程时可能进程上下文切换调度消耗占CPU不到1%可以忽略不计,如果启动数千甚至数万个进程,消耗就会直线上升。调度消耗可能占到 CPU 的百分之几十甚至 100%。

如果请求一个第三方请求非常慢,请求过程中会一直占用 CPU 资源,浪费了昂贵的硬件资源

2、IO 多路复用
这的复用指的是复用的线程,其实IO复用的历史和多进程一样长。
(1)select/poll

Linux很早就提供了 select 系统调用,可以在一个进程内维持1024个连接。

后来又加入了poll系统调用,poll做了一些改进,解决了 1024 限制的问题,可以维持任意数量的连接。

但select/poll还有一个问题就是,它需要循环 检测连接 是否有事件。

这样问题就来了,如果服务器有100万个连接,在某一时间只有一个连接向服务器发送了数据,select/poll需要做循环100万次,其中只有1次是命中的,剩下的99万9999次都是无效的,白白浪费了CPU资源。

(2)epoll

直到Linux 2.6内核提供了新的epoll系统调用,可以维持无限数量的连接,而且无需轮询,这才真正解决了 C10K 问题。现在各种高并发异步IO的服务器程序都是基于epoll实现的,比如Nginx、Node.js、Erlang、Golang。像 Node.js,Redis 这样单进程单线程的程序,都可以维持超过1百万TCP连接,全部归功于epoll技术。

在这就不得不提,基于 epoll 实现的 Reacter 模型,IO复用异步非阻塞程序使用经典的Reactor模型,Reactor顾名思义就是反应堆的意思,它本身不处理任何数据收发。只是可以监视一个socket句柄的事件变化。

3、高效的事件处理模式Reactor 模式

主进程/线程往epoll内核亊件中注册socket上的读就绪亊件。

主进程/线程调用epoll_wait等待socket上有数据可读。

当socket上有数据可读时,epoll_wait通知主进程/线程。主进程/线程则将socket可读事件放人请求队列。

睡眠在请求队列上的某个工作线程被唤醒,它从socket读取数据,并处理客户请求, 然后往epoll内核爭件表中注册该socket上的写就绪事件。

主线程调用epoll_wait等待socket可写。

当socket可写时,epoll_wait通知主进程/线程主进程/线程将socket可写事件放人清求队列。

睡眠在请求队列上的某个工作线程被唤醒,它往socket上写人服务器处理客户淸求

swoole 的 Reacter 线程 也是基于 Reacter 模型实现的