无论对于B/S还是C/S,程序再怎么变,唯一不变的是用户不想等太久的躁动心情。所以服务端对于客户的请求,能有多快就多快。如果服务端需要执行很耗时的操作,就需要异步任务处理机制,保证当前的响应速度不受影响。

现在以下面的一个例子为引子:

<?php
$server = new Swoole\Server('0.0.0.0', 9501);
$server->set([
'max_wait_time'=>60,
'reload_async'=>true,
'worker_num'=>1,
'task_worker_num'=>1,
'task_max_request'=>100
]); //监听连接进入事件
$server->on('Connect', function ($server, $fd) { }); //监听数据接收事件
$server->on('Receive', function ($server, $fd, $from_id, $data) {
$server->task("receive to task:$data"); //将任务丢入异步任务列队
}); $server->on("task",function ($serv, $fd, $from_id, $data){
//在这里处理任务
$serv->finish("$data ok"); //处理完成后,将结果传给finish
}); $server->on("finish",function ($serv, $task_id, $data){
//处理任务完成后的事情
echo "finish $data\n";
}); //监听连接关闭事件
$server->on('Close', function ($server, $fd) { }); //启动服务器
$server->start();

上面这个例子,在之前的代码上增加了两个属性(task_worker_num和task_max_request)和两个事件(onTask和onFinish)。代码中同时设置了这四项,便可以启动swoole的异步任务处理。是不是很简单?

那么现在分别了解关于异步任务的新属性和事件:

新配置:

1) task_worker_num:配置 Task 进程的数量。【默认值:未配置则不启动 task】

配置此参数后将会启用 task 功能。所以 Server 必须要注册 onTask、onFinish 2 个事件回调函数。如果没有注册,服务器程序将无法启动。

注意:

Task 进程是同步阻塞的;

最大值不得超过 swoole_cpu_num() * 1000;

如果单个 task 的处理需要 100ms,那一个进程 1 秒就可以处理 1/0.1=10 个 task;则task 投递的速度,如每秒产生 2000 个 task,2000/10=200,就需要设置 task_worker_num => 200,启用 200 个 Task 进程;

Task 进程内不能使用 Swoole\Server->task 方法。

2) task_max_request:设置 task 进程的最大任务数。【默认值:0】

设置 task 进程的最大任务数。一个 task 进程在处理完超过此数值的任务后将自动退出。这个参数是为了防止 PHP 进程内存溢出。如果不希望进程自动退出可以设置为 0。

3) task_tmpdir:设置 task 的数据临时目录。【默认值:Linux /tmp 目录】

在 Server 中,如果投递的数据超过 8180 字节,将启用临时文件来保存数据。这里的 task_tmpdir 就是用来设置临时文件保存的位置。

注意:

底层默认会使用 /tmp 目录存储 task 数据,如果你的 Linux 内核版本过低,/tmp 目录不是内存文件系统,可以设置为 /dev/shm/;

task_tmpdir 目录不存在,底层会尝试自动创建

4)task_use_object:使用面向对象风格的 Task 回调格式。【默认值:false】

设置为 true 时,onTask 回调将变成对象模式。

//面向对象风格代码示例$server = new Swoole\Server('127.0.0.1', 9501);
$server->set([
'worker_num' => 1,
'task_worker_num' => 3,
'task_use_object' => true,
]);
$server->on('receive', function (Swoole\Server $server, $fd, $tid, $data) {
$server->task(['fd' => $fd,]);
});
$server->on('Task', function (Swoole\Server $server, Swoole\Server\Task $task) {
//此处$task是Swoole\Server\Task对象
$server->send($task->data['fd'], json_encode($server->stats()));
});
$server->start();

5) task_ipc_mode(进阶):设置 Task 进程与 Worker 进程之间通信的方式。【默认值:1】

这是一个进阶属性,正常情况下是不用设置,使用默认值即可。要了解这个属性,请先看文末的高级话题 :什么是IPC?

知道了什么是IPC后,这个属性可取以下三个值:

其中:

