五:tcp异步服务端

服务端 有 异步非阻塞/协程

客户端 有 同步非阻塞/协程

TCP异步服务端

 

 

 这样就创建了一个 TCP 服务器,监听本机 9501 端口。它的逻辑很简单,当客户端 Socket 通过网络发送一个 hello 字符串时,服务器会回复一个 Server: hello 字符串。

Server 是异步服务器,所以是通过监听事件的方式来编写程序的。当对应的事件发生时底层会主动回调指定的函数。如当有新的 TCP 连接进入时会执行 onConnect 事件回调,当某个连接向服务器发送数据时会回调 onReceive 函数。

  • 服务器可以同时被成千上万个客户端连接,$fd 就是客户端连接的唯一标识符
  • 调用 $server->send() 方法向客户端连接发送数据,参数就是 $fd 客户端标识符
  • 调用 $server->close() 方法可以强制关闭某个客户端连接
  • 客户端可能会主动断开连接,此时会触发 onClose 事件回调

执行程序   php tcp_server.php

 

 

 

在命令行下运行 server.php 程序,启动成功后可以使用 netstat 工具看到已经在监听 9501 端口。

这时就可以使用 telnet/netcat 工具连接服务器

 

 

 

 以下将是Swoole\Server 类的全部方法、属性、配置项以及所有的事件。Swoole\Server 类是所有异步风格服务器的基类,后面章节的 Http\ServerWebSocket\ServerRedis\Server 都继承于它

方法:

(1):__construct()

创建一个异步 IO 的 Server 对象。

new swoole_server()创建连接的时候会调用到此方法;

Swoole\Server::__construct(string $host = '0.0.0.0', int $port = 0, int $mode = SWOOLE_PROCESS, int $sockType = SWOOLE_SOCK_TCP): \Swoole\Server

    • 参数

      • string $host

        • 功能:指定监听的 ip 地址
        • 默认值:无
        • 其它值:无

        IPv4 使用 127.0.0.1 表示监听本机,0.0.0.0 表示监听所有地址
        IPv6 使用::1 表示监听本机,:: (相当于 0:0:0:0:0:0:0:0) 表示监听所有地址

      • int $port

        • 功能:指定监听的端口,如 9501
        • 默认值:无
        • 其它值:无

        如果 $sockType 值为 UnixSocket Stream/Dgram,此参数将被忽略
        监听小于 1024 端口需要 root 权限
        如果此端口被占用 server->start 时会失败

      • int $mode

      • int $sockType

        • 功能:指定这组 Server 的类型
        • 默认值:无
        • 其它值:
          • SWOOLE_TCP/SWOOLE_SOCK_TCP tcp ipv4 socket
          • SWOOLE_TCP6/SWOOLE_SOCK_TCP6 tcp ipv6 socket
          • SWOOLE_UDP/SWOOLE_SOCK_UDP udp ipv4 socket
          • SWOOLE_UDP6/SWOOLE_SOCK_UDP6 udp ipv6 socket
          • SWOOLE_UNIX_DGRAM unix socket dgram
          • SWOOLE_UNIX_STREAM unix socket stream

        使用 $sock_type | SWOOLE_SSL 可以启用 SSL 隧道加密。启用 SSL 后必须配置 ssl_key_filessl_cert_file

 
// 可以混合使用UDP/TCP,同时监听内网和外网端口,多端口监听参考 addlistener 小节。

$server = new \Swoole\Server($host, $port = 0, $mode = SWOOLE_PROCESS, $sockType = SWOOLE_SOCK_TCP); $server->addlistener("127.0.0.1", 9502, SWOOLE_SOCK_TCP); // 添加 TCP $server->addlistener("192.168.1.100", 9503, SWOOLE_SOCK_TCP); // 添加 Web Socket $server->addlistener("0.0.0.0", 9504, SWOOLE_SOCK_UDP); // UDP $server->addlistener("/var/run/myserv.sock", 0, SWOOLE_UNIX_STREAM); //UnixSocket Stream $server->addlistener("127.0.0.1", 9502, SWOOLE_SOCK_TCP | SWOOLE_SSL); //TCP + SSL $port = $server->addListener("0.0.0.0", 0, SWOOLE_SOCK_TCP); // 系统随机分配端口,返回值为随机分配的端口 echo $port->port;

(2):set()

用于设置运行时的各项参数。服务器启动后通过 $serv->setting 来访问 Server->set 方法设置的参数数组。

Swoole\Server->set(array $setting): void
Server->set 必须在 Server->start 前调用

示例
$server->set(array(
    'reactor_num'   => 2,     // reactor thread num
    'worker_num'    => 4,     // worker process num
    'backlog'       => 128,   // listen backlog
    'max_request'   => 50,
    'dispatch_mode' => 1,
));

(3):on()

 

注册 Server 的事件回调函数。

 

Swoole\Server->on(string $event, mixed $callback): void
重复调用 on 方法时会覆盖上一次的设定
 
  • 参数

    • string $event

      • 功能:回调事件名称
      • 默认值:无
      • 其它值:无

      大小写不敏感,具体有哪些事件回调参考事件,事件名称字符串不要加 on

    • mixed $callback

      • 功能:回调函数
      • 默认值:无
      • 其它值:无

      可以是函数名的字符串,类静态方法,对象方法数组,匿名函数 参考此节

 


 




 

(4):addListener()

增加监听的端口。业务代码中可以通过调用 Server->getClientInfo 来获取某个连接来自于哪个端口。

 

