什么是进程php
进程Process
是计算机中的程序关于某数据集合上的一次运行活动,是系统分配资源和调度的基本单位,是操做系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体。在当代面向线程设计的计算机结构中,进程是线程的容器。简单来讲,程序是指令、数据以及其组织形式的描述,而进程则是程序的实体。前端
在操做系统中,进程表示正在运行的程序,例如在终端中使用PHP命令运行PHP脚本,此时就至关于建立了一个进程,这个进程会在系统中驻存,申请属于它本身的内存空间和系统资源,而且运行相应的程序。shell
$ php build.php
<?php //获取当前进程的PID echo posix_getpid(); //修改所在进程的名称 swoole_set_process_name("swoole process master"); //模拟持续运行100秒的程序 sleep(100);//持续运行100秒的目的是为了在进程中能够查看而不至于很快结束
运行程序编程
$ php build.php 71
查看进程vim
$ ps aux | grep 71 root 1 0.0 0.1 18188 1712 pts/0 Ss+ 11:07 0:00 /bin/bash root 71 0.0 3.0 340468 30788 pts/2 S+ 13:41 0:00 swoole process master root 76 0.0 0.0 11112 940 pts/1 S+ 13:42 0:00 grep 71
对于一个进程来讲,最核心的内容可分为两部分:一部分是它的内存,这个内存是在建立初始时从系统中分配的,进程中全部建立的变量都会存储在内存环境中。另外一部分是上下文环境, 进程是运行在操做系统中的,对于程序而言,它的运行依赖于操做系统分配的资源、操做系统的状态以及程序自身的状态,这些就构成了进程的上下文环境。数组
fd
描述符例如:父进程经过fopen
打开文件后获得一个IO句柄fd
,子进程复制父进程后一样会获得这个fd
。若是父进程和子进程同时对一个文件进行操做,会形成文件混乱,所以须要加互斥锁。缓存
例如:父进程中的变量x=1
,父进程派生子进程后,子进程也会存在变量x=1
,可是修改父进程中的变量x
并不会影响子进程的变量x
的值。安全
PHP是单进程执行的,在处理高并发时主要依赖于Web服务器或PHP-FPM的多进程管理以及进程的复用,但在PHP实现多进程尤为是后台PHP-CLI模式下处理大量数据或运行后台Deamon守护进程时,多进程的优点天然是最好的。性能优化
PHP的多线程也曾被人说起,但进程内多线程资源共享和分配问题难以解决,PHP有一个多线程过的扩展pthreads
,它要求PHP环境必须是线程安全的。bash
多进程简单来讲就是多个进程同时执行多个任务,能够将耗时但又必须执行的查询分红多个子进程进行操做。
开发使用PHP多进程的场景也就是使用PHP-FPM,PHP-FPM做为PHP的多进程管理器,当使用Nginx做为WebServer时,来自客户端的请求会根据Nginx的路由配置,将以PHP为后缀的文件转发给PHP-FPM。当多个用户同时请求API时,PHP-FPM会开启多个PHP的处理进程进行处理。
检查PHP是否支持多进程扩展
$ php -m | grep pcntl
多进程的优点
PHP相比C、C++、Java少了多线程,PHP中只有多进程的方案,因此PHP中的全局变量和对象不是共享的,数据结构也不能跨进程操做,另外Socket文件描述符也不能共享...
多线程看似比多进程强大的多,多线程的缺陷也一样明显:
相比较多线程,多进程拥有的优点是
对于并发服务器核心是IO,并不是大规模密集运算,高并发的服务器单机能维持10W链接,每秒能够处理3~5W笔消息收发。
普通的Web应用都是IO密集型的程序,瓶颈在MySQL上,因此体现不出PHP的性能优点。但在密集计算方面比C/C++、Java等静态编译语言相差几十倍甚至上百倍。
例如:使用多进程方式同时访问Web地址
$ vim multi.php
<?php echo "process begin: ".date("Y-m-d H:i:s").PHP_EOL; //初始化地址数组 $urls = [ "http://www.baidu.com", "http://www.360.com", "http://www.qq.com", "http://www.sina.com" ]; //初始化数组用于回收线程管道内容 $workers = []; //按照任务分配线程 for($i=0; $i<count($urls); $i++){ $url = $urls[$i]; //建立进程 $process = new swoole_process(function(swoole_process $worker) use($url){ //模拟执行耗时任务 file_get_contents($url); //sleep(1);//模拟耗时1秒 echo $url.PHP_EOL; }, true); //开启进程 $pid = $process->start(); $workers[$pid] = $process; } //打印管道内容 foreach($workers as $worker){ echo "pid : ".$worker->read(); } echo "process end: ".date("Y-m-d H:i:s").PHP_EOL;
运行代码
$ php multi.php process begin: 2019-06-22 16:19:48 pid : http://www.baidu.com pid : http://www.360.com pid : http://www.qq.com pid : http://www.sina.com process end: 2019-06-22 16:19:49
进程之间是相互独立的,那么如何实现进程之间的通讯呢? 这里可使用共享内存的方式来实现。
共享内存ShareMemory
是映射一段能被其余进程所访问的内存,这段共享内存由一个进程建立,但多个进程均可以访问。共享内存是最快的IPC
方式,是针对其余进程之间通讯效率低下而专门设计的,它每每与其它通讯机制,如信号量配置使用以实现进程之间的同步和通讯。
共享内存是操做系统中比较特殊的内存,它并不依赖于任何进程, 也不属于任何进程。经过调用系统函数建立共享内存,并指定它的索引,也就是它的IDshmid
,经过索引任何进程均可以在共享内存中申请内存空间并存储对应的值。
查看操做系统中共享内存的分片
$ ipcs -m ------------ 共享内存段 -------------- 键 shmid 拥有者 权限 字节 链接数 状态 0x00000000 131072 jc 777 16384 1 目标 0x00000000 327681 jc 600 67108864 2 目标 0x00000000 262146 jc 777 8077312 2 目标
Swoole没有采用多线程模型而使用了多线程模型,在必定程度上减小了访问数据时加锁解锁的开销,但同时也引入了新的需求 共享内存。Swoole中为了更好的进行内存管理,减小频繁分配释放内存空间形成的损耗和内存碎片,Rango实际并实现了三种不一样功能的内存池分别时FixedPool
、RingBuffer
、MemoryGlobal
。
对于传统PHP的Web开发而言,最经常使用的是LNMP架构。在LNMP架构中,当请求进入时,WebServer会将请求转交给PHP-FPM,PHP-FPM是一个进程池架构的FastCGI服务,内置了PHP解释器。PHP-FPM负责解释执行PHP文件并生成响应,最终返回给WebServer展示至前端。因为PHP-FPM自己是同步阻塞进程模型,在请求结束后会释放掉全部资源,包括框架初始化建立的一些列对象,从而致使PHP进程进入“空转”消耗大量CPU资源,最终致使单机的吞吐能力有限。
另外,在每次请求处理的过程都意味着一次PHP文件解析、环境设置等没必要要的耗时操做,当PHP进程处理完后就会销毁,没法在PHP程序中使用链接池等技术实现性能优化。
针对传统架构的问题,Swoole从PHP扩展下手,解决了上述问题。相比较传统的Web架构,Swoole进程模型最大的特色在于多线程Reactor模式处理网络请求,使其能轻松应对大量链接。
除此以外,Swoole是全异步非阻塞,所以占用资源少,程序执行效率高。在Swoole中程序运行只解析加载一次PHP文件,避免每次请求的重复加载。再者,Swoole进程常驻,使得链接池和请求之间的信息传递的实现成为可能。
使用Swoole开发时,须要开发人员对多进程的运行模式有着清晰的认识。另外,Swoole很容易形成内存泄露。在处理全局变量、静态变量的时候要当心,这种不会被GC清理的变量会存在整个生命周期中。若是没有正确的处理,很容易消耗完内存。而在PHP-FPM下,PHP代码执行完毕内存就会被彻底释放掉。
LNMP
架构中PHP
是须要依赖Nginx
这样的Web
服务器以及PHP-FPM
这样的多进程的PHP
解析器。当一个请求到来时PHP-FPM
会去建立一个新的进程去处理这个请求,在这种状况下,系统的开销很大程序上都用在建立和销毁进程上,致使了程序的响应效率并非很是高。
Swoole的强大之处在于进程模型的设计,即解决了异步问题,又解决了并发问题。
Swoole的进程可分为四种角色
在Swoole
中采用了和PHP-FPM
彻底不一样的架构,整个Swoole
扩展能够分为三层:
Master进程是Swoole的主进程,主要用于处理Swoole的核心事件驱动。Master主进程是一个多线程模型,拥有多个独立的Reactor线程。
Master主进程包含Master线程、Reactor线程、心跳检测线程、UDP收包线程。每一个Reactor子线程中都运行着一个epoll
函数的实例,Swoole对于事件的监听都会在Reactor线程中实现,好比来自客户端的链接、本地通讯使用的管道、异步操做使用的文件以及文件描述符都会注册在epoll
函数中。
Master主进程使用select/poll
进行IO
事件循环,Master主进程中的文件描述符只有几个,Reactor线程使用epoll
,由于Reactor线程中会监听大量链接的可读事件,使用epoll
能够支持大量的文件描述符。
以HTTP
服务器为例,Master
主进程负责监听端口,而后接收新的链接,并将这个链接分配给一个Reactor
线程,由这个Reactor
线程监听此链接,一旦此链接可读时,它会读取数据并解析协议,而后将请求投递到Worker
工做进程中去执行。
Master主进程内的回调函数
onStart
服务器启动时主进程的主线程回调此函数onShutdown
服务器正常结束时发生Swoole启动后Master主线程会负责监听服务器的socket
,若是有新的链接accept
,Master主线程会评估每一个Reactor线程的链接数量,并将此链接分配给链接最少的Reactor线程。这样作的好处是:
listen socket
时,节约了线程唤醒和切换的开销。signal
的处理,使Reactor线程运行中能够不被信号打断。主线程Master在accept
新的链接后,会将这个链接分配给一个固定的Reactor线程,并由这个线程负责监听此socket
,在socket
可读时读取数据,并进行协议解析,最后将请求投递到Worker进程。
TCP
链接、处理网络IO、处理协议、收发数据。Start/Shutdown
事件回调外,不执行任何PHP
代码。TCP
客户端发送来的数据缓冲、拼接、拆分为完整的请求数据包。Reactor
以多线程的方式运行Swoole拥有多线程Reactor,因此能够充分利用多核,开启CPU亲和设置后,Reactor线程能够绑定单独的核,节省CPU Cache开销。
Reactor线程负责处理TCP
链接,是收发数据的线程。Swoole的Master主线程在accept
新的链接后,会将这个链接分配给一个固定的Reactor线程,并由这个线程负责监听此socket
。在socket
可读时读取数据,并进行协议解析,将请求投递到Worker工做进程。在socket
可写时,将数据发送给TCP
客户端。
Reactor线程负责维护客户端TCP链接、处理网络IO、处理协议、收发数据,它彻底是异步非阻塞的模式。
Reactor线程是全异步非阻塞的,即便Worker进程采用了同步模式,依然不响应Reactor线程的性能。在Worker进程组很繁忙的状态下,Reactor线程彻底不受影响,依然能够收发处理数据。
因为TCP是流式的没有边界,因此处理起来很麻烦。Reactor线程可使用EOF或者包头长度,自动缓存数据、组装数据包,等一个请求彻底收到后,再次递交给Worker。
Reactor所有是C代码,除了Start/Shutdown事件回调外,不执行任何PHP代码。它将TCP客户端发来的数据缓冲、拼接、拆分红完整的一个请求数据包。
综上所述,Master主进程中包含两个关键线程:Master主线程和Reactor线程,Master主线程用来处理accept()
事件,建立新的socket fd
,当它接收到新链接后会将新的socket
链接放到Reactor线程的事件监听循环中,Reactor线程负责接收从客户端发送过来的数据,并按协议解析后经过管道pipe
传递给Worker工做进程进行处理。Worker工做进程处理完毕后,会将结果经过管道pipe
回传给Reactor线程,Reactor线程再按照协议将结果经过socket
发送给客户端。能够看到Reactor线程负责数据的IO和传输,在Linux系统下这些IO事件都是经过epoll
机制来处理的。
HeartbeatCheck
Swoole配置了心跳检测后心跳包线程会在固定事件内对全部以前在线的链接发送检测数据包。
UdpRecv
接收并处理客户端UDP数据包
Swoole运行中会建立一个单独的管理进程,全部的Worker进程和Task进程都是从管理进程fork
建立出来的。
Manager管理进程会监听全部子进程的退出事件,当Worker进程发生致命错误或运行生命周期结束时,Manager管理进程会回收此进程并建立新的进程。
Manager管理进程还能够平滑地重启全部工做进程Worker,以实现程序代码的从新加载。
Manager管理进程管理着Worker工做进程或Task任务进程,Worker工做进程或Task任务进程都被Manager管理进程fork
建立并管理着。
Manager进程负责建立和管理下层的Worker进程组和Task进程组,Manager进程中不会运行任何用户层面的业务逻辑,仅仅只作进程的管理和分配。
Manager进程会fork
建立出指定数量的Worker进程和Task进程。
Manager进程的工做职责
fork
建立并管理的Manager进程内的回调函数
onManagerStart
当管理进程启动时调用onManagerStop
当管理进程结束时调用onWorkerError
当Worker进程或Task进程发生异常后会在Manager进程会回调此函数与传统的半同步半异步服务器不一样是,Swoole的工做进程能够同步的也能够异步的。这样带来了工做进程相似于PHP-FPM进程,它接收由Reactor线程投递的请求数据包,并执行PHP回调函数处理数据。工做线程生成响应数据并发送给Reactor线程,由Reactor线程发送给TCP客户端。工做线程能够是异步模式,也能够是同步模式。另外,工做线程以多进程的方式运行。
Swoole想要实现最好的性能就必须建立出多个工做进程帮助处理任务,可是工做进程必须fork
操做,而fork
操做又是不安全的。若是没有管理将会出现不少僵尸进程,进而影响服务器性能。同时工做进程被误杀或因为程序缘由会引发异常退出,为了保证服务的稳定性,须要从新建立工做进程。
工做进程可分为两类:Worker进程和Task进程
Worker进程是Swoole的主逻辑进程,用于处理来自客户端的请求。
Task进程是Swoole提供的异步工做进程,用于处理耗时较长的同步任务。
Worker工做进程接收Reactor线程投递过来的数据,执行PHP代码,而后生成数据并交给Reactor线程,由Reactor线程经过TCP
将数据返回给客户端。若是是UDP
,Worker工做进程会直接将数据发送给客户端。
Worker进程中执行的PHP
代码,它等同于PHP-FPM
。PHP-FPM
在处理异步操做时是很无力的,但Swoole提供的Task进程能够很好的解决这个问题。Worker进程能够将一些异步任务投递给Task进程,而后直接返回,处理其余由Reactor线程投递过来的事件。
Worker进程内的回调函数
onWorkerStart
当Worker工做进程或Task任务进程启动时触发onWorkerStop
当Worker进程终止时触发onConnect
当有新的链接进入时触发onClose
当TCP客户端链接关闭后触发onReceive
当接收到数据时触发onPacket
当接收到UDP数据包是时触发onFinish
当Worker工做进程投递的任务在task_worker
中完成时,Task进程会经过finish()
方法将任务处理的结果发送给Worker进程。onWorkerExit
当开启reload_async
特性后有效,即异步重启特性。onPipeMessage
当工做进程收到由sendMessage
发送的管道消息时触发swoole_server->task
或swoole_server->taskwait
方法投递的任务。swoole_server->finish
。Swoolen除了Reactor线程,Task任务工做进程是以异步的方式处理其它任务的进程,使用方式相似于Gearman。它接收由Worker进程经过swoole_server->task/taskwait
方法投递的任务,而后处理任务,并将结果数据使用swoole_server->finish
返回给Worker进程。Task以多进程的方式进行运行。
简单来讲,能够将Reactor理解为Nginx,将Worker理解为PHP-FPM。Reactor线程异步并行地处理网络请求,而后再转发给Worker工做进程中去处理(在回调函数中处理)。Reactor和Worker之间经过UnixSocket进行通讯。
Swoole除了Reactor线程,Worker工做进程还提供了Task进程池。目的是为了解决业务代码中,有些逻辑部分不须要立刻执行。利用Task进程池,能够方便的投递一个异步任务区执行。
Task进程以彻底同步阻塞的方式运行,一个Task进程在执行任务期间是不接受从Worker进程投递的任务的,当Task进程执行完任务后,会异步的通知Worker进程并告诉它任务已经完成。
Task进程内的回调函数
onTask
在Task线程内被调用,Worker进程可以使用swoole_server_task
函数向Task进程投递新的任务。onWorkerStart
在Worker或Task进程启动时触发onPipeMessage
当工做进程收到由sendMessage
发送的管道消息时触发onConnect
事件对应的方法。onReceive
事件。Send
方法将数据发回给客户端时,数据会沿着这个路径逆流而上。形象来讲
当在业务窗口办理业务时,若是用户不少,后边的用户须要排队等待服务,Reactor负责与客户直接沟通,对客户的请求进行初步的整理(传输层级别的整理,组包),而后Manager负责将业务分配给合适的Worker,如空闲的Worker,最终Worker负责实现具体的业务。
Reactor和Worker与Task的关系,简单理解可认为:Reactor = Nginx、Worker = PHP-FPM
Reactor线程异步并行地处理网络请求,而后再转发给Worker工做进程中去处理。
Reactor线程和Worker工做进程之间经过socket
进行通讯。
在PHP-FPM的应用中,常常会将一个任务异步投递到Redis等队列中,并在后台启动一些PHP进程异步地处理这些任务。
Swoole提供的Worker工做进程是一套更加完整的方案,它将任务投递、队列、PHP任务进程管理融为一体。经过底层的API实现异步任务的处理。另外,Task任务进程能够在任务执行完毕后,再返回一个结果反馈到Worker工做进程。
Swoole的Reactor、Worker、Task之间能够紧密的结合起来,提供更加高级的使用方式。
假设Server是一个工厂
底层会为Worker工做进程、Task任务进程分配一个惟一的ID,不一样的Worker和Task任务进程之间能够经过sendMessage
接口进行通讯。
onConnect
,也就是接收到链接的回调。