模式1:支持定向投递,可在 task 和 taskwait 方法中使用 dst_worker_id,指定目标 Task进程。dst_worker_id 设置为 -1 时,底层会判断每个 Task 进程的状态,向当前状态为空闲的进程投递任务。

模式2、3:消息队列模式使用操作系统提供的内存队列存储数据,未指定 mssage_queue_key 消息队列 Key,将使用私有队列,在 Server 程序终止后会删除消息队列。指定消息队列 Key 后 Server 程序终止后,消息队列中的数据不会删除,因此进程重启后仍然能取到数据。这两者的不同之处在于,模式2 支持定向投递,$serv->task($data, $task_worker_id) 可以指定投递到哪个 task 进程。模式3 是完全争抢模式, task 进程会争抢队列,将无法使用定向投递,task/taskwait 将无法指定目标进程 ID,即使指定了 $task_worker_id,在模式3 下也是无效的。另外:模式3 会影响 sendMessage 方法,使 sendMessage 发送的消息会随机被某一个 task 进程获取。

新函数

函数task:投递一个异步任务到 task_worker 池中。此函数是非阻塞的,执行完毕会立即返回。Worker 进程可以继续处理新的请求。

Swoole\Server->task(mixed $data, int $dstWorkerId = -1): int

$data:要投递的任务数据,必须是可序列化的 PHP 变量。

$dstWorkerId:可以指定要给投递给哪个 Task 进程,传入 ID 即可,范围参考 $worker_id;默认值:-1。

函数finish: 用于在 Task 进程中通知 Worker 进程,投递的任务已完成。此函数可以传递结果数据给 Worker 进程(即,触发worker进程的onFinish事件)。

Swoole\Server->finish(mixed $data)

$data:任务处理的结果内容

注意:·

finish 方法可以连续多次调用,Worker 进程会多次触发 onFinish 事件;

在 onTask 回调函数中调用过 finish 方法后,return 数据依然会触发 onFinish 事件;

Server->finish 是可选的。如果 Worker 进程不关心任务执行的结果,不需要调用此函数;

在 onTask 回调函数中 return 字符串,等同于调用 finish;

新事件

1) 事件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);

$server:Swoole\Server 对象

$task_id:执行任务的 task 进程 id【$task_id 和 $src_worker_id 组合起来才是全局唯一的,不同的 worker 进程投递的任务 ID 可能会有相同】

$src_worker_id:投递任务的 worker 进程 id

$data:任务的数据内容

注意:

如果开启了 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']); //完成任务,结束并返回数据
});

在 onTask 函数中 return 字符串(return 的变量可以是任意非 null 的 PHP 变量),表示将此内容返回给 worker 进程。也可以通过 Swoole\Server->finish() 来触发 onFinish 函数,而无需再 return。此时worker 进程中会触发 onFinish 函数,表示投递的 task 已完成。

onTask 函数执行时遇到致命错误退出,或者被外部进程强制 kill,当前的任务会被丢弃,但不会影响其他正在排队的 Task。

2)事件onFinish:在 worker 进程被调用,当 worker 进程投递的任务在 task 进程中完成时被触发。

function onFinish(Swoole\Server $server, int $task_id, mixed $data)

$server:Swoole\Server 对象;

$task_id:执行任务的 task 进程 id;

$data:任务处理的结果内容。

注意:

- task 进程的 onTask 事件中没有调用 finish 方法或者 return 结果,worker 进程不会触发 onFinish。

-执行 onFinish 逻辑的 worker 进程与下发 task 任务的 worker 进程是同一个进程。

关于异步任务的注意点:

-使用消息队列通信,如果 Task进程 处理能力低于投递速度,可能会引起 Worker 进程阻塞。

-使用消息队列通信后 task 进程无法支持协程 (开启 task_enable_coroutine)。

----------- 高级话题分隔线--------------

什么是IPC

同一台主机上两个进程间通信 (简称 IPC) 的方式有很多种,在 Swoole 下我们使用了 2 种方式 Unix Socket 和 sysvmsg,下面分别介绍:

第一种:Unix Socket