Swoole\Server->addListener(string $host, int $port, int $sockType): bool|Swoole\Server\Port
监听 1024 以下的端口需要 root 权限
主服务器是 WebSocketHTTP 协议,新监听的 TCP 端口默认会继承主 Server 的协议设置。必须单独调用 set 方法设置新的协议才会启用新协议
  • string $host

    • 功能:与 __construct()$host 相同
    • 默认值:与 __construct()$host 相同
    • 其它值:与 __construct()$host 相同
  • int $port

    • 功能:与 __construct()$port 相同
    • 默认值:与 __construct()$port 相同
    • 其它值:与 __construct()$port 相同
  • int $sockType

    • 功能:与 __construct()$sockType 相同
    • 默认值:与 __construct()$sockType 相同
    • 其它值:与 __construct()$sockType 相同

-Unix Socket 模式下 $host 参数必须填写可访问的文件路径,$port 参数忽略
-Unix Socket 模式下,客户端 $fd 将不再是数字,而是一个文件路径的字符串
-Linux 系统下监听 IPv6 端口后使用 IPv4 地址也可以进行连接

(5):listen()

此方法是 addlistener 的别名。

(6):addProcess()

添加一个用户自定义的工作进程。此函数通常用于创建一个特殊的工作进程,用于监控、上报或者其他特殊的任务。

 

Swoole\Server->addProcess(Swoole\Process $process): int
不需要执行 start。在 Server 启动时会自动创建进程,并执行指定的子进程函数
    • 参数

      • Swoole\Process

        • 功能:Swoole\Process 对象
        • 默认值:无
        • 其它值:无
    • 注意

      - 创建的子进程可以调用 $server 对象提供的各个方法,如 getClientList/getClientInfo/stats
      - 在 Worker/Task 进程中可以调用 $process 提供的方法与子进程进行通信
      - 在用户自定义进程中可以调用 $server->sendMessageWorker/Task 进程通信
      - 用户进程内不能使用 Server->task/taskwait 接口
      - 用户进程内可以使用 Server->send/close 等接口
      - 用户进程内应当进行 while(true)(如下边的示例) 或 EventLoop 循环 (例如创建个定时器),否则用户进程会不停地退出重启

    • 生命周期

      - 用户进程的生存周期与 MasterManager 是相同的,不会受到 reload 影响
      - 用户进程不受 reload 指令控制,reload 时不会向用户进程发送任何信息
      - 在 shutdown 关闭服务器时,会向用户进程发送 SIGTERM 信号,关闭用户进程
      - 自定义进程会托管到 Manager 进程,如果发生致命错误,Manager 进程会重新创建一个
      - 自定义进程也不会触发 onWorkerStop 等事件

示例:

 

(7):start()

启动服务器,监听所有 TCP/UDP 端口。

 

Swoole\Server->start(): bool
  • 提示

    • 启动成功后会创建 worker_num+2 个进程。Master 进程 +Manager 进程 +serv->worker_numWorker 进程。
    • 启动失败会立即返回 false
    • 启动成功后将进入事件循环,等待客户端连接请求。start 方法之后的代码不会执行
    • 服务器关闭后,start 函数返回 true,并继续向下执行
    • 设置了 task_worker_num 会增加相应数量的 Task 进程
    • 方法列表中 start 之前的方法仅可在 start 调用前使用,在 start 之后的方法仅可在 onWorkerStartonReceive 等事件回调函数中使用
  • 扩展

    • Master 主进程

      • 主进程内有多个 Reactor 线程,基于 epoll/kqueue 进行网络事件轮询。收到数据后转发到 Worker 进程去处理
    • Manager 进程

      • 对所有 Worker 进程进行管理,Worker 进程生命周期结束或者发生异常时自动回收,并创建新的 Worker 进程
    • Worker 进程

      • 对收到的数据进行处理,包括协议解析和响应请求。未设置 worker_num,底层会启动与 CPU 数量一致的 Worker 进程。
      • 启动失败扩展内会抛出致命错误,请检查 php error_log 的相关信息。errno={number} 是标准的 Linux Errno,可参考相关文档。
      • 如果开启了 log_file 设置,信息会打印到指定的 Log 文件中。

 

(8):reload()

安全地重启所有 Worker/Task 进程。

 

Swoole\Server->reload(bool $only_reload_taskworker = false): bool

例如:一台繁忙的后端服务器随时都在处理请求,如果管理员通过 kill 进程方式来终止 / 重启服务器程序,可能导致刚好代码执行到一半终止。
这种情况下会产生数据的不一致。如交易系统中,支付逻辑的下一段是发货,假设在支付逻辑之后进程被终止了。会导致用户支付了货币,但并没有发货,后果非常严重。
Swoole 提供了柔性终止 / 重启的机制,管理员只需要向 Server 发送特定的信号,ServerWorker 进程可以安全的结束。参考 如何正确的重启服务

  • 参数

    • bool $only_reload_taskworker

      • 功能:是否仅重启 Task 进程
      • 默认值:false
      • 其它值:无

-reload 有保护机制,当一次 reload 正在进行时,收到新的重启信号会丢弃
- 如果设置了 user/groupWorker 进程可能没有权限向 master 进程发送信息,这种情况下必须使用 root 账户,在 shell 中执行 kill 指令进行重启
-reload 指令对 addProcess 添加的用户进程无效

扩展

  • 发送信号

    • SIGTERM: 向主进程 / 管理进程发送此信号服务器将安全终止
    • 在 PHP 代码中可以调用 $serv->shutdown() 完成此操作
    • SIGUSR1: 向主进程 / 管理进程发送 SIGUSR1 信号,将平稳地 restart 所有 Worker 进程和 TaskWorker 进程
    • SIGUSR2: 向主进程 / 管理进程发送 SIGUSR2 信号,将平稳地重启所有 Task 进程
    • 在 PHP 代码中可以调用 $serv->reload() 完成此操作
# 重启所有worker进程
kill -USR1 主进程PID

