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

Event

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

Swoole扩展还提供了直接操作底层epoll/kqueue事件循环的接口。可将其他扩展创建的socketPHP代码中stream/socket扩展创建的socket等加入到SwooleEventLoop中, 否则第三方的$fd如果是同步IO会导致Swoole的EventLoop得不到执行,参考案例

!> Event模块比较底层,是epoll的初级封装,使用者最好有IO多路复用编程经验。

事件优先级

  1. 通过Process::signal设置的信号处理回调函数
  2. 通过Timer::tickTimer::after设置的定时器回调函数
  3. 通过Event::defer设置的延迟执行函数
  4. 通过Event::cycle设置的周期回调函数

方法

add()

将一个socket加入到底层的reactor事件监听中。此函数可以用在ServerClient模式下。

Swoole\Event::add(mixed $sock, callable $read_callback, callable $write_callback = null, int $flags = null): bool;

!> 在 Server 程序中使用时,必须在 Worker 进程启动后使用。在 Server::start 之前不得调用任何异步 IO 接口

  • 参数

    • mixed $sock

      • 功能:文件描述符、stream资源、sockets资源、object
      • 默认值:无
      • 其它值:无
    • callable $read_callback

      • 功能:可读事件回调函数
      • 默认值:无
      • 其它值:无
    • callable $write_callback

      • 功能:为可写事件回调函数【此参数可以是字符串函数名、对象+方法、类静态方法或匿名函数,当此socket可读或者可写时回调指定的函数。】
      • 默认值:无
      • 其它值:无
    • int $flags

      • 功能:事件类型的掩码【可选择关闭/开启可读可写事件,如SWOOLE_EVENT_READSWOOLE_EVENT_WRITE或者SWOOLE_EVENT_READ|SWOOLE_EVENT_WRITE
      • 默认值:无
      • 其它值:无
  • $sock 4种类型

类型说明
int文件描述符,包括Swoole\Client->$sockSwoole\Process->$pipe或者其他fd
stream资源stream_socket_client/fsockopen创建的资源
sockets资源sockets扩展中socket_create创建的资源,需要在编译时加入 ./configure --enable-sockets
objectSwoole\ProcessSwoole\Client,底层自动转换为UnixSocketProcess)或客户端连接的socketSwoole\Client
  • 返回值

    • 添加事件监听成功成功返回true
    • 添加失败返回false,请使用swoole_last_error获取错误码
    • 已添加过的socket不能重复添加,可以使用swoole_event_set修改socket对应的回调函数和事件类型

    !> 使用Swoole\Event::addsocket加入到事件监听后,底层会自动将该socket设置为非阻塞模式

  • 使用示例

$fp = stream_socket_client("tcp://www.qq.com:80", $errno, $errstr, 30);
fwrite($fp,"GET / HTTP/1.1\r\nHost: www.qq.com\r\n\r\n");

Swoole\Event::add($fp, function($fp) {
    $resp = fread($fp, 8192);
    //socket处理完成后,从epoll事件中移除socket
    Swoole\Event::del($fp);
    fclose($fp);
});
echo "Finish\n";  //Swoole\Event::add 不会阻塞进程,这行代码会顺序执行
  • 回调函数

    • 在可读($read_callback)事件回调函数中必须使用freadrecv等函数读取socket缓存区中的数据,否则事件会持续触发,如果不希望继续读取必须使用Swoole\Event::del移除事件监听
    • 在可写($write_callback)事件回调函数中,写入socket之后必须调用Swoole\Event::del移除事件监听,否则可写事件会持续触发
    • 执行freadsocekt_recvsocket_readSwoole\Client::recv返回false,并且错误码为EAGAIN时表示当前socket接收缓存区内没有任何数据,这时需要加入可读监听等待EventLoop通知
    • 执行fwritesocket_writesocket_sendSwoole\Client::send操作返回false,并且错误码为EAGAIN时表示当前socket发送缓存区已满,暂时不能发送数据。需要监听可写事件等待EventLoop通知

set()

修改事件监听的回调函数和掩码。

Swoole\Event::set($fd, mixed $read_callback, mixed $write_callback, int $flags): bool;
  • 参数

    • 参数与Event::add完全相同。如果传入$fdEventLoop中不存在返回false
    • $read_callback不为null时,将修改可读事件回调函数为指定的函数
    • $write_callback不为null时,将修改可写事件回调函数为指定的函数
    • $flags可关闭/开启,可写(SWOOLE_EVENT_READ)和可读(SWOOLE_EVENT_WRITE)事件的监听

    !> 注意如果监听了SWOOLE_EVENT_READ事件,而当前并未设置read_callback,底层会直接返回false,添加失败。SWOOLE_EVENT_WRITE同理。

  • 状态变更

    • 使用Event::addEvent::set设置了可读事件回调,但并未监听SWOOLE_EVENT_READ可读事件,这时底层仅保存回调函数的信息,并不会产生任何事件回调。
    • 可以使用Event::set($fd, null, null, SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE),修改监听的事件类型,这时底层会触发可读事件。
  • 释放回调函数

!> 注意Event::set只能替换回调函数,但并不能释放事件回调函数。如:Event::set($fd, null, null, SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE),参数中传入的read_callbackwrite_callbacknull,表示不对Event::add设置的回调函数进行修改,而不是将事件回调函数设为null

只有调用Event::del清除事件监听时,底层才会释放read_callbackwrite_callback事件回调函数。

isset()

检测传入的$fd是否已加入了事件监听。