全名 UNIX Domain Socket, 简称 UDS, 使用套接字的 API (socket,bind,listen,connect,read,write,close 等),和 TCP/IP 不同的是不需要指定 ip 和 port,而是通过一个文件名来表示 (例如 FPM 和 Nginx 之间的 /tmp/php-fcgi.sock),UDS 是 Linux 内核实现的全内存通信,无任何 IO 消耗。在 1 进程 write,1 进程 read,每次读写 1024 字节数据的测试中,100 万次通信仅需 1.02 秒,而且功能非常的强大,Swoole 下默认用的就是这种 IPC 方式

Swoole 下面使用 UDS 通讯有两种类型:SOCK_STREAM 和 SOCK_DGRAM,可以简单的理解为 TCP 和 UDP 的区别,当使用 SOCK_STREAM 类型的时候同样需要考虑 TCP 粘包问题。

当使用 SOCK_DGRAM 类型的时候不需要考虑粘包问题,每个 send() 的数据都是有边界的,发送多大的数据接收的时候就收到多大的数据,没有传输过程中的丢包、乱序问题,send 写入和 recv 读取的顺序是完全一致的。send 返回成功后一定是可以 recv 到。
在 IPC 传输的数据比较小时非常适合用 SOCK_DGRAM 这种方式,由于 IP 包每个最大有 64k 的限制,所以用 SOCK_DGRAM 进行 IPC 时候单次发送数据不能大于 64k,同时要注意收包速度太慢操作系统缓冲区满了会丢弃包,因为 UDP 是允许丢包的,可以适当调大缓冲区。

第二种:sysvmsg

即 Linux 提供的消息队列,这种 IPC 方式通过一个文件名来作为 key 进行通讯,这种方式非常的不灵活,实际项目使用的并不多,不做过多介绍。此种 IPC 方式只有两个场景下有用:

1)防止丢数据,如果整个服务都挂掉,再次启动队列中的消息也在,可以继续消费,但同样有脏数据的问题。

2)可以外部投递数据,比如 Swoole 下的 Worker进程通过消息队列给 Task进程投递任务,第三方的进程也可以投递任务到队列里面让 Task 消费,甚至可以在命令行手动添加消息到队列。

---------------------------  我是可爱的分割线  ----------------------------

最后博主借地宣传一下,漳州编程小组招新了,这是一个面向漳州青少年信息学/软件设计的学习小组,有意向的同学点击链接,联系我吧。

Swoole从入门到入土(5)——TCP服务器[异步任务]的更多相关文章

  1. 【Swoole】简单安装与创建TCP服务器

    pecl install swoole PHP的异步.并行.高性能网络通信引擎,使用纯C语言编写,提供了php语言的异步多线程服务器,异步TCP/UDP网络客户端,异步MySQL,异步Redis,数据 ...

  2. php的异步非阻塞swoole模块使用(一)实现简易tcp服务器--服务端

    绑定tcp服务器的地址 $swserver = new swoole_server("127.0.0.1",9501); 设置tcp服务器装机容量(太危言耸听了-其实就是设置属性) ...

  3. HTTP从入门到入土(3)——TCP三次握手

    TCP三次握手 客户端与服务器之间互相发送HTTP请求响应之前需要先进行TCP连接,因为HTTP是一个无连接.无状态协议,不存在连接的概念,只有请求和响应的概念.而请求和响应实际上只是数据包,他们需要 ...

  4. php的异步非阻塞swoole模块使用(一)实现简易tcp服务器--客户端

    //实例化一个swoole客户端 $swclient = new swoole_client(SWOOLE_SOCK_TCP); //建立连接---如果连接无效则退出 )){ echo "连 ...

  5. Swoole学习(七)Swoole之异步TCP服务器的创建

    环境:Centos6.4,PHP环境:PHP7 <?php //创建TCP服务器 /** * $host 是swoole需要监听的ip,如果要监听本地,不对外服务,那么就是127.0.0.1;如 ...

  6. Swoole学习(二)Swoole之TCP服务器的创建

    环境:Centos6.4,PHP环境:PHP7 <?php //创建TCP服务器 /** * $host 是swoole需要监听的ip,如果要监听本地,不对外服务,那么就是127.0.0.1;如 ...

  7. swoole 创建tcp服务器

    server.php <?php /** * 创建tcp服务器 * Date: 2019/1/15 */ $serv = new swoole_server('127.0.0.1', 9501) ...

  8. Swoole系列(三):建立TCP服务器并发送数据测试

    <?php // 建立tcp服务器下 $host = '0.0.0.0'; $port = 9501; $serv = new swoole_server($host,$port); $serv ...

  9. 18-ESP8266 SDK开发基础入门篇--TCP 服务器 RTOS版,串口透传,TCP客户端控制LED

    https://www.cnblogs.com/yangfengwu/p/11112015.html 先规定一下协议 aa 55 02 01 F1 4C 控制LED点亮  F1 4C为CRC高位和低位 ...

  10. 17-ESP8266 SDK开发基础入门篇--TCP服务器 RTOS版,小试牛刀

    https://www.cnblogs.com/yangfengwu/p/11105466.html 现在开始写... lwip即可以用socket 的API  也可以用 netconn  的API实 ...

