1. swoole
进程结构
Swoole
的高效不仅仅于底层使用
c
编写,他的进程结构模型也使其可以高效的处理业务,我们想要深入学习,并且在实际的场景当中使用必须了解,下面我们先看一下结构图:

如上分为四层
:
1. master
:主进程
2. Manger
:管理进程
3. worker
:工作进程
4. task
:异步任务工作进程
1.1 master
第一层,
Master
进程,这个是
swoole
的主进程
,
这个进程是用于处理
swoole
的核心事件驱动的,那么在这个进程当中可以看到它拥有一个
MainReactor[
线程
]
以及若干个
Reactor[
线程
]
,
swoole
所有对于事件的监听都会在这
些线程中实现,比如来自客户端的连接,信号处理等。
MainReactor
(主线程)
主线程会负责监听
server socket
,如果有新的连接
accept
,主线程会评估每个
Reactor
线程的连接数量。将此连接分配给连接数最少的
reactor
线程,做一个负载均衡。
Reactor
线程组
Reactor
线程负责维护客户端机器的
TCP
连接、处理网络
IO
、收发数据完全是异步非阻塞的模式。
swoole
的主线程在
Accept
新的连接后,会将这个连接分配给一个固定的
Reactor
线程,在
socket
可读时读取数据,并进行协
议解析,将请求投递到
Worker
进程。在
socket
可写时将数据发送给
TCP
客户端。
心跳包检测线程(
HeartbeatCheck
)
Swoole
配置了心跳检测之后,心跳包线程会在固定时间内对所有之前在线的连接发送检测数据包
UDP
收包线程(
UdpRecv
)
接收并且处理客户端
udp
数据包
1.2 manager
Swoole
想要实现最好的性能必须创建出多个工作进程帮助处理任务,但
Worker
进程就必须
fork
操作,但是
fork
操作是不安全的,如果没有管理会出现很多的僵尸进程,进而影响服务器性能,同时
worker进程被误杀或者由于程序的原因会异常退出,为了保证服务的稳定性,需要重新创建
worker
进程。
Swoole
在运行中会创建一个单独的管理进程,所有的
worker
进程和
task
进程都是从管理进程
Fork
出来的。管理进程会监视所有子进程的退出事件,当
worker
进程发生致命错误或者运行生命周期结束时,管理进程会回收
此进程,并创建新的进程。换句话也就是说,对于
worker
、
task
进程的创建、回收等操作全权有
“
保姆
”Manager
进程进行管理。
2.3 worker
worker
进程属于
swoole
的主逻辑进程,用户处理客户端的一系列请求,接受由
Reactor
线程投递的请求数据包,并执行
PHP
回调函数处理数据生成响应数据并发给
Reactor
线程,由
Reactor
线程发送给
TCP
客户端可以是异
步非阻塞模式,也可以是同步阻塞模式
2.4 task
taskWorker
进程这一进城是
swoole
提供的异步工作进程,这些进程主要用于处理一些耗时较长的同步任务,在
worker
进程当中投递过来。
3.
进程查看及流程梳理
当启动一个
Swoole
应用时,一共会创建
2 + n + m
个进程,
2
为一个
Master
进程和一个
Manager
进程,其中
n
为
Worker
进程数。
m
为
TaskWorker
进程数。
默认如果不设置,
swoole
底层会根据当前机器有多少
CPU
核数,启动对应数量的
Reactor
线程和
Worker
进程。我机器为
1
核的。
Worker
为
1
。
所以现在默认我启动了
1
个
Master
进程,
1
个
Manager
进程,和
1
个
worker
进程,
TaskWorker
没有设置也就是为
0
,当前
server
会产生
3
个进程。
在启动了
server
之后,在命令行查看当前产生的进程
ps -aux | grep swool
执行结果:

当然我们也可以对于swoole的进程限制数量
<?php
// server
$server->set([
'worker_num'=>1, //设置进程
]);
?>
4. swoole
进程函数
| 事件 | 哪种类型的进程 | 作用 |
| onStart | master进程 | server启动 onStart回调中,仅允许echo、打印Log、修改进程名称。不得执行其他操作。onWorkerStart和onStart回调是在不同进程中并行执行的,不存在先后顺序。 |
| onShutdown | master进程 | 此事件在server正常结束时发生 强制kill进程不会回调onShutdown,如kill -9 在命令行中使用Ctrl+C中断程序会立即停止,底层不会回调onShutdown 需要使用kill -15来发送SIGTREM信号到主进程才能按照正常的流程终止 |
| onManagerStart | manager进程 | 当管理进程启动时调用它 onManagerStart触发时,说明:
Task和Worker进程已创建Master进程状态不明,因为Manager与Master是并行的,onManagerStart回调发生是不能确定Master进程是否已就绪
|
| onManagerStop | manager进程 | 当管理进程结束时调用它 onManagerStop触发时,说明Task和Worker进程已结束运行,已被Manager进程回收。 |
| onWorkError | manager进程 | 当worker/task_worker进程发生异常后会在manager进程内回调此函数 此函数主要用于报警和监控,一旦发现Worker进程异常退出,那么很有可能是遇到了致命错误或者进程CoreDump。通过记录日志或者发送报警的信息来提示开发者进行相应的处理。常见错误可以去手册查看 |
| onWorkerStart | worker进程 | 此事件在Worker进程/Task进程启动时发生
onWorkerStart/onStart是并发执行的,没有先后顺序- 可以通过
$server->taskworker属性来判断当前是Worker进程还是Task进程 - 设置了
worker_num和task_worker_num超过1时,每个进程都会触发一次onWorkerStart事件,可通过判断$worker_id区分不同的工作进程 - 由 worker 进程向 task 进程发送任务,task 进程处理完全部任务之后通过onFinish回调函数通知 worker 进程。例如,我们在后台操作向十万个用户群发通知邮件,操作完成后操作的状态显示为发送中,这时我们可以继续其他操作。等邮件群发完毕后,操作的状态自动改为已发送。
|
| onWorkerStop | worker进程 | 此事件在worker进程正常终止时发生
$worker_id是一个从0-$worker_num之间的数字,表示这个Worker进程的ID$worker_id和进程PID没有任何关系- 进程异常结束,如被强制
kill、致命错误、core dump时无法执行onWorkerStop回调函数
|
| onConnect | worker进程 | 有新的连接进入时,在worker进程中回调 |
| onClose | worker进程 | CP客户端连接关闭后,在worker进程中回调此函数 |
| onReceive | worker进程 | 接收到数据时回调此函数,发生在worker进程中 |
| onPacket | worker进程 | 接收到UDP数据包时回调此函数,发生在worker进程中 |
| onFinish | worker进程 | 当worker进程投递的任务在task_worker中完成时,task进程会通过finish()方法将任务处理的结果发送给worker进程 |
| onWorkerExit | worker进程 | 仅在开启reload_async特性后有效。异步重启特性
Worker进程未退出,onWorkerExit会持续触发onWorkerExit仅在Worker进程内触发,Task进程不执行onWorkerExit
|
| onPipeMessage | worker进程 | 当工作进程收到由 sendMessage 发送的管道消息时会触发事件 |
| onTask | Task进程 | 在task_worker进程内被调用。worker进程可以使用swoole_server_task函数向task_worker进程投递新的任务 |
| onWorkerStart | Task进程 | 此事件在Worker进程/Task进程启动时发生 |
| onPipeMessage | Task进程 | 当工作进程收到由 sendMessage 发送的管道消息时会触发事件 |
请勿在onShutdown、onWorkerStop、中调用任何异步或协程相关API,触发onShutdown时底层已销毁了所有事件循环设施。
如上的表格就是
swoole
的进程可以绑定的时间, 并不是所有的;比如在启动的时候做的操作,那么就会触发启动的事件,发送信息的时候会触发的事件;
swoole
对于不同的进程,在不同的情况下就定义了不同的事件,这里可以像大家介绍一下事件的使用与其效果,但是并不是所有的事件。
我们可以看一下
swoole
在官方所提供的
swoole
运行流程图
1.
服务器关闭程序终止时最后一次事件是
onShutdown
。
2.
服务器启动成功后,
onStart/onManagerStart/onWorkerStart
会在不同的进程内并发执 行,并不是顺序的。
3.
所有事件回调均在
$server->start
后发生,
start
之后写的代码是无效代码。
4. onStart/onManagerStart/onWorkerStart 3
个事件的执行顺序是不确定的
Swoole
的
Reactor
、
Worker
、
TaskWorker
之间可以紧密的结合起来,提供更高级的使用方式。
一个更通俗的比喻,假设
Server
就是一个工厂,那
Reactor
就是销售,接受客户订单。而
Worker
就是工人,当销售接到订单后,
Worker
去工作生产出客户要的东西。而
TaskWorker
可以理解为行政人员,可以帮助
Worker
干
些杂事,让
Worker
专心工作。
我们可以尝试对于
swoole
的进程更名称
// 1. 创建swoole 默认创建的是一个同步的阻塞tcp服务
$host = "0.0.0.0"; // 0.0.0.0 代表接听所有
$post = 9500;
$serv = new Swoole\Server($host, $post);
// 2. 注册事件
$serv->on('Start', function(){
// sleep(1); // 这个函数不推荐在swoole中直接使用
swoole_set_process_name("swoole:start ");
// 修改进程的名称
echo "=== >>> on start \n";
});
$serv->on('managerStart', function(){
swoole_set_process_name("swoole:managerStart ");
// 修改进程的名称
echo "=== >>> on workerStart \n";
});
$serv->on('workerStart', function(){
swoole_set_process_name("swoole:workerStart ");
// 修改进程的名称
echo "=== >>> on workerStart \n";
});
//监听连接进入事件
$serv->on('Connect', function ($serv, $fd) {
echo "Client: Connect.\n";
});
$serv->on('Receive', function ($serv, $fd, $from_id, $data) {
$serv->send($fd, "Server: ");
});
//监听连接关闭事件
$serv->on('Close', function ($serv, $fd) {
echo "QQ离线.\n";
});
$serv->start(); // 阻塞与非阻塞
效果:

多看看
swoole
手册中的高级部分,因为对于
swoole
的理解会更加好一点
swoole
中的
Reactor https://wiki.swoole.com/wiki/page/163.html
1. Reactor
是管理
tcp
连接的, 并向
worker
进程投递请求,投递方式根据
dispatch_mode
配置。
2. worker
进程可以自己处理任务,也可以投递给
taskworker
进程来做。