# 仅重启task进程
kill -USR2 主进程PID


      • Process 模式

        Process 启动的进程中,来自客户端的 TCP 连接是在 Master 进程内维持的,worker 进程的重启和异常退出,不会影响连接本身。

      • Base 模式

        Base 模式下,客户端连接直接维持在 Worker 进程中,因此 reload 时会切断所有连接。


        Base 模式不支持 reload Task 进程


      • Reload 有效范围

        Reload 操作只能重新载入 Worker 进程启动后加载的 PHP 文件,使用 get_included_files 函数来列出哪些文件是在 WorkerStart 之前就加载的 PHP 文件,在此列表中的 PHP 文件,即使进行了 reload 操作也无法重新载入。要关闭服务器重新启动才能生效。


$serv->on('WorkerStart', function(Swoole\Server $server, int $workerId) {
    var_dump(get_included_files()); //此数组中的文件表示进程启动前就加载了,所以无法reload
});

      • APC/OPcache

        如果 PHP 开启了 APC/OPcachereload 重载入时会受到影响,有 2 种解决方案

        • 打开 APC/OPcachestat 检测,如果发现文件更新 APC/OPcache 会自动更新 OPCode
        • onWorkerStart 中加载文件(require、include 等函数)之前执行 apc_clear_cacheopcache_reset 刷新 OPCode 缓存
    • 注意

      - 平滑重启只对 onWorkerStartonReceive 等在 Worker 进程中 include/require 的 PHP 文件有效
      -Server 启动前就已经 include/require 的 PHP 文件,不能通过平滑重启重新加载
      - 对于 Server 的配置即 $serv->set() 中传入的参数设置,必须关闭 / 重启整个 Server 才可以重新加载
      -Server 可以监听一个内网端口,然后可以接收远程的控制命令,去重启所有 Worker 进程

 

 

(9):stop()

使当前 Worker 进程停止运行,并立即触发 onWorkerStop 回调函数。

 

Swoole\Server->stop(int $workerId = -1, bool $waitEvent = false): bool
    • 参数

      • int $workerId

        • 功能:指定 worker id
        • 默认值:-1
        • 其它值:无
      • bool $waitEvent

        • 功能:控制退出策略,false 表示立即退出,true 表示等待事件循环为空时再退出
        • 默认值:false
        • 其它值:true
    • 提示

      - 异步 IO 服务器在调用 stop 退出进程时,可能仍然有事件在等待。比如使用了 Swoole\MySQL->query,发送了 SQL 语句,但还在等待 MySQL 服务器返回结果。这时如果进程强制退出,SQL 的执行结果就会丢失了。
      - 设置 $waitEvent = true 后,底层会使用异步安全重启策略。先通知 Manager 进程,重新启动一个新的 Worker 来处理新的请求。当前旧的 Worker 会等待事件,直到事件循环为空或者超过 max_wait_time 后,退出进程,最大限度的保证异步事件的安全性。

 

(10):shutdown()

  关闭服务 Swoole\Server->shutdown(): void

 

  • 提示

    • 此函数可以用在 Worker 进程内
    • 向主进程发送 SIGTERM 也可以实现关闭服务

 

  kill -15 主进程PID

 

(11):tick()

添加 tick 定时器,可以自定义回调函数。此函数是 Swoole\Timer::tick 的别名。

 

Swoole\Server->tick(int $millisecond, mixed $callback): void
    • 参数

      • int $millisecond

        • 功能:间隔时间【毫秒】
        • 默认值:无
        • 其它值:无
      • mixed $callback

        • 功能:回调函数
        • 默认值:无
        • 其它值:无
    • 注意

      -Worker 进程结束运行后,所有定时器都会自动销毁
      -tick/after 定时器不能在 Server->start 之前使用

 

(12):after()

添加一个一次性定时器,执行完成后就会销毁。此函数是 Swoole\Timer::after 的别名。

 

Swoole\Server->after(int $millisecond, mixed $callback)
    • 参数

      • int $millisecond

        • 功能:执行时间【毫秒】
        • 默认值:无
        • 其它值:无
        • 版本影响:在 Swoole v4.2.10 以下版本最大不得超过 86400000
      • mixed $callback

        • 功能:回调函数,必须是可以调用的,callback 函数不接受任何参数
        • 默认值:无
        • 其它值:无
    • 注意

      - 定时器的生命周期是进程级的,当使用 reloadkill 重启关闭进程时,定时器会全部被销毁
      - 如果有某些定时器存在关键逻辑和数据,请在 onWorkerStop 回调函数中实现,或参考 如何正确的重启服务

 

(13):defer()

延后执行一个函数,是 Event::defer 的别名

 

Swoole\Server->defer(callable $callback): void
      • 参数

        • callable $callback

          • 功能:回调函数【必填】,可以是可执行的函数变量,可以是字符串、数组、匿名函数
          • 默认值:无
          • 其它值:无
      • 注意

        - 底层会在 EventLoop 循环完成后执行此函数。此函数的目的是为了让一些 PHP 代码延后执行,程序优先处理其他的 IO 事件。比如某个回调函数有 CPU 密集计算又不是很着急,可以让进程处理完其他的事件再去 CPU 密集计算
        - 底层不保证 defer 的函数会立即执行,如果是系统关键逻辑,需要尽快执行,请使用 after 定时器实现
        - 在 onWorkerStart 回调中执行 defer 时,必须要等到有事件发生才会回调

示例
function query($server, $db) { $server->defer(function() use ($db) { $db->close(); }); }

 

(14):clearTimer()

清除 tick/after 定时器,此函数是 Swoole\Timer::clear 的别名。

 

Swoole\Server->clearTimer(int $timerId): bool
  • 参数

    • int $timerId

      • 功能:指定定时器 id
      • 默认值:无
      • 其它值:无
  • 注意

    clearTimer 仅可用于清除当前进程的定时器

