事件
此节将介绍所有的Swoole的回调函数,每个回调函数都是一个PHP函数,对应一个事件。
onStart
?> 启动后在主进程(master)的主线程回调此函数
function onStart(Swoole\Server $server);
参数
Swoole\Server $server
- 功能:Swoole\Server对象
- 默认值:无
- 其它值:无
在此事件之前
Server
已进行了如下操作- 启动创建完成
manager
进程 - 启动创建完成
worker
子进程 - 监听所有TCP/UDP/unixSocket端口,但未开始Accept连接和请求
- 监听了定时器
- 启动创建完成
接下来要执行
- 主Reactor开始接收事件,客户端可以
connect
到Server
- 主Reactor开始接收事件,客户端可以
onStart
回调中,仅允许echo
、打印Log
、修改进程名称。不得执行其他操作(不能调用server
相关函数等操作,因为服务尚未就绪)。onWorkerStart
和onStart
回调是在不同进程中并行执行的,不存在先后顺序。
可以在onStart
回调中,将$serv->master_pid
和$serv->manager_pid
的值保存到一个文件中。这样可以编写脚本,向这两个PID
发送信号来实现关闭和重启的操作。
onStart
事件在Master
进程的主线程中被调用。
!> 在onStart
中创建的全局资源对象不能在Worker
进程中被使用,因为发生onStart
调用时,worker
进程已经创建好了
新创建的对象在主进程内,Worker
进程无法访问到此内存区域
因此全局对象创建的代码需要放置在Server::start
之前,典型的例子是Swoole\Table
- 安全提示
在onStart
回调中可以使用异步和协程的API,但需要注意这可能会与dispatch_func
和package_length_func
存在冲突,请勿同时使用。
onStart
回调在return
之前服务器程序不会接受任何客户端连接,因此可以安全地使用同步阻塞的函数。
- BASE 模式
SWOOLE_BASE模式下没有master
进程,因此不存在onStart
事件,请不要在BASE
模式中使用使用onStart
回调函数。
onShutdown
?> 此事件在Server
正常结束时发生
function onShutdown(Swoole\Server $server);
参数
Swoole\Server $server
- 功能:Swoole\Server对象
- 默认值:无
- 其它值:无
在此之前
Swoole\Server
已进行了如下操作
!> 强制kill
进程不会回调onShutdown
,如kill -9
需要使用kill -15
来发送SIGTREM
信号到主进程才能按照正常的流程终止
在命令行中使用Ctrl+C
中断程序会立即停止,底层不会回调onShutdown
- 注意事项
!> 请勿在onShutdown
中调用任何异步或协程相关API
,触发onShutdown
时底层已销毁了所有事件循环设施。
onWorkerStart
?> 此事件在 Worker进程/ Task进程启动时发生,这里创建的对象可以在进程生命周期内使用。
function onWorkerStart(Swoole\Server $server, int $workerId);
参数
Swoole\Server $server
- 功能:Swoole\Server对象
- 默认值:无
- 其它值:无
int $workerId
- 功能:
Worker
进程id
(非进程的 PID) - 默认值:无
- 其它值:无
- 功能:
onWorkerStart/onStart
是并发执行的,没有先后顺序可以通过
$server->taskworker
属性来判断当前是Worker
进程还是 Task进程设置了
worker_num
和task_worker_num
超过1
时,每个进程都会触发一次onWorkerStart
事件,可通过判断$worker_id区分不同的工作进程由
worker
进程向task
进程发送任务,task
进程处理完全部任务之后通过onFinish回调函数通知worker
进程。例如,我们在后台操作向十万个用户群发通知邮件,操作完成后操作的状态显示为发送中,这时我们可以继续其他操作,等邮件群发完毕后,操作的状态自动改为已发送。
下面的示例用于为 Worker 进程/ Task进程重命名。
$server->on('WorkerStart', function ($server, $worker_id){
global $argv;
if($worker_id >= $server->setting['worker_num']) {
swoole_set_process_name("php {$argv[0]} task worker");
} else {
swoole_set_process_name("php {$argv[0]} event worker");
}
});
如果想使用Reload机制实现代码重载入,必须在onWorkerStart
中require
你的业务文件,而不是在文件头部。在onWorkerStart
调用之前已包含的文件,不会重新载入代码。
可以将公用的、不易变的php文件放置到onWorkerStart
之前。这样虽然不能重载入代码,但所有Worker
是共享的,不需要额外的内存来保存这些数据。 onWorkerStart
之后的代码每个进程都需要在内存中保存一份
$worker_id
表示这个Worker
进程的ID
,范围参考$worker_id$worker_id和进程
PID
没有任何关系,可使用posix_getpid
函数获取PID
协程支持
- 在
onWorkerStart
回调函数中会自动创建协程,所以onWorkerStart
可以调用协程API
- 在
注意
!> 发生致命错误或者代码中主动调用
exit
时,Worker/Task
进程会退出,管理进程会重新创建新的进程。这可能导致死循环,不停地创建销毁进程
onWorkerStop
?> 此事件在Worker
进程终止时发生。在此函数中可以回收Worker
进程申请的各类资源。
function onWorkerStop(Swoole\Server $server, int $workerId);
参数
Swoole\Server $server
- 功能:Swoole\Server对象
- 默认值:无
- 其它值:无
int $workerId
- 功能:
Worker
进程id
(非进程的 PID) - 默认值:无
- 其它值:无
- 功能:
注意
!> -进程异常结束,如被强制
kill
、致命错误、core dump
时无法执行onWorkerStop
回调函数。
-请勿在onWorkerStop
中调用任何异步或协程相关API
,触发onWorkerStop
时底层已销毁了所有事件循环设施。
onWorkerExit
?> 仅在开启reload_async特性后有效。参见 如何正确的重启服务
function onWorkerExit(Swoole\Server $server, int $workerId);
参数
Swoole\Server $server
- 功能:Swoole\Server对象
- 默认值:无
- 其它值:无
int $workerId
- 功能:
Worker
进程id
(非进程的 PID) - 默认值:无
- 其它值:无
- 功能:
Worker
进程在退出时先会执行一次onWorkerStop
事件回调,然后会在事件循环的每个周期结束时调用onWorkerExit
通知Worker
进程退出。
注意
!> -
Worker
进程未退出,onWorkerExit
会持续触发
-onWorkerExit
仅在Worker
进程内触发, Task进程不执行onWorkerExit
-在onWorkerExit
中尽可能地移除/关闭异步的Socket
连接,最终底层检测到事件循环中事件监听的句柄数量为0
时退出进程。
onConnect
?> 有新的连接进入时,在worker进程中回调。
function onConnect(Swoole\Server $server, int $fd, int $reactorId);
参数
Swoole\Server $server
- 功能:Swoole\Server对象
- 默认值:无
- 其它值:无
int $fd
- 功能:连接的文件描述符
- 默认值:无
- 其它值:无
int $reactorId
- 功能:连接所在的Reactor线程
ID
- 默认值:无
- 其它值:无
- 功能:连接所在的Reactor线程
dispatch_mode = 1/3
注意
!>
onConnect/onClose
这2
个回调发生在worker
进程内,而不是主进程。UDP
协议下只有onReceive事件,没有onConnect/onClose
事件
onReceive
?> 接收到数据时回调此函数,发生在worker
进程中。
function onReceive(Swoole\Server $server, int $fd, int $reactorId, string $data);
参数
Swoole\Server $server
- 功能:Swoole\Server对象
- 默认值:无
- 其它值:无
int $fd
- 功能:连接的文件描述符
- 默认值:无
- 其它值:无
int $reactorId
- 功能:
TCP
连接所在的Reactor线程ID
- 默认值:无
- 其它值:无
- 功能:
string $data
- 功能:收到的数据内容,可能是文本或者二进制内容
- 默认值:无
- 其它值:无
关于
TCP
协议下包完整性,参考TCP粘包问题- 使用底层提供的
open_eof_check/open_length_check/open_http_protocol
等配置可以保证数据包的完整性 - 不使用底层的协议处理,在onReceive后PHP代码中自行对数据分析,合并/拆分数据包。
例如:代码中可以增加一个
$buffer = array()
,使用$fd
作为key
,来保存上下文数据。 每次收到数据进行字符串拼接,$buffer[$fd] .= $data
,然后在判断$buffer[$fd]
字符串是否为一个完整的数据包。默认情况下,同一个
fd
会被分配到同一个Worker
中,所以数据可以拼接起来。使用dispatch_mode = 3时。 请求数据是抢占式的,同一个fd
发来的数据可能会被分到不同的进程。所以无法使用上述的数据包拼接方法- 使用底层提供的
多端口监听,参考此节
当主服务器设置了协议后,额外监听的端口默认会继承主服务器的设置。需要显式调用
set
方法来重新设置端口的协议。$server = new Swoole\Http\Server("127.0.0.1", 9501); $port2 = $server->listen('127.0.0.1', 9502, SWOOLE_SOCK_TCP); $port2->on('receive', function (Swoole\Server $server, $fd, $reactor_id, $data) { echo "[#".$server->worker_id."]\tClient[$fd]: $data\n"; });
这里虽然调用了
on
方法注册了onReceive回调函数,但由于没有调用set
方法覆盖主服务器的协议,新监听的9502
端口依然使用Http
协议。使用telnet
客户端连接9502
端口发送字符串时服务器不会触发onReceive。注意
!> 未开启自动协议选项,onReceive单次收到的数据最大为
64K
开启了自动协议处理选项,onReceive将收到完整的数据包,最大不超过 package_max_length
支持二进制格式,$data
可能是二进制数据
onPacket
?> 接收到UDP
数据包时回调此函数,发生在worker
进程中。
function onPacket(Swoole\Server $server, string $data, array $clientInfo);
参数
Swoole\Server $server
- 功能:Swoole\Server对象
- 默认值:无
- 其它值:无
string $data
- 功能:收到的数据内容,可能是文本或者二进制内容
- 默认值:无
- 其它值:无
array $clientInfo
- 功能:客户端信息包括
address/port/server_socket
等多项客户端信息数据,见 - 默认值:无
- 其它值:无
- 功能:客户端信息包括
注意
!> 服务器同时监听
TCP/UDP
端口时,收到TCP
协议的数据会回调onReceive,收到UDP
数据包回调onPacket
。 服务器设置的EOF
或Length
等自动协议处理(见),对UDP
端口是无效的,因为UDP
包本身存在消息边界,不需要额外的协议处理。
onClose
?> TCP
客户端连接关闭后,在worker
进程中回调此函数。
function onClose(Swoole\Server $server, int $fd, int $reactorId);
参数
Swoole\Server $server
- 功能:Swoole\Server对象
- 默认值:无
- 其它值:无
int $fd
- 功能:连接的文件描述符
- 默认值:无
- 其它值:无
int $reactorId
- 功能:来自那个
reactor
线程,主动close
关闭时为负数 - 默认值:无
- 其它值:无
- 功能:来自那个
提示
注意
!> -onClose 回调函数如果发生了致命错误,会导致连接泄漏。通过
netstat
命令会看到大量CLOSE_WAIT
状态的TCP
连接 ,参考Swoole视频教程
-无论由客户端发起close
还是服务器端主动调用$server->close()
关闭连接,都会触发此事件。因此只要连接关闭,就一定会回调此函数
-onClose中依然可以调用getClientInfo方法获取到连接信息,在onClose回调函数执行完毕后才会调用close
关闭TCP
连接
-这里回调onClose时表示客户端连接已经关闭,所以无需执行$server->close($fd)
。代码中执行$server->close($fd)
会抛出PHP
错误警告。
onTask
?> 在task
进程内被调用。worker
进程可以使用task函数向task_worker
进程投递新的任务。当前的 Task进程在调用onTask回调函数时会将进程状态切换为忙碌,这时将不再接收新的Task,当onTask函数返回时会将进程状态切换为空闲然后继续接收新的Task
。
function onTask(Swoole\Server $server, int $task_id, int $src_worker_id, mixed $data);
参数
Swoole\Server $server
- 功能:Swoole\Server对象
- 默认值:无
- 其它值:无
int $task_id
- 功能:执行任务的
task
进程id
【$task_id
和$src_worker_id
组合起来才是全局唯一的,不同的worker
进程投递的任务ID
可能会有相同】 - 默认值:无
- 其它值:无
- 功能:执行任务的
int $src_worker_id
- 功能:投递任务的
worker
进程id
【$task_id
和$src_worker_id
组合起来才是全局唯一的,不同的worker
进程投递的任务ID
可能会有相同】 - 默认值:无
- 其它值:无
- 功能:投递任务的
mixed $data
- 功能:是任务的数据内容
- 默认值:无
- 其它值:无
提示
V4.2.12起如果开启了 task_enable_coroutine 则回调函数原型是
$server->on('Task', function (Swoole\Server $server, Swoole\Server\Task $task) { $task->worker_id; //来自哪个`Worker`进程 $task->id; //任务的编号 $task->flags; //任务的类型,taskwait, task, taskCo, taskWaitMulti 可能使用不同的 flags $task->data; //任务的数据 co::sleep(0.2); //协程 API $task->finish([123, 'hello']); //完成任务,结束并返回数据 });
返回执行结果到
worker
进程
注意
!> onTask函数执行时遇到致命错误退出,或者被外部进程强制
kill
,当前的任务会被丢弃,但不会影响其他正在排队的Task
onFinish
?> 此回调函数在worker进程被调用,当worker
进程投递的任务在task
进程中完成时, task进程会通过Swoole\Server->finish()
方法将任务处理的结果发送给worker
进程。
function onFinish(Swoole\Server $server, int $task_id, string $data)
参数
Swoole\Server $server
- 功能:Swoole\Server对象
- 默认值:无
- 其它值:无
int $task_id
- 功能:执行任务的
task
进程id
- 默认值:无
- 其它值:无
- 功能:执行任务的
string $data
- 功能:任务处理的结果内容
- 默认值:无
- 其它值:无
注意
!> - task进程的onTask事件中没有调用
finish
方法或者return
结果,worker
进程不会触发onFinish
-执行onFinish逻辑的worker
进程与下发task
任务的worker
进程是同一个进程
onPipeMessage
?> 当工作进程收到由 $server->sendMessage()
发送的unixSocket消息时会触发 onPipeMessage
事件。worker/task
进程都可能会触发 onPipeMessage
事件
function onPipeMessage(Swoole\Server $server, int $src_worker_id, mixed $message);
参数
Swoole\Server $server
- 功能:Swoole\Server对象
- 默认值:无
- 其它值:无
int $task_id
- 功能:消息来自哪个
Worker
进程 - 默认值:无
- 其它值:无
- 功能:消息来自哪个
mixed $message
- 功能:消息内容,可以是任意PHP类型
- 默认值:无
- 其它值:无
onWorkerError
?> 当Worker/Task
进程发生异常后会在Manager
进程内回调此函数。
function onWorkerError(Swoole\Server $server, int $worker_id, int $worker_pid, int $exit_code, int $signal);
参数
Swoole\Server $server
- 功能:Swoole\Server对象
- 默认值:无
- 其它值:无
int $worker_id
- 功能:异常
worker
进程的id
- 默认值:无
- 其它值:无
- 功能:异常
int $worker_pid
- 功能:异常
worker
进程的pid
- 默认值:无
- 其它值:无
- 功能:异常
int $exit_code
- 功能:退出的状态码,范围是
0~255
- 默认值:无
- 其它值:无
- 功能:退出的状态码,范围是
int $signal
- 功能:进程退出的信号
- 默认值:无
- 其它值:无
提示
此函数主要用于报警和监控,一旦发现Worker进程异常退出,那么很有可能是遇到了致命错误或者进程CoreDump。通过记录日志或者发送报警的信息来提示开发者进行相应的处理。
常见错误
signal = 11
:说明Worker
进程发生了segment fault
段错误,可能触发了底层的BUG
,请收集core dump
信息和valgrind
内存检测日志,向我们反馈此问题exit_code = 255
:说明Worker进程发生了Fatal Error
致命错误,请检查PHP的错误日志,找到存在问题的PHP代码,进行解决signal = 9
:说明Worker
被系统强行Kill
,请检查是否有人为的kill -9
操作,检查dmesg
信息中是否存在OOM(Out of memory)
- 如果存在
OOM
,分配了过大的内存。是否创建了非常大的Swoole\Table内存模块。
onManagerStart
?> 当管理进程启动时触发此事件
function onManagerStart(Swoole\Server $server);
提示
在这个回调函数中可以修改管理进程的名称。
在
4.2.12
以前的版本中manager
进程中不能添加定时器,不能投递task任务、不能用协程。在
4.2.12
或更高版本中manager
进程可以使用基于信号实现的同步模式定时器manager
进程中可以调用sendMessage接口向其他工作进程发送消息启动顺序
Task
和Worker
进程已创建Master
进程状态不明,因为Manager
与Master
是并行的,onManagerStart
回调发生是不能确定Master
进程是否已就绪
BASE 模式
- 在SWOOLE_BASE 模式下,如果设置了
worker_num
、max_request
、task_worker_num
参数,底层将创建manager
进程来管理工作进程。因此会触发onManagerStart
和onManagerStop
事件回调。
- 在SWOOLE_BASE 模式下,如果设置了
onManagerStop
?> 当管理进程结束时触发
function onManagerStop(Swoole\Server $serv);
提示
onManagerStop
触发时,说明Task
和Worker
进程已结束运行,已被Manager
进程回收。