遥不可及|Swoft源码之Swoole和Swoft的分析( 三 )

swoft.pipeMessage事件最终由PipeMessageListener处理 。 在相关的监听其中 , 如果发现swoft.pipeMessage事件由Task::deliverByProcess()产生的 , Worker进程会替其执行一次Task::deliver() , 最终将任务数据投递到TaskWorker进程中 。
一道简单的回顾练习:从Task::deliverByProcess()到某TaskBean 最终执行任务 , 经历了哪些进程 , 而调用链的哪些部分又分别是在哪些进程中执行?
从Command进程或其子进程中投递任务【遥不可及|Swoft源码之Swoole和Swoft的分析】//Swoft\Task\QueueTask.php/** * @param string $data * @param int$taskWorkerId * @param int$srcWorkerId * * @return bool */public function deliver(string $data, int $taskWorkerId = null, $srcWorkerId = null){if ($taskWorkerId === null) {$taskWorkerId = mt_rand($this->workerNum + 1, $this->workerNum + $this->taskNum);}if ($srcWorkerId === null) {$srcWorkerId = mt_rand(0, $this->workerNum - 1);}$this->check();$data= http://kandian.youth.cn/index/$this->pack($data, $srcWorkerId);$result = \msg_send($this->queueId, $taskWorkerId, $data, false);if (!$result) {return false;}return true;}对于Command进程的任务投递 , 情况会更复杂一点 。 上文提到的Process , 其往往衍生于Http/Rpc服务 , 作为同一个Manager的子孙进程 , 他们能够拿到Swoole\Server的句柄变量 , 从而通过$server->sendMessage(),$server->task()等方法进行任务投递 。
但在Swoft的体系中 , 还有一个十分路人的角色: Command 。 Command的进程从shell或cronb独立启动 , 和Http/Rpc服务相关的进程没有亲缘关系 。 因此Command进程以及从Command中启动的Process进程是没有办法拿到Swoole\Server的调用句柄直接通过UnixSocket进行任务投递的 。 为了为这种进程提供任务投递支持 , Swoft利用了Swoole的Task进程的一个特殊功能----消息队列
遥不可及|Swoft源码之Swoole和Swoft的分析同一个项目中Command和Http\RpcServer 通过约定一个message_queue_key获取到系统内核中的同一条消息队列 , 然后Comand进程就可以通过该消息队列向Task进程投递任务了 。 该机制没有提供对外的公开方法 , 仅仅被包含在Task::deliver()方法中 , Swoft会根据当前环境隐式切换投递方式 。 但该消息队列的实现依赖Semaphore拓展 , 如果你想使用 , 需要在编译PHP时加上--enable-sysvmsg参数 。
定时任务除了手动执行的普通任务 , Swoft还提供了精度为秒的定时任务功能用来在项目中替代Linux的Crontab功能.
Swoft用两个前置Process---任务计划进程:CronTimerProcess和任务执行进程CronExecProcess , 和两张内存数据表-----RunTimeTable(任务(配置)表)OriginTable((任务)执行表)用于定时任务的管理调度 。 两张表的每行记录的结构如下:
\\Swoft\Task\Crontab\TableCrontab.php/** * 任务表 , 记录用户配置的任务信息 * 表每行记录包含的字段如下,其中`rule`,`taskClass`,`taskMethod`生成key唯一确定一条记录 * @var array $originStruct*/private $originStruct = ['rule'=> [\Swoole\Table::TYPE_STRING, 100],//定时任务执行规则 , 对应@Scheduled注解的cron属性'taskClass'=> [\Swoole\Table::TYPE_STRING, 255],//任务名 对应@Task的name属性(默认为类名)'taskMethod' => [\Swoole\Table::TYPE_STRING, 255],//Task方法 , 对应@Scheduled注解所在方法'add_time'=> [\Swoole\Table::TYPE_STRING, 11],//初始化该表内容时的10位时间戳]; /** * 执行表 , 记录短时间内要执行的任务列表及其执行状态 * 表每行记录包含的字段如下,其中`taskClass`,`taskMethod`,`minute`,`sec`生成key唯一确定一条记录 * @var array $runTimeStruct*/private $runTimeStruct = ['taskClass'=> [\Swoole\Table::TYPE_STRING, 255],//同上'taskMethod' => [\Swoole\Table::TYPE_STRING, 255],//同上'minute'=> [\Swoole\Table::TYPE_STRING, 20],//需要执行任务的时间 , 精确到分钟 格式date('YmdHi')'sec'=> [\Swoole\Table::TYPE_STRING, 20],//需要执行任务的时间 , 精确到分钟 10位时间戳'runStatus'=> [\Swoole\TABLE::TYPE_INT, 4],//任务状态 , 有 0(未执行)1(已执行)2(执行中) 三种 。//注意:这里的执行是一个容易误解的地方 , 此处的执行并不是指任务本身的执行 , 而是值`任务投递`这一操作的执行 , 从宏观上看换成 _未投递_,_已投递_ , _投递中_描述会更准确 。];


推荐阅读