示例
$timerId = $server->tick(1000, function ($id) use ($server) { $server->clearTimer($id);//$id是定时器的id });

 

 

(15):close()

关闭客户端连接

 

Swoole\Server->close(int $fd, bool $reset = false): bool
    • 注意

      -Server 主动 close 连接,也一样会触发 onClose 事件
      - 不要在 close 之后写清理逻辑。应当放置到 onClose 回调中处理
      -HTTP\Serverfd 在上层回调方法的 response 中获取

示例
$server->on('request', function ($request, $response) use ($server) {
    $server->close($response->fd);
});
 

 

(16):send()

向客户端发送数据。

 

Swoole\Server->send(int $fd, string $data, int $serverSocket  = -1): bool

  • 参数

    • int $fd

      • 功能:指定客户端的文件描述符
      • 默认值:无
      • 其它值:无
    • string $data

      • 功能:发送的数据,TCP 协议最大不得超过 2M,可修改 buffer_output_size 改变允许发送的最大包长度
      • 默认值:无
      • 其它值:无
    • int $serverSocket

      • 功能:向 UnixSocket DGRAM 对端发送数据时需要此项参数,TCP 客户端不需要填写
      • 默认值:-1
      • 其它值:无
  • 提示

    发送过程是异步的,底层会自动监听可写,将数据逐步发送给客户端,也就是说不是 send 返回后对端就收到数据了。

    • 安全性

      • send 操作具有原子性,多个进程同时调用 send 向同一个 TCP 连接发送数据,不会发生数据混杂
    • 长度限制

      • 如果要发送超过 2M 的数据,可以将数据写入临时文件,然后通过 sendfile 接口进行发送
      • 通过设置 buffer_output_size 参数可以修改发送长度的限制
      • 在发送超过 8K 的数据时,底层会启用 Worker 进程的共享内存,需要进行一次 Mutex->lock 操作
    • 缓存区

      • Worker 进程的 unixSocket 缓存区已满时,发送 8K 数据将启用临时文件存储
      • 如果连续向同一个客户端发送大量数据,客户端来不及接收会导致 Socket 内存缓存区塞满,Swoole 底层会立即返回 false,false 时可以将数据保存到磁盘,等待客户端收完已发送的数据后再进行发送
    • 协程调度

      • 在协程模式开启了 send_yield 情况下 send 遇到缓存区已满时会自动挂起,当数据被对端读走一部分后恢复协程,继续发送数据。

 

(17):sendfile()

发送文件到 TCP 客户端连接。

 

Swoole\Server->sendfile(int $fd, string $filename, int $offset = 0, int $length = 0): bool

  • 参数

    • int $fd

      • 功能:指定客户端的文件描述符
      • 默认值:无
      • 其它值:无
    • string $filename

      • 功能:要发送的文件路径,如果文件不存在会返回 false
      • 默认值:无
      • 其它值:无
    • int $offset

      • 功能:指定文件偏移量,可以从文件的某个位置起发送数据
      • 默认值:0 【默认为 0,表示从文件头部开始发送】
      • 其它值:无
    • int $length

      • 功能:指定发送的长度
      • 默认值:文件尺寸
      • 其它值:无
  • 注意

    此函数与 Server->send 都是向客户端发送数据,不同的是 sendfile 的数据来自于指定的文件

 

(18):sendto()

向任意的客户端 IP:PORT 发送 UDP 数据包。

 

向任意的客户端 IP:PORT 发送 UDP 数据包。

 

Swoole\Server->sendto(string $ip, int $port, string $data, int $serverSocket = -1): bool

 

  • 参数

    • string $ip

      • 功能:指定客户端 ip
      • 默认值:无
      • 其它值:无

      $ipIPv4IPv6 字符串,如 192.168.1.102。如果 IP 不合法会返回错误

    • int $port

      • 功能:指定客户端 port
      • 默认值:无
      • 其它值:无

      $port1-65535 的网络端口号,如果端口错误发送会失败

    • string $data

      • 功能:要发送的数据内容,可以是文本或者二进制内容
      • 默认值:无
      • 其它值:无
    • int $serverSocket

      • 功能:指定使用哪个端口发送数据包的对应端口 server_socket 描述符【可以在 onPacket 事件$clientInfo 中获取】
      • 默认值:无
      • 其它值:无

      服务器可能会同时监听多个 UDP 端口,参考多端口监听,此参数可以指定使用哪个端口发送数据包

  • 注意

    必须监听了 UDP 的端口,才可以使用向 IPv4 地址发送数据
    必须监听了 UDP6 的端口,才可以使用向 IPv6 地址发送数据

  • 示例

 

//向IP地址为220.181.57.216主机的9502端口发送一个hello world字符串。
$server->sendto('220.181.57.216', 9502, "hello world");
//向IPv6服务器发送UDP数据包
$server->sendto('2600:3c00::f03c:91ff:fe73:e98f', 9501, "hello world");

 

 

(19):sendwait()

 

同步地向客户端发送数据。

 

Swoole\Server->sendwait(int $fd, string $data): bool

 

    • 参数

      • int $fd

        • 功能:指定客户端的文件描述符
        • 默认值:无
        • 其它值:无
      • string $data

        • 功能:指定客户端的文件描述符
        • 默认值:无
        • 其它值:无
    • 提示

      • 有一些特殊的场景,Server 需要连续向客户端发送数据,而 Server->send 数据发送接口是纯异步的,大量数据发送会导致内存发送队列塞满。

      • 使用 Server->sendwait 就可以解决此问题,Server->sendwait 会等待连接可写。直到数据发送完毕才会返回。

    • 注意

      sendwait 目前仅可用于 SWOOLE_BASE 模式
      sendwait 只用于本机或内网通信,外网连接请勿使用 sendwait,在 enable_coroutine=>true (默认开启) 的时候也不要用这个函数,会卡死其他协程,只有同步阻塞的服务器才可以用。

 

 

(20):sendMessage()

 

向任意 Worker 进程或者 Task 进程发送消息。在非主进程和管理进程中可调用。收到消息的进程会触发 onPipeMessage 事件。

 

Swoole\Server->sendMessage(string $message, int $workerId): bool

 

    • 参数

      • string $message

        • 功能:为发送的消息数据内容,没有长度限制,但超过 8K 时会启动内存临时文件
        • 默认值:无
        • 其它值:无
      • int $workerId

        • 功能:目标进程的 ID,范围参考 $worker_id
        • 默认值:无
        • 其它值:无
    • 提示

    • 注意

      - 如果 sendMessage()异步 IO 的,如果对端进程因为种种原因不接收数据,千万不要一直调用 sendMessage(),会导致占用大量的内存资源。可以增加一个应答机制,如果对端不回应就暂停调用;
      -MacOS/FreeBSD下超过 2K 就会使用临时文件存储;
      - 使用 sendMessage 必须注册 onPipeMessage 事件回调函数;
      - 设置了 task_ipc_mode = 3 将无法使用 sendMessage 向特定的 task 进程发送消息。

示例

 

 

 

 

 

(21):exist()

 

检测 fd 对应的连接是否存在。

 

Swoole\Server->exist(int $fd): bool

 

  • 参数

    • int $fd

      • 功能:文件描述符
      • 默认值:无
      • 其它值:无
  • 提示

    • 此接口是基于共享内存计算,没有任何 IO 操作

 

 

(22):pause()

 

停止接收数据。

 

Swoole\Server->pause(int $fd)

 

  • 参数

    • int $fd

      • 功能:指定文件描述符
      • 默认值:无
      • 其它值:无
  • 提示

    • 调用此函数后会将连接从 EventLoop 中移除,不再接收客户端数据。
    • 此函数不影响发送队列的处理
    • 只能在 SWOOLE_PROCESS 模式下,调用 pause 后,可能有部分数据已经到达 Worker 进程,因此仍然可能会触发 onReceive 事件

 

 

(23):resume()

 

恢复数据接收。与 pause 方法成对使用。

 

Swoole\Server->resume(int $fd)

 

  • 参数

    • int $fd

      • 功能:指定文件描述符
      • 默认值:无
      • 其它值:无
  • 提示

    • 调用此函数后会将连接重新加入到 EventLoop 中,继续接收客户端数据

 

 

(24):getCallback()

 

获取 Server 指定名称的回调函数

 

Swoole\Server->getCallback(string $event_name)

 

  • 参数

    • string $event_name

      • 功能:事件名称,不需要加 on,不区分大小写
      • 默认值:无
      • 其它值:参考 事件
  • 返回值

    • 对应回调函数存在时,根据不同的回调函数设置方式返回 Closure / string / array
    • 对应回调函数不存在时,返回 null

 

 

(25):getClientInfo()

 

获取连接的信息,别名是 Swoole\Server->connection_info()

 

Swoole\Server->getClientInfo(int $fd, int $reactorId, bool $ignoreError = false): bool|array

 

  • 参数

    • int $fd

      • 功能:指定文件描述符
      • 默认值:无
      • 其它值:无
    • int $reactorId

      • 功能:连接所在的 Reactor 线程 ID
      • 默认值:无
      • 其它值:无
    • bool $ignoreError

      • 功能:是否忽略错误,如果设置为 true,即使连接关闭也会返回连接的信息
      • 默认值:无
      • 其它值:无
  • 提示

    • 客户端证书

      • 仅在 onConnect 触发的进程中才能获取到证书
      • 格式为 x509 格式,可使用 openssl_x509_parse 函数获取到证书信息
    • 当使用 dispatch_mode = 1/3 配置时,考虑到这种数据包分发策略用于无状态服务,当连接断开后相关信息会直接从内存中删除,所以 Server->getClientInfo 是获取不到相关连接信息的。

  • 返回值

    • 调用失败返回 false
    • 调用成功返回 array

 

 

示例

 

 

 

 

 

 

 

(26):getClientList()

 

遍历当前 Server 所有的客户端连接,Server::getClientList 方法是基于共享内存的,不存在 IOWait,遍历的速度很快。另外 getClientList 会返回所有 TCP 连接,而不仅仅是当前 Worker 进程的 TCP 连接。别名是 Swoole\Server->connection_list()

 

Swoole\Server->getClientList(int $start_fd = 0, int $pageSize = 10): bool|array

 

  • 参数

    • int $start_fd

      • 功能:指定起始 fd
      • 默认值:无
      • 其它值:无
    • int $pageSize

      • 功能:每页取多少条,最大不得超过 100
      • 默认值:无
      • 其它值:无
  • 返回值

    • 调用成功将返回一个数字索引数组,元素是取到的 $fd。数组会按从小到大排序。最后一个 $fd 作为新的 start_fd 再次尝试获取
    • 调用失败返回 false
  • 提示

    • 推荐使用 Server::$connections 迭代器来遍历连接
    • getClientList 仅可用于 TCP 客户端,UDP 服务器需要自行保存客户端信息
    • SWOOLE_BASE 模式下只能获取当前进程的连接

 

 

 

 

 

 

(27):bind()

将连接绑定一个用户定义的 UID,可以设置 dispatch_mode=5 设置以此值进行 hash 固定分配。可以保证某一个 UID 的连接全部会分配到同一个 Worker 进程。

 

Swoole\Server->bind(int $fd, int $uid): bool

 

  • 注意

 

- 仅在设置 dispatch_mode=5 时有效
- 未绑定 UID 时默认使用 fd 取模进行分配
- 同一个连接只能被 bind 一次,如果已经绑定了 UID,再次调用 bind 会返回 false

 

  • 参数

    • int $fd

      • 功能:指定连接的 fd
      • 默认值:无
      • 其它值:无
    • int $uid

      • 功能:要绑定的 UID,必须为非 0 的数字
      • 默认值:无
      • 其它值:UID 最大不能超过 4294967295,最小不能小于 -2147483648
  • 提示

    • 可以使用 $serv->getClientInfo($fd) 查看连接所绑定 UID 的值

    • 在默认的 dispatch_mode=2 设置下,Server 会按照 socket fd 来分配连接数据到不同的 Worker 进程。因为 fd 是不稳定的,一个客户端断开后重新连接,fd 会发生改变。这样这个客户端的数据就会被分配到别的 Worker。使用 bind 之后就可以按照用户定义的 UID 进行分配。即使断线重连,相同 UIDTCP 连接数据会被分配相同的 Worker 进程。

    • 时序问题

      • 客户端连接服务器后,连续发送多个包,可能会存在时序问题。在 bind 操作时,后续的包可能已经 dispatch,这些数据包仍然会按照 fd 取模分配到当前进程。只有在 bind 之后新收到的数据包才会按照 UID 取模分配。
      • 因此如果要使用 bind 机制,网络通信协议需要设计握手步骤。客户端连接成功后,先发一个握手请求,之后客户端不要发任何包。在服务器 bind 完后,并回应之后。客户端再发送新的请求。
    • 重新绑定

      • 某些情况下,业务逻辑需要用户连接重新绑定 UID。这时可以切断连接,重新建立 TCP 连接并握手,绑定到新的 UID

 

 

 

 

 

 

(28):stats()

 

得到当前 Server 的活动 TCP 连接数,启动时间等信息,accept/close(建立连接 / 关闭连接) 的总次数等信息。

 

Swoole\Server->stats(): array

 

 


 

 

(29):task()

投递一个异步任务到 task_worker 池中。此函数是非阻塞的,执行完毕会立即返回。Worker 进程可以继续处理新的请求。使用 Task 功能,必须先设置 task_worker_num,并且必须设置 ServeronTaskonFinish 事件回调函数。

 

Swoole\Server->task(mixed $data, int $dstWorkerId = -1, callable $finishCallback): int
    • 参数

      • mixed $data

        • 功能:要投递的任务数据,必须是可序列化的 PHP 变量
        • 默认值:无
        • 其它值:无
      • int $dstWorkerId

        • 功能:可以指定要给投递给哪个 Task 进程,传入 Task 进程的 ID 即可,范围为 [0, $server->setting['task_worker_num']-1]
        • 默认值:-1【默认为 -1 表示随机投递,底层会自动选择一个空闲 Task 进程
        • 其它值:[0, $server->setting['task_worker_num']-1]
      • callable $finishCallback

        • 功能:finish 回调函数,如果任务设置了回调函数,Task 返回结果时会直接执行指定的回调函数,不再执行 ServeronFinish 回调,只有在 Worker 进程中投递任务才可触发
        • 默认值:null
        • 其它值:无
    • 返回值

      • 调用成功,返回值为整数 $task_id,表示此任务的 ID。如果有 finish 回调,onFinish 回调中会携带 $task_id 参数
      • 调用失败,返回值为 false$task_id 可能为 0,因此必须使用 === 判断是否失败
    • 提示

      • 此功能用于将慢速的任务异步地去执行,比如一个聊天室服务器,可以用它来进行发送广播。当任务完成时,在 task 进程中调用 $serv->finish("finish") 告诉 worker 进程此任务已完成。当然 Swoole\Server->finish 是可选的。
      • task 底层使用 unixSocket 通信,是全内存的,没有 IO 消耗。单进程读写性能可达 100万/s,不同的进程使用不同的 unixSocket 通信,可以最大化利用多核。
      • 未指定目标 Task 进程,调用 task 方法会判断 Task 进程的忙闲状态,底层只会向处于空闲状态的 Task 进程投递任务。如果所有 Task 进程均处于忙的状态,底层会轮询投递任务到各个进程。可以使用 server->stats 方法获取当前正在排队的任务数量。
      • 第三个参数,可以直接设置 onFinish 函数,如果任务设置了回调函数,Task 返回结果时会直接执行指定的回调函数,不再执行 ServeronFinish 回调,只有在 Worker 进程中投递任务才可触发
      $server->task($data, -1, function (Swoole\Server $server, $task_id, $data) {
          echo "Task Callback: ";
          var_dump($task_id, $data);
      });
      • $task_id 是从 0-42 亿的整数,在当前进程内是唯一的
      • 默认不启动 task 功能,需要在手动设置 task_worker_num 来启动此功能
      • TaskWorker 的数量在 Server->set() 参数中调整,如 task_worker_num => 64,表示启动 64 个进程来接收异步任务
    • 配置参数

      • Server->task/taskwait/finish 3 个方法当传入的 $data 数据超过 8K 时会启用临时文件来保存。当临时文件内容超过 server->package_max_length 时底层会抛出一个警告。此警告不影响数据的投递,过大的 Task 可能会存在性能问题。
      WARN: task package is too big.
    • 单向任务

      • MasterManagerUserProcess 进程中投递的任务,是单向的,在 TaskWorker 进程中无法使用 returnServer->finish() 方法返回结果数据。
    • 注意

      -task 方法不能在 task 进程中调用
      - 使用 task 必须为 Server 设置 onTaskonFinish 回调,否则 Server->start 会失败
      -task 操作的次数必须小于 onTask 处理速度,如果投递容量超过处理能力,task 数据会塞满缓存区,导致 Worker 进程发生阻塞。Worker 进程将无法接收新的请求
      - 使用 addProcess 添加的用户进程中可以使用 task 单向投递任务,但不能返回结果数据。请使用 sendMessage 接口与 Worker/Task 进程通信

 

 

 
 

 

 

(30):taskwait()

 

taskwaittask 方法作用相同,用于投递一个异步的任务到 task 进程池去执行。与 task 不同的是 taskwait 是同步等待的,直到任务完成或者超时返回。$result 为任务执行的结果,由 $server->finish 函数发出。如果此任务超时,这里会返回 false

 

Swoole\Server->taskwait(mixed $data, float $timeout = 0.5, int $dstWorkerId = -1): string|bool

 

    • 参数

      • mixed $data

        • 功能:投递的任务数据,可以是任意类型,非字符串类型底层会自动进行串化
        • 默认值:无
        • 其它值:无
      • float $timeout

        • 功能:超时时间,浮点型,单位为秒,最小支持 1ms 粒度,超过规定时间内 Task 进程未返回数据,taskwait 将返回 false,不再处理后续的任务结果数据
        • 默认值:无
        • 其它值:无
      • int $dstWorkerId

        • 功能:指定要给投递给哪个 Task 进程,传入 Task 进程的 ID 即可,范围为 [0, $server->setting['task_worker_num']-1]
        • 默认值:-1【默认为 -1 表示随机投递,底层会自动选择一个空闲 Task 进程
        • 其它值:[0, $server->setting['task_worker_num']-1]
    • 提示

      • 协程模式

        • 4.0.4 版本开始 taskwait 方法将支持协程调度,在协程中调用 Server->taskwait() 时将自动进行协程调度,不再阻塞等待。
        • 借助协程调度器,taskwait 可以实现并发调用。
      • 同步模式

        • 在同步阻塞模式下,taskwait 需要使用 UnixSocket 通信和共享内存,将数据返回给 Worker 进程,这个过程是同步阻塞的。
      • 特例

        • 如果 onTask 中没有任何同步 IO 操作,底层仅有 2 次进程切换的开销,并不会产生 IO 等待,因此这种情况下 taskwait 可以视为非阻塞。实际测试 onTask 中仅读写 PHP 数组,进行 10 万次 taskwait 操作,总耗时仅为 1 秒,平均每次消耗为 10 微秒
    • 注意

      -Swoole\Server::finish, 不要使用 taskwait
      -taskwait 方法不能在 task 进程中调用

 

 

(31):taskWaitMulti()

 

并发执行多个 task 异步任务,此方法不支持协程调度,会导致其他协程开始,协程环境下需要用下文的 taskCo

 

Swoole\Server->taskWaitMulti(array $tasks, float $timeout = 0.5): bool|array

 

  • 参数

    • array $tasks

      • 功能:必须为数字索引数组,不支持关联索引数组,底层会遍历 $tasks 将任务逐个投递到 Task 进程
      • 默认值:无
      • 其它值:无
    • float $timeout

      • 功能:为浮点型,单位为秒
      • 默认值:0.5 秒
      • 其它值:无
  • 返回值

    • 任务完成或超时,返回结果数组。结果数组中每个任务结果的顺序与 $tasks 对应,如:$tasks[2] 对应的结果为 $result[2]
    • 某个任务执行超时不会影响其他任务,返回的结果数据中将不包含超时的任务
  • 注意

    - 最大并发任务不得超过 1024

 

 

 

 

 

 

(32):taskCo()

 

并发执行 Task 并进行协程调度,用于支持协程环境下的 taskWaitMulti 功能。

 

Swoole\Server->taskCo(array $tasks, float $timeout = 0.5): array

 

  • $tasks 任务列表,必须为数组。底层会遍历数组,将每个元素作为 task 投递到 Task 进程池
  • $timeout 超时时间,默认为 0.5 秒,当规定的时间内任务没有全部完成,立即中止并返回结果
  • 任务完成或超时,返回结果数组。结果数组中每个任务结果的顺序与 $tasks 对应,如:$tasks[2] 对应的结果为 $result[2]
  • 某个任务执行失败或超时,对应的结果数组项为 false,如:$tasks[2] 失败了,那么 $result[2] 的值为 false

 

最大并发任务不得超过 1024

 

  • 调度过程

    • $tasks 列表中的每个任务会随机投递到一个 Task 工作进程,投递完毕后,yield 让出当前协程,并设置一个 $timeout 秒的定时器
    • onFinish 中收集对应的任务结果,保存到结果数组中。判断是否所有任务都返回了结果,如果为否,继续等待。如果为是,进行 resume 恢复对应协程的运行,并清除超时定时器
    • 在规定的时间内任务没有全部完成,定时器先触发,底层清除等待状态。将未完成的任务结果标记为 false,立即 resume 对应协程

 

 

示例

 

 

 

 

(33):finish()

 

用于在 Task 进程中通知 Worker 进程,投递的任务已完成。此函数可以传递结果数据给 Worker 进程。

 

Swoole\Server->finish(mixed $data)

 

  • 参数

    • mixed $data

      • 功能:任务处理的结果内容
      • 默认值:无
      • 其它值:无
  • 提示

    • finish 方法可以连续多次调用,Worker 进程会多次触发 onFinish 事件
    • onTask 回调函数中调用过 finish 方法后,return 数据依然会触发 onFinish 事件
    • Server->finish 是可选的。如果 Worker 进程不关心任务执行的结果,不需要调用此函数
    • onTask 回调函数中 return 字符串,等同于调用 finish
  • 注意

    使用 Server->finish 函数必须为 Server 设置 onFinish 回调函数。此函数只可用于 Task 进程onTask 回调中

 

 

(34):heartbeat()

 

heartbeat_check_interval 的被动检测不同,此方法主动检测服务器所有连接,并找出已经超过约定时间的连接。如果指定 if_close_connection,则自动关闭超时的连接。未指定仅返回连接的 fd 数组。

 

Swoole\Server->heartbeat(bool $ifCloseConnection = true): bool|array

 

  • 参数

    • bool $ifCloseConnection

      • 功能:是否关闭超时的连接
      • 默认值:true
      • 其它值:false
  • 返回值

    • 调用成功将返回一个连续数组,元素是已关闭的 $fd
    • 调用失败返回 false
  • 示例

 

$closeFdArrary = $server->heartbeat();

 

 

(35):getLastError()

 

获取最近一次操作错误的错误码。业务代码中可以根据错误码类型执行不同的逻辑。

 

Swoole\Server->getLastError(): int

 

 

 

 

 

(36):getSocket()

 

调用此方法可以得到底层的 socket 句柄,返回的对象为 sockets 资源句柄。

 

Swoole\Server->getSocket()

 

此方法需要依赖 PHP 的 sockets 扩展,并且编译 Swoole 时需要开启 --enable-sockets 选项

 

      • 监听端口

        • 使用 listen 方法增加的端口,可以使用 Swoole\Server\Port 对象提供的 getSocket 方法。
        $port = $server->listen('127.0.0.1', 9502, SWOOLE_SOCK_TCP);
        $socket = $port->getSocket();
      • 使用 socket_set_option 函数可以设置更底层的一些 socket 参数。

 

$socket = $server->getSocket();
if (!socket_set_option($socket, SOL_SOCKET, SO_REUSEADDR, 1)) {
    echo 'Unable to set option on socket: '. socket_strerror(socket_last_error()) . PHP_EOL;
}

 

  • 支持组播

    • 使用 socket_set_option 设置 MCAST_JOIN_GROUP 参数可以将 Socket 加入组播,监听网络组播数据包。

 

 

 

 

(37):protect()

 

设置客户端连接为保护状态,不被心跳线程切断。

 

Swoole\Server->protect(int $fd, bool $value = true)

 

  • 参数

    • int $fd

      • 功能:指定客户端连接 fd
      • 默认值:无
      • 其它值:无
    • bool $value

      • 功能:设置的状态
      • 默认值:true 【表示保护状态】
      • 其它值:false 【表示不保护】

 

(38):confirm()

 

确认连接,与 enable_delay_receive 配合使用。当客户端建立连接后,并不监听可读事件,仅触发 onConnect 事件回调,在 onConnect 回调中执行 confirm 确认连接,这时服务器才会监听可读事件,接收来自客户端连接的数据。

 

Swoole 版本 >= v4.5.0 可用

 

Swoole\Server->confirm(int $fd)

 

  • 参数

    • int $fd

      • 功能:连接的唯一标识符
      • 默认值:无
      • 其它值:无
  • 返回值

    • 确认成功返回 true
    • $fd 对应的连接不存在、已关闭或已经处于监听状态时,返回 false,确认失败
  • 用途

    此方法一般用于保护服务器,避免收到流量过载攻击。当收到客户端连接时 onConnect 函数触发,可判断来源 IP,是否允许向服务器发送数据。

 

 

 

 

(39):getWorkId()

 

获取当前 Worker 进程 id(非进程的 PID),和 onWorkerStart 时的 $workerId 一致

 

Swoole\Server->getWorkerId(): int|false

 Swoole 版本 >= v4.5.0RC1 可用

(40):getWorkerPid()

 

获取当前 Worker 进程 PID

 

Swoole\Server->getWorkerPid(): int|false

  Swoole 版本 >= v4.5.0RC1 可用

(41):getWorkerStatus()

 

获取 Worker 进程状态

 

Swoole\Server->getWorkerStatus(int $worker_id): int|false

 

Swoole 版本 >= v4.5.0RC1 可用

 

  • 参数

    • int $worker_id

      • 功能:Worker 进程 id
      • 默认值:当前 Worker 进程 id
      • 其它值:无
  • 返回值

    • 返回 Worker 进程状态,参考进程状态值
    • 不是 Worker 进程或者进程不存在返回 false

 

 

 

 

(42):getManagerPid()

 

获取当前服务的 Manager 进程 PID

 

Swoole\Server->getManagerPid(): int

Swoole 版本 >= v4.5.0RC1 可用

 

(43):getMasterPid()

 

获取当前服务的 Master 进程 PID

 

Swoole\Server->getMasterPid(): int

Swoole 版本 >= v4.5.0RC1 可用

(44):addCommand()

 

添加一个 command

 

Swoole\Server->addCommand(string $name, int $accepted_process_types, callable $callback): bool

 

Swoole 版本 >= v4.8.0 可用

 

    • 参数

      • string $name

        • 功能:command 名称
        • 默认值:无
        • 其它值:无
      • int $accepted_process_types

        • 功能:接受请求的进程类型
        • 默认值:无
        • 其它值:SWOOLE_SERVER_COMMAND_MASTERSWOOLE_SERVER_COMMAND_MANAGERSWOOLE_SERVER_COMMAND_EVENT_WORKERSWOOLE_SERVER_COMMAND_TASK_WORKER
      • callable $callback

        * 功能:回调函数
        * 默认值:无
        * 其它值:无
  • 返回值

    • 返回 Worker 进程状态,参考进程状态值
    • 不是 Worker 进程或者进程不存在返回 false

 

(45):command()

 

调用 command

 

Swoole\Server->command(string $name, int $process_id, int $process_type, $data, bool $json_decode = true)

 

Swoole 版本 >= v4.8.0 可用

 

  • 参数

    • string $name

      • 功能:command 名称
      • 默认值:无
      • 其它值:无
    • int $process_id

      • 功能:进程 ID
      • 默认值:无
      • 其它值:无
    • int $process_type

      • 功能:进程请求类型
      • 默认值:无
      • 其它值:SWOOLE_SERVER_COMMAND_MASTERSWOOLE_SERVER_COMMAND_MANAGERSWOOLE_SERVER_COMMAND_EVENT_WORKERSWOOLE_SERVER_COMMAND_TASK_WORKER
    • $data

      • 功能:请求的数据
      • 默认值:无
      • 其它值:无
    • bool $json_decode

      * 功能:是否使用`json_decode`解析
      * 默认值:无
      * 其它值:无

 



































posted @ 2022-03-31 15:32  痞子胥  阅读(112)  评论(0)    收藏  举报