Swoole\Event::isset(mixed $fd, int $events = SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE): bool;
  • 参数

    • mixed $fd

      • 功能:任意的socket文件描述符【参考 Event::add 文档】
      • 默认值:无
      • 其它值:无
    • int $events

      • 功能:检测的事件类型
      • 默认值:无
      • 其它值:无
  • $events

事件类型说明
SWOOLE_EVENT_READ是否监听了可读事件
SWOOLE_EVENT_WRITE是否监听了可写事件
SWOOLE_EVENT_READ \| SWOOLE_EVENT_WRITE监听可读或可写事件
  • 使用示例
use Swoole\Event;

$fp = stream_socket_client("tcp://www.qq.com:80", $errno, $errstr, 30);
fwrite($fp,"GET / HTTP/1.1\r\nHost: www.qq.com\r\n\r\n");

Event::add($fp, function($fp) {
    $resp = fread($fp, 8192);
    Swoole\Event::del($fp);
    fclose($fp);
}, null, SWOOLE_EVENT_READ);
var_dump(Event::isset($fp, SWOOLE_EVENT_READ)); //返回 true
var_dump(Event::isset($fp, SWOOLE_EVENT_WRITE)); //返回 false
var_dump(Event::isset($fp, SWOOLE_EVENT_READ | SWOOLE_EVENT_WRITE)); //返回 true

write()

用于PHP自带stream/sockets扩展创建的socket,使用fwrite/socket_send等函数向对端发送数据。当发送的数据量较大,socket写缓存区已满,就会发送阻塞等待或者返回EAGAIN错误。

Event::write函数可以将stream/sockets资源的数据发送变成异步的,当缓冲区满了或者返回EAGAIN,Swoole底层会将数据加入到发送队列,并监听可写。socket可写时Swoole底层会自动写入

Swoole\Event::write(mixed $fd, miexd $data): bool;
  • 参数

    • mixed $fd

      • 功能:任意的socket文件描述符【参考 Event::add 文档】
      • 默认值:无
      • 其它值:无
    • miexd $data

      • 功能:要发送的数据 【发送数据的长度不得超过Socket缓存区尺寸】
      • 默认值:无
      • 其它值:无

!> Event::write不能用于SSL/TLS等有隧道加密的stream/sockets资源
Event::write操作成功后,会自动将该$socket设置为非阻塞模式

  • 使用示例
use Swoole\Event;

$fp = stream_socket_client('tcp://127.0.0.1:9501');
$data = str_repeat('A', 1024 * 1024*2);

Event::add($fp, function($fp) {
     echo fread($fp);
});

Event::write($fp, $data);

SOCKET缓存区已满后,Swoole的底层逻辑

持续写入SOCKET如果对端读取不够快,那SOCKET缓存区会塞满。Swoole底层会将数据存到内存缓存区中,直到可写事件触发再写入SOCKET

如果内存缓存区也被写满了,此时Swoole底层会抛出pipe buffer overflow, reactor will block.错误,并进入阻塞等待。

!> 缓存塞满返回false是原子操作,只会出现全部写入成功或者全部失败

del()

reactor中移除监听的socketEvent::del应当与Event::add成对使用。

Swoole\Event::del(mixed $sock): bool;

!> 必须在socketclose操作前使用Event::del移除事件监听,否则可能会产生内存泄漏

  • 参数

    • mixed $sock
      • 功能socket的文件描述符
      • 默认值:无
      • 其它值:无

exit()

退出事件轮询。

!> 此函数仅在Client程序中有效

Swoole\Event::exit(): void;

defer()

在下一个事件循环开始时执行函数。

Swoole\Event::defer(mixed $callback_function);

!> Event::defer的回调函数会在当前EventLoop的事件循环结束、下一次事件循环开始前执行。

  • 参数

    • mixed $callback_function
      • 功能: 时间到期后所执行的函数 【必须是可以调用的。回调函数不接受任何参数,可以使用匿名函数的use语法传递参数到回调函数中;在$callback_function函数执行过程中添加新的defer任务,仍然会在本轮事件循环内执行完成】
      • 默认值:无
      • 其它值:无
  • 使用示例

Swoole\Event::defer(function(){
    echo "After EventLoop\n";
});

cycle()

定义事件循环周期执行函数。此函数会在每一轮事件循环结束时调用。

Swoole\Event::cycle(callable $callback, bool $before = false): bool;
  • 参数

    • callable $callback_function

      • 功能: 要设置的回调函数 【$callbacknull时表示清除cycle函数,已设置cycle函数,重新设置时会覆盖上一次的设定】
      • 默认值:无
      • 其它值:无
    • bool $before

      • 功能: 在EventLoop之前调用该函数
      • 默认值:无
      • 其它值:无

!> 可以同时存在before=truebefore=false两个回调函数。

  • 使用示例
Swoole\Timer::tick(2000, function ($id) {
    var_dump($id);
});

Swoole\Event::cycle(function () {
    echo "hello [1]\n";
    Swoole\Event::cycle(function () {
        echo "hello [2]\n";
        Swoole\Event::cycle(null);
    });
});

wait()

启动事件监听。

!> 请将此函数放置于PHP程序末尾

Swoole\Event::wait();
  • 使用示例
Swoole\Timer::tick(1000, function () {
    echo "hello\n";
});

Swoole\Event::wait();

dispatch()

启动事件监听。

!> 仅执行一次reactor->wait操作,在Linux平台下相当手工调用一次epoll_wait。与Event::dispatch不同的是,Event::wait在底层内部维持了循环。

Swoole\Event::dispatch();
  • 使用示例
while(true)
{
    Event::dispatch();
}

此函数的目的是兼容一些框架,如amp,它在框架内部自行控制reactor的循环,而使用Event::wait,Swoole底层维持了控制权,就无法让出给框架方。