猫耳山在天边 2019-06-27
本文主要讲解EasySwoole 服务的启动过程,会通过源码片段讲解主体的设计流程
当我们通过php easyswoole start
启动EasySwoole 服务时,命令真正到达的文件是 easyswoole项目\vendor\easyswoole\easyswoole\bin\easyswoole
,命令start执行的整体流程如下图:
主要方法为:serverStart($options);
,其重要执行代码如下:
$conf = Conf::getInstance(); $inst = Core::getInstance()->initialize(); $inst->run();
Config.php
内容如下:return [ 'SERVER_NAME'=>"EasySwoole", 'MAIN_SERVER'=>[ 'HOST'=>'0.0.0.0', 'PORT'=>9501, 'SERVER_TYPE'=>\EasySwoole\Core\Swoole\ServerManager::TYPE_WEB_SERVER, 'SOCK_TYPE'=>SWOOLE_TCP,//该配置项当为SERVER_TYPE值为TYPE_SERVER时有效 'RUN_MODEL'=>SWOOLE_PROCESS, 'SETTING'=>[ 'task_worker_num' => 8, //异步任务进程 'task_max_request'=>10, 'max_request'=>5000,//强烈建议设置此配置项 'worker_num'=>8 ], ], 'DEBUG'=>true, 'TEMP_DIR'=>null,//若不配置,则默认框架初始化 'LOG_DIR'=>null,//若不配置,则默认框架初始化 'EASY_CACHE'=>[ 'PROCESS_NUM'=>1,//若不希望开启,则设置为0 'PERSISTENT_TIME'=>0//如果需要定时数据落地,请设置对应的时间周期,单位为秒 ], 'CLUSTER'=>[ 'enable'=>false, 'token'=>null, 'broadcastAddress'=>['255.255.255.255:9556'], 'listenAddress'=>'0.0.0.0', 'listenPort'=>'9556', 'broadcastTTL'=>5, 'nodeTimeout'=>10, 'nodeName'=>'easySwoole', 'nodeId'=>null ] ];
initialize()
方法run()
方法ps:此处插入说明一个点,EasySwoole中单例类都复用同一个trait
trait Singleton { private static $instance; static function getInstance(...$args) { if(!isset(self::$instance)){ self::$instance = new static(...$args); } return self::$instance; } }
Easywechat 真实入口文件为EasySwoole\Core\Core
,上述已经说到命令启动时,执行了以下代码:
Core::getInstance(); $inst = Core::getInstance()->initialize(); $inst->run();
在整个EasySwoole生命周期中,Core对象只会被实例化一次,Code的初始化做了如下操作:
public function __construct() { defined('SWOOLE_VERSION') or define('SWOOLE_VERSION',intval(phpversion('swoole'))); defined('EASYSWOOLE_ROOT') or define('EASYSWOOLE_ROOT',realpath(getcwd())); if(file_exists(EASYSWOOLE_ROOT.'/EasySwooleEvent.php')){ require_once EASYSWOOLE_ROOT.'/EasySwooleEvent.php'; //引入全局初始化事件类 } $this->sysDirectoryInit(); //设置temp目录和log目录,路径可配置化 }
SWOOLE_VERSION
和 EASYSWOOLE_ROOT
EasySwooleEvent.php
$this->sysDirectoryInit();
设置temp目录和log目录,路径可配置化Core类中的initialize
方法:
public function initialize():Core { Di::getInstance()->set(SysConst::VERSION,'2.1.2'); Di::getInstance()->set(SysConst::HTTP_CONTROLLER_MAX_DEPTH,3); EasySwooleEvent::frameInitialize(); $this->errorHandle(); return $this; }
SysConst::VERSION
和 SysConst::HTTP_CONTROLLER_MAX_DEPTH
的值EasySwooleEvent::frameInitialize();
事件$this->errorHandle();
注册系统中的set_error_handler、register_shutdown_functionCore类的run方法为核心功能:
public function run():void { ServerManager::getInstance()->start(); }
ServerManager
类,并执行start()
启动整个服务ServerManager
是一个单例对象,在整个EasySwoole生命周期中,ServeManager
对象只会被实例化一次.ServeManager
的 run
方法干了下面几件事:
public function start():void { $this->createMainServer(); Cache::getInstance(); Cluster::getInstance()->run(); CronTab::getInstance()->run(); $this->attachListener(); $this->isStart = true; $this->getServer()->start(); }
createMainServer()
创建主服务cache
,添加对应的CacheProcess
。(Easyswoole的缓存服务是基于swoole_process
的管道通信,后续会专门解析下系统组件cache
的源码)Cluster
集群模式的注册 (有兴趣的可以通过链接看看)CronTab
服务开启,后面说下crontab的使用attachListener
事件监听,子服务多端口监听$this->getServer()->start();
调用swoole_server
的start方法,正式启动Easyswoole
服务ServerManager
类的createMainServer()
方法:
(1)读取配置,创建对应的swoole_server服务
case self::TYPE_SERVER:{ $this->mainServer = new \swoole_server($host,$port,$runModel,$sockType); break; } case self::TYPE_WEB_SERVER:{ $this->mainServer = new \swoole_http_server($host,$port,$runModel,$sockType); break; } case self::TYPE_WEB_SOCKET_SERVER:{ $this->mainServer = new \swoole_websocket_server($host,$port,$runModel,$sockType); break; } default:{ Trigger::throwable(new \Exception("unknown server type :{$conf['SERVER_TYPE']}")); } }
(2)注册事件,onWorker、onTask、onFinish、onRequest
等;还有Easyswoole
事件mainServerCreate
,开发者可以在mainServerCreate
事件设置CronTab
服务等
$register = new EventRegister();//事件容器 $this->finalHook($register); EasySwooleEvent::mainServerCreate($this,$register); $events = $register->all();
(3)事件注册的过程中,还做了如下操作:实例化对象池。开发者可以通过配置,在此实例化mysql连接池等:
//实例化对象池管理 PoolManager::getInstance(); PoolManager::getInstance()->__workerStartHook($workerId);
(4)在onRequest
事件中,还执行了EasySwooleEvent
的onRequest
和afterAction
,开发者可以在此自定义处理代码,如日志统一刷出等
EasySwooleEvent::onRequest($request_psr,$response_psr); $dispatcher->dispatch($request_psr,$response_psr); EasySwooleEvent::afterAction($request_psr,$response_psr);
ServerManager
中 Cache::getInstance()
全局跨进程Cache的注册:
function __construct() { $num = intval(Config::getInstance()->getConf("EASY_CACHE.PROCESS_NUM")); if($num <= 0){ return; } $this->cliTemp = new SplArray(); //若是在主服务创建,而非单元测试调用 if(ServerManager::getInstance()->getServer()){ //创建table用于数据传递 TableManager::getInstance()->add(self::EXCHANGE_TABLE_NAME,[ 'data'=>[ 'type'=>Table::TYPE_STRING, 'size'=>10*1024 ], 'microTime'=>[ 'type'=>Table::TYPE_STRING, 'size'=>15 ] ],2048); $this->processNum = $num; for ($i=0;$i < $num;$i++){ ProcessManager::getInstance()->addProcess($this->generateProcessName($i),CacheProcess::class); } } }
cache
服务基于 swoole_table
实现全局数据共享和传递。
以上是Easyswoole
服务启动过程中的主体设计,其中包括了各种组件的实例化,如PoolManager(对象池)、cache、CronTab
等。