这几天学习框架,一方面要学会如何组装零件,也要会拆解零件,更加要学会改进零件。
问问自己:
(1) 框架的数据流是否梳理清楚
(2) 能否改进框架
(3) 如果框架有bug,能否将其修复
(4) 这个框架有什么优缺点,为什么要如此设计------需要相对其他的daemon程序而言
为什么要设计Interface Abstract Class
interface IWorkerEvent
interface IQueue extends \Stark\Daemon\IWorkerEvent
abstract class Base extends \Stark\Core\Options implements IQueue
class Callback extends Base
为什么要搞这么多层继承。就是为了代码复用吗?就是为了抽象吗?
Interface(所有函数都不能实例化)和abstract(不能创建实例,abstract函数必须在子类中实现)有什么区别??
信号处理是采用阻塞还是非阻塞
socket通信是如何实现的
(5) 如果重写这个框架,你是否能重写好这个框架。
前言
有些事情,出现得刚刚好。
刚好最近在研究框架之类的东西,先看了一个MVC框架类的简单东西。很久没有刷微博,偶尔刷微博的时候,突然发现了一个,跟项目的框架非常类似,但是更加简洁。所以更加便于学习框架设计核心思想。
这个框架主要是开多个子进程来异步处理任务,同时主进程能和子进程通信,了解子进程的status,并且能restart子进程,防止内存泄漏。管理者可以了解主进程的信息info,并且能shutdown主进程。管理者和主进程之间通过tcp通信。这个过程涉及主进程Master,子进程Worker,队列Queue,以及消费者Consumer。
感性认识
屁颠屁颠将代码 git clone下来,并且在机器上部署了ubantu的虚拟机。
(1)配置运行环境——linux环境,安装xmapp
(2)运行daemon程序——php src/Stark/run.php -f [ini_config_file]
php src/Stark/run.php -f tests/config_1.ini
(3)从queue中pop出data,然后输出这个data
第一步自己写了一个测试,使用queue->pop出data,consumer->run打印data。
(4)安装redis,配置phpredis
(5)在RedisQueue中写队列的相关扩展
-----RedisQueue使用redis来作为队列,进行数据的pop。
_redis = new \Redis(); $this->_redis->connect($this->_host, $this->_port); $this->_redis->rpush($this->_queueKey, "you are a beautiful girl!"); } public function pop(\Stark\Daemon\Worker $worker) { $data = $this->_redis->lpop($this->_queueKey); return $data; } public function push(\Stark\Daemon\Worker $worker, $data) { echo "push data - { $this->_queueKey} - { $data}\r\n"; return $this->_redis->rpush($this->_queueKey, $data); } public function complete(\Stark\Daemon\Worker $worker) { //关闭链接 $this->_redis->close(); }}
-----在配置文件中添加队列Queue的配置,配置文件config_5.ini如下:
[main]name = "config_5"host = "127.0.0.1"port = 9008working_dir = "/tmp"[run]script_file = "run_5.php"memory_limit = "1024M"[worker]count = 4max_run_count = 10000max_run_seconds = 3600max_idle_seconds = 60[queue]host = "127.0.0.1"port = "6380"queueKey = "test:redis"
-----重写运行脚本run_5.php,将从redisQueue中pop出的数据又重新推回队列,并且打印出来。当然你也可以将数据进行处理之后push到另一个redis队列中,代码如下:
queue->push($worker, $data); echo "Worker { $worker->index} current: { $g_count} data:{ $data}\r\n"; usleep(rand(0, 1000) * 1000);}
-----在run.php中get_options返回queue的相关参数,这样可以创建队列实例,并且实现pop函数。
array( 'class' => '\\Stark\\Daemon\\Queue\\RedisQueue', 'options' => array( 'host' => get_option_value($config, 'queue.host', '127.0.0.1'), 'port' => get_option_value($config, 'queue.port', 6379), 'queueKey' => get_option_value($config, 'queue.queueKey', ""), ), ), .... }
-----这样可以通过worker->queue->pop()从队列中获取到data,并且采用worker->consumer->run(data)来消耗数据。运行的结果如下:
(6)主进程和子进程如何通信的,子进程如何循环重启的?
代码中有自动重启的部分,如果运行超过一定时间,子进程会给主进程发送restart的命令,主进程会将子进程的worker从workerclients中去除。主进程在轮询的时候,遍历workerclients,发现worker不存在了,会重新再创建。
(7)管理进程与主进程如何保持通信
直接redis-cli –h 127.0.0.1 –p 9008就可以管理主进程,向主进程发送info命令了解主进程运行状态,发送shutdown关闭主进程的daemon程序。
(8)使用mysqlDB,能够访问db的资源
(9)为一个工程配置各种资源
(10)想想如何将工程逻辑单独提取出来,再单独建立一个App的文件夹,然后在App中写好run的相关逻辑,在配置指定好run函数所在脚本。
总体认识
如果大家看到代码就头大,那我希望自己试图能够通过更形象的例子来说明该框架的运行原理。
本框架主要涉及了Master,Worker,Consumer,Queue以及admin。希望通过一些类比能让大家明白本框架的基本逻辑。如下图所示:
Master主进程:企业
Worker子进程:工人
Consumer:车间机器
Queue:仓库
Data:原材料
admin:管理者
流水线作业:
- 企业(master主进程)雇佣(fork)n个工人(n个worker子进程)
- 工人(worker子进程)从仓库(queue)取得(pop)原材料(data)
- 然后使用(调用)机器(consumer)加工(处理)原材料(data)
- 从而得到(返回)产品(处理结果数据)
- 管理者(admin)可以获取企业运行状态(info),也可以关闭(shutdown)企业
- 企业(master)可以定时考核(sendHeartbeat )来了解(检查)工人(Workers)的工作绩效(status)
- 企业(master)可以解雇(quit)一些工人(workers),同时也可以雇佣一些工人(restart/create workers)
show me the code--入口文件run.php
本框架代码不多,其实可以细细读来,甚至重写。
run.php关键的三行代码,这三行代码按照配置文件中的配置启动了整个主进程。
$options = get_options($config_file);
$daemon = new \Stark\Daemon\Master($options);
$daemon->start();
1. getOptions解析配置文件,获取创建Consumer,Queue,Master实例的配置参数。
通过parse_ini函数解析配置文件config.ini得到config数组,然后通过如下代码获取Options:
Options = array( 'consumer' => array( 'class' => '\\Stark\\Daemon\\Consumer\\Callback', 'options' => array( 'init' => 'init', 'run' => 'run', 'complete' => 'complete', ), ), 'queue' => array( 'class' => '\\Stark\\Daemon\\Queue\\RedisQueue', 'options' => array( 'host' => get_option_value($config, 'queue.host', '127.0.0.1'), 'port' => get_option_value($config, 'queue.port', 6379), 'queueKey' => get_option_value($config, 'queue.queueKey', ""), ), ), 'master' => array( 'name' => get_option_value($config, 'main.name', 'Stark_' . time()), 'host' => get_option_value($config, 'main.host', '127.0.0.1'), 'port' => get_option_value($config, 'main.port', 9003), 'maxWorkerCount' => get_option_value($config, 'worker.count', 1), 'maxRunCount' => get_option_value($config, 'worker.max_run_count', 10000), 'maxRunSeconds' => get_option_value($config, 'worker.max_run_seconds', 3600), 'maxIdleSeconds' => get_option_value($config, 'worker.max_idle_seconds', 60), 'memoryLimit' => get_option_value($config, 'run.memory_limit', '1024M'), ),);
2. 创建Master实例
根据解析配置文件返回Options数组创建master的consumer,queue实例,并且初始化master的相应选项。如下图所示:
上述流程图实现功能主要体现以下三个方面:
- 创建master对象的_consumer成员变量(Consumer类型),同时初始化_consumer的_init,_run,_complete成员变量。
- 创建master实例的_queue对象成员(Queue类型),同时初始化_queue的_host,_port,_queuekey成员变量
- 初始化master实例的_name,_host,_port以及_maxWorkerCount,_maxRunCount,_maxRunSeconds,_maxIdelSeconds,_memoryLimit成员变量。
3. 启动主进程
主进程启动函数master->start(),主要负责以下功能:
- 检查运行环境(php版本)是否满足要求checkEnvironments;
- 并初始化工程目录initialize;
- 判断daemon程序是否一直在运行(若在运行,则终止);
- 在后台运行主进程runInBackground,并且设置其跑满所有的核setAffinity;
- 然后是创建主进程和子进程的本地通信socket连接(createDaemonSocket);
- 管理进程和主进程的网络通信socket连接(createAdminSocket);
- 创建多个workers,并且启动多个worker;
- 主进程startLoop轮询去检查多个worker的状态,接受管理进程的请求(info,shutdown),接受子进程的请求(restart,status)。
以下是master->start()函数运行的流程图如下:
startWorkers创建多个wokers子进程,并且启动各个子进程,获取子进程的相应状态信息,同时主进程(startLoop)轮询监听管理进程发来的命令以及向子进程定时发送心跳检查子进程的健康状态。具体过程参见下面流程图:
(1) 启动子进程
轮询消耗队列,对队列数据进行加工处理,同时当子进程生命周期结束时向主进程发出restart的请求,同时也会接收主进程的status命令请求,返回状态信息。
public function start() { $this->_initialize(); $this->_startLoop(); $this->_finalize(); exit; }
Worker的循环,不断的消耗队列。DoQueue由消费者去消耗queue->pop出的data。ReceiveCommands连接主进程,接收主进程的命令,并返回相应的结果。
private function _startLoop() { while ($this->_run) { $this->_queueStartTime = microtime(true); $this->_checkStatus(); // 检查相关的状态 $this->_receiveCommands(); // 接收主进程的相关命令(比如status命令) if ($this->_pause) { usleep(1000); continue; } if ($this->_doQueue() === false) { usleep(1000); } } }
(2)主进程Master和管理进程Adim的TCP通信
注意Master是主进程,Worker是子进程,Master和Worker之间是主进程和子进程间的本地通信,而Admin和Master之间是两台机器之间的网络TCP通信。
管理进程和主进程如何通信的?具体步骤参见以下流程图和步骤说明:
结合上图,管理进程和主进程主要通过以下几步完成通信:
a)已经通过createAdminSocket函数建立了主进程和admin之间的socket连接
b)然后通过acceptConnection监听这个socket,获取到与主进程连接的adminClients信息
c)接着从adminClient那读取admin的请求值responseValue, 解析responseValue得到请求命令command和相应参数arguments
d)根据admin的请求命令command得到命令处理函数{$command}AdminHandler
e)传入admin请求参数arguments给命令处理函数{$command}AdminHandler,调用并执行该命令处理函数
f)responseToManager将处理结果返回给adminClients
为了更好的理解进程通信,根据数据流打印了日志,通过日志流更好的理解进程通信。
本框架主要实现了通过redis-cli连接daemon配置的host和port,发送info命令可以了解daemon的运行状态,发送shutdown命令可以关闭daemon。
主要步骤如下:
1)admin给主进程发送info命令,将会返回daemon的运行状态,如上截图所示。
2) 主进程checkAdminCommands接收命令info,并且responseCommand,解析命令info,调用infoAdminHandler
3) infoAdminHandler调用getStatus函数通过workerStatus数组来获得状态信息,并将状态信息sendToManager
4)workerStatus数组是在主进程检查子进程心跳时,请求子进程返回状态信息,从而对workerStatus数组进行更新的。
主进程和管理进程之间的通信-------info----主进程master接收管理进程admin的info命令Thu, 20 Aug 2015 06:32:29 -0700 | Master.php | checkAdminCommands | get response value from client - 1 - ["info"] - ^MThu, 20 Aug 2015 06:32:29 -0700 | Master.php | responseCommand | parse reponse command - ["info"]^MThu, 20 Aug 2015 06:32:29 -0700 | Master.php | responseCommand | info - _infoAdminHandler - []^MThu, 20 Aug 2015 06:32:29 -0700 | Master.php | infoAdminHandler: :getStatus | get worker statuses - [{ "pid":4369,"startTime":1440077506.2846,"lastActiveTime":1440077546.0741,"totalCount":84,"totalTime":40.72473192215,"totalQPS":2.0626286788229,"totalCpuU":0,"totalCpuS":0,"memory":1048576,"ticks":1720237390,"cpuU":2,"cpuS":3},{ "pid":4370,"startTime":1440077506.2854,"lastActiveTime":1440077546.2101,"totalCount":85,"totalTime":40.803544521332,"totalQPS":2.083152358383,"totalCpuU":0,"totalCpuS":0,"memory":1048576,"ticks":1720237399,"cpuU":3,"cpuS":2},{ "pid":4372,"startTime":1440077506.286,"lastActiveTime":1440077545.8301,"totalCount":78,"totalTime":40.104762077332,"totalQPS":1.9449061896838,"totalCpuU":0,"totalCpuS":0,"memory":1048576,"ticks":1720237328,"cpuU":1,"cpuS":4},{ "pid":4374,"startTime":1440077506.2865,"lastActiveTime":1440077546.1811,"totalCount":82,"totalTime":40.579809188843,"totalQPS":2.0207093537183,"totalCpuU":0,"totalCpuS":0,"memory":1048576,"ticks":1720237376,"cpuU":1,"cpuS":4}]^MThu, 20 Aug 2015 06:32:29 -0700 | Master.php | infoAdminHandler | get status - "host:127.0.0.1\r\nport:9008\r\ndaemon_name:config_5\r\nstart_time:2015-08-20 06:31:46\r\nmax_worker:4\r\ntotal_count:329\r\ntotal_time:162.21284770966\r\ntotal_qps:2.0281993975525\r\ntotal_memory:4194304\r\ntotal_cpu_u:7\r\ntotal_cpu_s:13\r\ntotal_worker_client:4\r\ntotal_manage_client:2\r\n"^MThu, 20 Aug 2015 06:32:29 -0700 | Master.php | sendResponseToManager - send multi bulk to admin client - Resource id #119 - "host:127.0.0.1\r\nport:9008\r\ndaemon_name:config_5\r\nstart_time:2015-08-20 06:31:46\r\nmax_worker:4\r\ntotal_count:329\r\ntotal_time:162.21284770966\r\ntotal_qps:2.0281993975525\r\ntotal_memory:4194304\r\ntotal_cpu_u:7\r\ntotal_cpu_s:13\r\ntotal_worker_client:4\r\ntotal_manage_client:2\r\n"^M
如何平滑关闭掉daemon程序,主要通过以下几个步骤完成:
(1)主进程checkAdminCommands来接收到shutdown的命令
(2)主进程responseCommand来解析shutdown命令,并调用shutdownAdminHandler
(3)主进程向admin进程返回ok。同时调用_quit函数。
(4)主进程的_quit函数向各个子进程sendQuitCommandToWorker。主进程获取子进程quit命令处理的返回结果。返回结果是statusCommandHandle的处理结果。调用statusWorkerHandler,更新workerStatus数组。
(5)主进程的_quit函数关闭主子进程通信的daemon socket, 关闭主进程和admin进程通信的admin socket。并且exit退出主进程程序
(6)子进程接收到quit命令处理相关结果,但是连接不上主进程,就会跳出循环(设置run为false),关闭socket终止子进程程序。
主进程和管理进程之间的通信------shutdown----主进程master接收管理进程admin的shutdown命令Sun, 23 Aug 2015 04:33:14 -0700|Master.php | checkAdminCommands | get response value from client - 0 - ["SHUTDOWN"] - ^MSun, 23 Aug 2015 04:33:14 -0700|Master.php | responseCommand | parse reponse command - ["SHUTDOWN"]^MSun, 23 Aug 2015 04:33:14 -0700|Master.php | responseCommand - SHUTDOWN - _SHUTDOWNAdminHandler - []^MSun, 23 Aug 2015 04:33:14 -0700|Master.php | _shutdownAdminHandler | Received command: shutdown^MSun, 23 Aug 2015 04:33:14 -0700|Master.php | sendResponseToManager - Resource id #82 - "OK"^MSun, 23 Aug 2015 04:33:14 -0700||Master.php | _quit | STEP 1 |Ending daemon to quit all workers : config_5//******主进程和子进程之间的通信-----quit------主进程master向子进程workers发送quit命令Thu, 20 Aug 2015 06:32:30 -0700|Master.php | sendCommandToWorker | write commnad to worker - quit - Resource id #33^M// 此处是(B)子进程接收quit命令,并将结果返回。即返回worker的status处理结果private function _restart($reason = false) { if ($reason != false) { $this->log->log("No.{$this->index} worker restart: {$reason}"); } // $this->_sendResponse('restart', $reason); 不需要这个 $this->_statusCommandHandle();//TODO:汇报失败时从文件恢复状态 $this->_quit(); }// 返回status处理结果,就调用statusWorkerHandlerSun, 23 Aug 2015 04:33:14 -0700|Master.php | sendCommandToWorker | write commnad to worker - quit - Resource id #299^MSun, 23 Aug 2015 04:33:14 -0700|Master.php | sendCommandToWorker | read response from worker - quit - Resource id #299 - ["status","0","7893","0","{\"lastActiveTime\":1440329593.3279,\"totalCount\":6,\"totalTime\":4.3658437728882,\"totalQPS\":1.3743047878305,\"memory\":1048576,\"ticks\":1720834680,\"cpuU\":0,\"cpuS\":0}"]^MSun, 23 Aug 2015 04:33:14 -0700|Master.php | sendCommandToWorker | response Command - quit - Resource id #299 - ["status","0","7893","0","{\"lastActiveTime\":1440329593.3279,\"totalCount\":6,\"totalTime\":4.3658437728882,\"totalQPS\":1.3743047878305,\"memory\":1048576,\"ticks\":1720834680,\"cpuU\":0,\"cpuS\":0}"]^MSun, 23 Aug 2015 04:33:14 -0700|Master.php | responseCommand | parse reponse command - ["status","0","7893","0","{\"lastActiveTime\":1440329593.3279,\"totalCount\":6,\"totalTime\":4.3658437728882,\"totalQPS\":1.3743047878305,\"memory\":1048576,\"ticks\":1720834680,\"cpuU\":0,\"cpuS\":0}"]^MSun, 23 Aug 2015 04:33:14 -0700|Master.php | responseCommand - status - _statusWorkerHandler - ["0","7893","0","{\"lastActiveTime\":1440329593.3279,\"totalCount\":6,\"totalTime\":4.3658437728882,\"totalQPS\":1.3743047878305,\"memory\":1048576,\"ticks\":1720834680,\"cpuU\":0,\"cpuS\":0}"]^MSun, 23 Aug 2015 04:33:14 -0700|Master.php | statusWorkerHandler | update worker status from arguments - Resource id #299 - ["0","7893","0","{\"lastActiveTime\":1440329593.3279,\"totalCount\":6,\"totalTime\":4.3658437728882,\"totalQPS\":1.3743047878305,\"memory\":1048576,\"ticks\":1720834680,\"cpuU\":0,\"cpuS\":0}"]^M// 此时quit接收的status不是quit返回的status,因为此时的daemon已经close了。----因为第一次返回的是false,后续处理的结构都在后面。----这是一个问题Sun, 23 Aug 2015 04:33:14 -0700|Master.php | _quit | STEP 2 | close the daemon socket^MSun, 23 Aug 2015 04:33:14 -0700|Master.php | _quit | close the admin socket^MSun, 23 Aug 2015 04:33:14 -0700|Worker.php | startLoop | Worker 2 | check worker status^MSun, 23 Aug 2015 04:33:14 -0700|Worker.php | checkStatus | Worker 2 | check the worker health^MSun, 23 Aug 2015 04:33:14 -0700|Worker.php | startLoop | Worker 2 | receive commands^MSun, 23 Aug 2015 04:33:14 -0700|Worker.php | receiveCommands | Worker 2 | read commandValue - ["quit"]^MSun, 23 Aug 2015 04:33:14 -0700|Worker.php | receiveCommands | Worker 2 | response commandValue - ["quit"]^MSun, 23 Aug 2015 04:33:14 -0700|Worker.php | responseCommand | Worker 2 - parse command value - _quitCommandHandle - []^MSun, 23 Aug 2015 04:33:14 -0700|Worker.php | responseCommand | Worker 2 - call command handler function - _quitCommandHandle - []^MSun, 23 Aug 2015 04:33:14 -0700|No.2 worker restart: Worker 2 received command: quit^MSun, 23 Aug 2015 04:33:14 -0700|Worker.php | _restart | Worker 2 | send response of status for reason:Worker 2 received command: quit^MSun, 23 Aug 2015 04:33:14 -0700|Worker.php | statusCommandHandle | Worker 2 | - []^MSun, 23 Aug 2015 04:33:14 -0700|Worker.php | statusCommandHandle | Worker 2 | get status - { "lastActiveTime":1440329594.496,"totalCount":11,"totalTime":5.3853900432587,"totalQPS":2.0425632891288,"memory":1048576,"ticks":1720834782,"cpuU":0,"cpuS":0}^MSun, 23 Aug 2015 04:33:14 -0700|Worker.php | sendResponse | Worker 2 | status - "{\"lastActiveTime\":1440329594.496,\"totalCount\":11,\"totalTime\":5.3853900432587,\"totalQPS\":2.0425632891288,\"memory\":1048576,\"ticks\":1720834782,\"cpuU\":0,\"cpuS\":0}"^MSun, 23 Aug 2015 04:33:14 -0700|Worker.php | sendResponse | Worker 2 | send multi bulk to daemon socket - Resource id #302 - ["status",2,7896,0,"{\"lastActiveTime\":1440329594.496,\"totalCount\":11,\"totalTime\":5.3853900432587,\"totalQPS\":2.0425632891288,\"memory\":1048576,\"ticks\":1720834782,\"cpuU\":0,\"cpuS\":0}"]^MSun, 23 Aug 2015 04:33:14 -0700|Worker 2 socket error, reconnecting^MSun, 23 Aug 2015 04:33:14 -0700|Unable to connect daemon socket^MSun, 23 Aug 2015 04:33:14 -0700|Worker.php | _finalize | Worker 2 | close the socket^MSun, 23 Aug 2015 04:33:14 -0700|Worker 2 is completed^MUnable to connect daemon socket^M
(3) 主进程Master和子进程Worker的本地通信
主进程和子进程通信主要是为了了解子进程的工作状态(status)信息,并隔一段时间重启(restart)子进程以防止内存泄漏。那这个过程具体是怎么实现的呢
首先,sendHeartbeatToWorkers定时发送心跳检查worker的健康状态,sendCommandToWorker($workerClient,'status')即主进程定时向子进程发送status命令,子进程响应主进程的请求,读取status命令
然后,子进程getStatus统计当前worker运行状态信息(当前qps,cpu、内存等使用情况,程序运行时间等)。
最后,sendResponse子进程将状态信息返回给主进程。
具体过程参见下图:
上述流程的详细代码如下:
// 给子进程发送心跳,检查其状态,如果无法获取到子进程的状态,说明该子进程已经死掉,所以要从workerClients列表中清除它。 private function _sendHeartbeatToWorkers() { foreach ($this->_workerClients as $key => $clientInfo) { $client = $clientInfo['client']; if ($this->_sendCommandToWorker($client, 'status') === false) { unset($this->_workerClients[$key]); continue; } } } private function _sendCommandToWorker($client, $command, $arguments = array()) { $write = Protocol::write($client, $command, $arguments); // 往子进程写命令请求 if ($write === false) return false; $responseValue = Protocol::read($client, false); // 读取子进程返回的结果 if (empty($responseValue) === false) { $this->_responseCommand($client, $responseValue, self::COMMAND_FROM_WORKER); // 解析子进程的返回的结果 } return true; }
对于主进程发送给子进程的status命令,子进程给主进程发送的restart命令的通信过程如下流程图所示:
注意:
status是子进程处理命令请求,返回结果。但是这个status命令请求是主进程发出来的。
后续的restart命令是子进程传给主进程,主进程处理restart命令请求,将该子进程杀掉,之后在检查该子进程不存在的时候,重新创建。
为了获取daemon的运行状态,主进程会定期给子进程发送心跳,检查子进程的运行状态(cpu,mem,consumer_count,qps等)。
子进程接收主进程发来的status命令,获取当前子进程的各种状态参数返回给主进程。
******主进程和子进程之间的通信---sendStatusComandToWorkers----master给所有的workers发送status commandThu, 20 Aug 2015 06:32:30 -0700|Master.php | sendHeartbeatToWorkers - worker index 0 - ^MThu, 20 Aug 2015 06:32:30 -0700|Master.php | sendHeartbeatToWorkers - send status command to worker index 0 - Resource id #33^MThu, 20 Aug 2015 06:32:30 -0700|Master.php | sendCommandToWorker | write commnad to worker - status - Resource id #33^M// ----此处就是worker接收master的status----(A),并将处理结果返回给masterThu, 20 Aug 2015 06:32:30 -0700|Master.php | sendCommandToWorker | read response from worker - Resource id #33 - ["status","0","4369","0","{\"lastActiveTime\":1440077547.7961,\"totalCount\":86,\"totalTime\":42.103279829025,\"totalQPS\":2.042596214576,\"memory\":1048576,\"ticks\":1720237528,\"cpuU\":2,\"cpuS\":3}"]^MThu, 20 Aug 2015 06:32:30 -0700|Master.php | sendCommandToWorker | response Command - Resource id #33 - ["status","0","4369","0","{\"lastActiveTime\":1440077547.7961,\"totalCount\":86,\"totalTime\":42.103279829025,\"totalQPS\":2.042596214576,\"memory\":1048576,\"ticks\":1720237528,\"cpuU\":2,\"cpuS\":3}"]^MThu, 20 Aug 2015 06:32:30 -0700|Master.php | responseCommand | parse reponse command - ["status","0","4369","0","{\"lastActiveTime\":1440077547.7961,\"totalCount\":86,\"totalTime\":42.103279829025,\"totalQPS\":2.042596214576,\"memory\":1048576,\"ticks\":1720237528,\"cpuU\":2,\"cpuS\":3}"]^MThu, 20 Aug 2015 06:32:30 -0700|Master.php | responseCommand - status - _statusWorkerHandler - ["0","4369","0","{\"lastActiveTime\":1440077547.7961,\"totalCount\":86,\"totalTime\":42.103279829025,\"totalQPS\":2.042596214576,\"memory\":1048576,\"ticks\":1720237528,\"cpuU\":2,\"cpuS\":3}"]^MThu, 20 Aug 2015 06:32:30 -0700|Master.php | statusWorkerHandler | update worker status from arguments - Resource id #33 - ["0","4369","0","{\"lastActiveTime\":1440077547.7961,\"totalCount\":86,\"totalTime\":42.103279829025,\"totalQPS\":2.042596214576,\"memory\":1048576,\"ticks\":1720237528,\"cpuU\":2,\"cpuS\":3}"]^MThu, 20 Aug 2015 06:32:30 -0700|Worker.php | startLoop | check worker status^MThu, 20 Aug 2015 06:32:30 -0700|Worker.php | checkStatus | check the worker health^M(A)子进程worker接收主进程master的status的命令,并将处理结果返回给主进程Thu, 20 Aug 2015 06:32:30 -0700|Worker.php | startLoop | receive commands^MThu, 20 Aug 2015 06:32:30 -0700|Worker.php | receiveCommands | read commandValue - ["status"]^MThu, 20 Aug 2015 06:32:30 -0700|Worker.php | receiveCommands | response commandValue - ["status"]^MThu, 20 Aug 2015 06:32:30 -0700|Worker.php | responseCommand | parse command value - _statusCommandHandle - []^MThu, 20 Aug 2015 06:32:30 -0700|Worker.php | responseCommand | call command handler function - _statusCommandHandle - []^MThu, 20 Aug 2015 06:32:30 -0700|Worker.php | statusCommandHandle | []^MThu, 20 Aug 2015 06:32:30 -0700|Worker.php | statusCommandHandle | get status to send response - { "lastActiveTime":1440077549.9742,"totalCount":94,"totalTime":43.978601455688,"totalQPS":2.1374031208044,"memory":1048576,"ticks":1720237716,"cpuU":4,"cpuS":2}^MThu, 20 Aug 2015 06:32:30 -0700|Worker.php | sendResponse | send status command response - "{\"lastActiveTime\":1440077549.9742,\"totalCount\":94,\"totalTime\":43.978601455688,\"totalQPS\":2.1374031208044,\"memory\":1048576,\"ticks\":1720237716,\"cpuU\":4,\"cpuS\":2}"^MThu, 20 Aug 2015 06:32:30 -0700|Worker.php | sendResponse - send multi bulk to daemon socket - Resource id #36 - ["status",1,4370,0,"{\"lastActiveTime\":1440077549.9742,\"totalCount\":94,\"totalTime\":43.978601455688,\"totalQPS\":2.1374031208044,\"memory\":1048576,\"ticks\":1720237716,\"cpuU\":4,\"cpuS\":2}"]
在shutdown命令的时候,主进程会定时给子进程发送quit命令,子进程接收主进程的quit命令,并将处理结果返回给主进程。
******主进程和子进程之间的通信---sendQuitComandToWorkers----master给所有的workers发送quit command 在主进程接收Admin进程的shutdown命令的时候,会调用_quit函数,向子进程sendQuitCommandToWorkers
(B)子进程worker接收主进程master的quit的命令,并将处理结果返回给主进程Sun, 23 Aug 2015 04:13:41 -0700|Worker.php | startLoop | Worker 0 | check worker status^MSun, 23 Aug 2015 04:13:41 -0700|Worker.php | checkStatus | Worker 0 | check the worker health^MSun, 23 Aug 2015 04:13:41 -0700|Worker.php | checkStatus | Worker 0 | the worker is not so as health to restart - Run time limit reached^MSun, 23 Aug 2015 04:13:41 -0700|No.0 worker restart: Run time limit reached^MSun, 23 Aug 2015 04:13:41 -0700|Worker.php | _restart | Worker 0 | send response of status for reason:Run time limit reached^MSun, 23 Aug 2015 04:13:41 -0700|Worker.php | statusCommandHandle | Worker 0 | - []^MSun, 23 Aug 2015 04:13:41 -0700|Worker.php | statusCommandHandle | Worker 0 | get status - { "lastActiveTime":1440328421.3354,"totalCount":19,"totalTime":10.315975666046,"totalQPS":1.8418034915046,"memory":1048576,"ticks":1720717490,"cpuU":1,"cpuS":0}^MSun, 23 Aug 2015 04:13:41 -0700|Worker.php | sendResponse | Worker 0 | status - "{\"lastActiveTime\":1440328421.3354,\"totalCount\":19,\"totalTime\":10.315975666046,\"totalQPS\":1.8418034915046,\"memory\":1048576,\"ticks\":1720717490,\"cpuU\":1,\"cpuS\":0}"^MSun, 23 Aug 2015 04:13:41 -0700|Worker.php | sendResponse | Worker 0 | send multi bulk to daemon socket - Resource id #36 - ["status",0,7460,0,"{\"lastActiveTime\":1440328421.3354,\"totalCount\":19,\"totalTime\":10.315975666046,\"totalQPS\":1.8418034915046,\"memory\":1048576,\"ticks\":1720717490,\"cpuU\":1,\"cpuS\":0}"]^MSun, 23 Aug 2015 04:13:41 -0700|Worker.php | _restart | Worker 0 | to set the run false:Run time limit reached^MSun, 23 Aug 2015 04:13:41 -0700|Worker.php | startLoop | Worker 0 | receive commands^MSun, 23 Aug 2015 04:13:41 -0700|Worker.php | receiveCommands | Worker 0 | read commandValue - ["status"]^MSun, 23 Aug 2015 04:13:41 -0700|Worker.php | receiveCommands | Worker 0 | response commandValue - ["status"]^MSun, 23 Aug 2015 04:13:41 -0700|Worker.php | responseCommand | Worker 0 - parse command value - _statusCommandHandle - []^MSun, 23 Aug 2015 04:13:41 -0700|Worker.php | responseCommand | Worker 0 - call command handler function - _statusCommandHandle - []^MSun, 23 Aug 2015 04:13:41 -0700|Worker.php | statusCommandHandle | Worker 0 | - []^MSun, 23 Aug 2015 04:13:41 -0700|Worker.php | statusCommandHandle | Worker 0 | get status - { "lastActiveTime":1440328421.3354,"totalCount":19,"totalTime":10.315975666046,"totalQPS":1.8418034915046,"memory":1048576,"ticks":1720717490,"cpuU":1,"cpuS":0}^MSun, 23 Aug 2015 04:13:41 -0700|Worker.php | sendResponse | Worker 0 | status - "{\"lastActiveTime\":1440328421.3354,\"totalCount\":19,\"totalTime\":10.315975666046,\"totalQPS\":1.8418034915046,\"memory\":1048576,\"ticks\":1720717490,\"cpuU\":1,\"cpuS\":0}"^MSun, 23 Aug 2015 04:13:41 -0700|Worker.php | sendResponse | Worker 0 | send multi bulk to daemon socket - Resource id #36 - ["status",0,7460,0,"{\"lastActiveTime\":1440328421.3354,\"totalCount\":19,\"totalTime\":10.315975666046,\"totalQPS\":1.8418034915046,\"memory\":1048576,\"ticks\":1720717490,\"cpuU\":1,\"cpuS\":0}"]^MSun, 23 Aug 2015 04:13:42 -0700|Worker.php | startLoop | Worker 0 | stop the loop^MSun, 23 Aug 2015 04:13:42 -0700|Worker.php | _finalize | Worker 0 | close the socket^MSun, 23 Aug 2015 04:13:42 -0700|Worker 0 is completed^M
4. 本框架逻辑流程图
以下是项目作者绘制的框架程序流程图。可以做参考,这个流程图描述了框架的所有过程。