当前位置: 首页 > 文档资料 > Swoole 中文文档 >

事件

优质
小牛编辑
133浏览
2023-12-01

此节将介绍所有的Swoole的回调函数,每个回调函数都是一个PHP函数,对应一个事件。

onStart

?> 启动后在主进程(master)的主线程回调此函数

function onStart(Swoole\Server $server);
  • 参数

    • Swoole\Server $server
      • 功能:Swoole\Server对象
      • 默认值:无
      • 其它值:无
  • 在此事件之前Server已进行了如下操作

    • 启动创建完成manager进程
    • 启动创建完成worker子进程
    • 监听所有TCP/UDP/unixSocket端口,但未开始Accept连接和请求
    • 监听了定时器
  • 接下来要执行

    • Reactor开始接收事件,客户端可以connectServer

onStart回调中,仅允许echo、打印Log、修改进程名称。不得执行其他操作(不能调用server相关函数等操作,因为服务尚未就绪)。onWorkerStartonStart回调是在不同进程中并行执行的,不存在先后顺序。

可以在onStart回调中,将$serv->master_pid$serv->manager_pid的值保存到一个文件中。这样可以编写脚本,向这两个PID发送信号来实现关闭和重启的操作。

onStart事件在Master进程的主线程中被调用。

!> 在onStart中创建的全局资源对象不能在Worker进程中被使用,因为发生onStart调用时,worker进程已经创建好了
新创建的对象在主进程内,Worker进程无法访问到此内存区域
因此全局对象创建的代码需要放置在Server::start之前,典型的例子是Swoole\Table

  • 安全提示

onStart回调中可以使用异步和协程的API,但需要注意这可能会与dispatch_funcpackage_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已进行了如下操作

    • 已关闭所有Reactor线程、HeartbeatCheck线程、UdpRecv线程
    • 已关闭所有Worker进程、 Task进程User进程
    • close所有TCP/UDP/UnixSocket监听端口
    • 已关闭主Reactor

!> 强制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_numtask_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机制实现代码重载入,必须在onWorkerStartrequire你的业务文件,而不是在文件头部。在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
      • 默认值:无
      • 其它值:无
  • dispatch_mode = 1/3

    • 在此模式下onConnect/onReceive/onClose可能会被投递到不同的进程。连接相关的PHP对象数据,无法实现在onConnect回调初始化数据,onClose清理数据
    • onConnect/onReceive/onClose 3种事件可能会并发执行,可能会带来异常
  • 注意

    !> onConnect/onClose2个回调发生在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。 服务器设置的EOFLength等自动协议处理(),对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关闭时为负数
      • 默认值:无
      • 其它值:无
  • 提示

    • 主动关闭

      • 当服务器主动关闭连接时,底层会设置此参数为-1,可以通过判断$reactorId < 0来分辨关闭是由服务器端还是客户端发起的。
      • 只有在PHP代码中主动调用close方法被视为主动关闭
    • 心跳检测

      • 心跳检测是由心跳检测线程通知关闭的, 关闭时onClose$reactorId参数不为-1
  • 注意

    !> -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函数中 return 字符串,表示将此内容返回给 worker 进程。worker 进程中会触发 onFinish 函数,表示投递的 task 已完成,当然你也可以通过 Swoole\Server->finish() 来触发 onFinish 函数,而无需再 return

      • return 的变量可以是任意非 nullPHP 变量

  • 注意

    !> 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接口向其他工作进程发送消息

    • 启动顺序

      • TaskWorker进程已创建
      • Master进程状态不明,因为ManagerMaster是并行的,onManagerStart回调发生是不能确定Master进程是否已就绪
    • BASE 模式

      • SWOOLE_BASE 模式下,如果设置了worker_nummax_requesttask_worker_num参数,底层将创建manager进程来管理工作进程。因此会触发onManagerStartonManagerStop事件回调。

onManagerStop

?> 当管理进程结束时触发

function onManagerStop(Swoole\Server $serv);
  • 提示

  • onManagerStop触发时,说明TaskWorker进程已结束运行,已被Manager进程回收。

事件执行顺序

  • 所有事件回调均在$server->start后发生
  • 服务器关闭程序终止时最后一次事件是onShutdown
  • 服务器启动成功后,onStart/onManagerStart/onWorkerStart会在不同的进程内并发执行
  • onReceive/onConnect/onCloseWorker进程中触发
  • Worker/Task进程启动/结束时会分别调用一次onWorkerStart/onWorkerStop
  • onTask事件仅在 task进程中发生
  • onFinish事件仅在worker进程中发生
  • onStart/onManagerStart/onWorkerStart 3个事件的执行顺序是不确定的