随机推荐

  1. Go-稀疏数组

    package main import "fmt" // 稀疏数组 // 1. 二维数组 // 2. 存在大量相同相同数据和少量不同数据 // 思维: 将大量相同数据转化为: (数 ...

  2. [转帖]TiKV Control 使用说明

    https://docs.pingcap.com/zh/tidb/stable/tikv-control TiKV Control(以下简称 tikv-ctl)是 TiKV 的命令行工具,用于管理 T ...

  3. [转帖]Nginx 反向代理解决跨域问题

    https://juejin.cn/post/6995374680114741279 编写代码两分钟,解决跨域两小时,我吐了. 如果对跨域还不了解的朋友,可以看这篇:[基础]HTTP.TCP/IP 协 ...

  4. [转帖]SSL数字证书分类DV/OV/EV

    SSL证书的分类主要是通过下面两个维度进行分类: 1.根据验证模式分类 根据CA机构对申请者的身份审核范围分为:DV证书.OV证书.EV证书. 1.1.DV证书(域名证书) DV(Domain Val ...

  5. [转帖]Linux内核线程kthread简介【最好的一篇!】

    https://zhuanlan.zhihu.com/p/581587583 Linux内核可以看作一个服务进程(管理软硬件资源,响应用户进程的种种合理以及不合理的请求).内核需要多个执行流并行,为了 ...

  6. [转帖]jmeter 响应时间rt很小,但是tps也很小&jmeter,脚本处理,千万不要用js

    一.背景: 在压测的时候,查看jmeter聚合报告,发现rt很小,但是tps也很小. 讲道理来说,响应时间越小,tps应该越大. 一共压测10分钟,发现jmeter请求的样本数量非常小,才8500个请 ...

  7. [转帖]整理常用的 vim 命令

    vim 是一款功能强大的文本编辑器,它是Linux下常用的编辑器之一,对于熟练掌握了 vim 的人来说,用它编辑文件,方便又快捷,能极大的提高工作效率 vim 功能强大,对应的命令也非常的多,对于初学 ...

  8. 根目录被赋予777 -R权限后的处理过程

    解决某研发手残导致的系统宕机问题的处理过程 背景 2022.8.8 公司一台服务器出现了宕机的现象: 所有的人都无法远程, 都提示密码错误. 但是网络还是通的. 2022.8.12 出差前一天去了一趟 ...

  9. MySQL批量执行SQL修改视图属主的办法

    前人挖坑 后人填坑 Study From https://blog.csdn.net/carefree2005/article/details/109812943 第一步: 形成SQL select ...

  10. ngnix在linux安装并设置反向代理

    一.nginx安装 1.安装Nginx依赖的环境 安装Nginx依赖的gcc的编译环境: yum install gcc-c++ Nginx的http模块需要使用pcre来解析正则表达式,需要安装pc ...