做者:bromine
连接:https://www.jianshu.com/p/4c0...
來源:简书
著做权归做者全部,本文已得到做者受权转载,并对原文进行了从新的排版。
Swoft Github: https://github.com/swoft-clou...php
Swoft源码剖析系列目录: https://segmentfault.com/a/11...
Swoft
在PHPer
圈中是一个门槛较高的Web
框架,不只仅因为框架自己带来了不少新概念和前沿的设计,还在于Swoft
是一个基于Swoole
的框架。Swoole
在PHPer
圈内学习成本最高的工具没有之一,虽然Swoft
的出现下降了Swoole
的使用成本,但若是你对Swoole
自己了解不够深刻,仍然很难避免栽进种种"坑"中。git
考虑到这个现状,也为下降阅读难度,后续几个和Swoole
联系较为密切的机制,笔者会调整写做思路,将文章的定位从 「帮助读者深刻理解Swoft」 调整为 「帮助读者理解Swoft和Swoole」,叙述节奏也会放慢。github
LNMP
和LAMP
是绝大多数PHPer
最熟悉的基础Web架构,这里以常见的LNMP
做为例子描述一个常见 无Swoole
应用的构件组成:Nginx
充当Web Service
, PHP-FPM
维护一个进程池去运行Web
项目。数据库
对比更古老的CGI
模型,PHP-FPM
已经引入了进程常驻的概念,避免每次请求建立并销毁进程的开销以及拓展加载的开销,可是每一个请求仍然要执行PHP RINIT
与 RSHUTDOWN
之间的全部流程,包括从新加载一次框架源码以及项目代码,形成极大的性能浪费。编程
这种模型的优势是简单成熟和稳定,一次运行随后销毁 带来的开发便捷性是PHP
可以流行起来的缘由之一。市面上绝大多数PHP
项目使用的都是基于该种架构的变体。bootstrap
LNMP-with-Swoole
是 LNMP
的一种变体,其在LNMP
的基础上引入了Swoole
组件。
和PHP-FPM
同样,Swoole
有一套本身的进程管理机制。但因为代码变得高度常驻和编程思惟须要从同步到异步的转变,因此Swoole
和传统的基于PHP-FPM
的Web
框架亲和度很低,即便是适配升级过的老式Web
框架,目前在Swoole
上运行的表现每每并很差。segmentfault
所以出现了这在这种折中方案,并无直接将原有PHP
代码运行在Swoole
中,而是使用Swoole
搭建了一个服务,系统经过接口与Swoole
通讯,从而为Web
项目补充了异步处理的能力。我称呼这种同时使用PHP-FPM
和Swoole
的系统为 半Swoole
应用。由于接入简单,因此是绝大多数现有项目优先考虑的Swoole接入方案。服务器
LNMP-with-Swoole模型虽然引入了Swoole
和异步处理能力,可是核心仍是PHP-FPM
,实际上还远远没有发挥出Swoole
的真正优点。swoole
Swoole-HTTP-Server
和LNMP-with-Swoole
相比有巨大的变化,这种模型中充当Web Server
角色的构件不只仅有Nginx
,应用自己也包含了一个内建Web Server
,不过因为Swoole Http Server
不是专业的HTTP Server
,对Http
的处理不完善 ,所以仍然须要使用Nginx做为静态资源服务器以及反代,Swoole HTTP Server
仅仅处理PHP
相关的HTTP
流量。架构
一方面因为Swoole
已经包含了WebServer
,再也不须要实现CGI
或者Fast-CGI
的通用协议去和Web Server
通讯,另外一方面Swoole
有本身的进程管理,所以PHP-FPM
能够直接被去除了。对于PHP
资源而言,在这种模型中,Swoole Http Server
的地位至关于传统模型中的Nginx
和PHP-FPM
之和。
一次加载常驻内存,不一样的请求间基本上复用了onRequest之外的全部流程,使得每一个请求的开销大大下降;异步IO
的特性使得这种模型吞吐量远远高于传统的LNMP模型
。另外相对于独立的Swoole
服务,内嵌在Web
系统中的Swoole
使用更加的直接方便,支持更好。
Swoole
是一个异步引擎,核心是为PHP
提供异步IO
执行的能力,同时提供一套异步编程可能会用到的工具集。Swoole-HTTP-Server
是Swoole
的一个组件,是SwooleServer
中的一种,提供了一个适合Swoole
直接运行的HttpServer
环境。Swoft
一个现代的Web框架
,和Swoole
亲和性高,同时也是上面提到的Swoole-HTTP-Server
模型的一个实践。Swoft
管理着该Web
模型中的Swoole
,以及Swoole-HTTP-Server
,对开发者屏蔽Swoole
的种种复杂操做细节,并做为一个Web框架
向开发者提供各类Web开发
须要用到的路由
,MVC
,数据库访问
等功能组件等。
最核心的就是HttpServer
以及RpcServer
Swoft
直接使用的是Swoole
内建的\Swoole\Http\Server
,它已经处理好全部HTTP
层面的全部东西,咱们只须要关注应用自己,咱们来看一下HTTP
服务几个重要生命周期点。
这个阶段进行的行为有几个特征
bootstrap
行为:如必须的常量定义,Composer
加载器引入,配置读取等;Worker/Task
进程共享的程序全局期的对象,如Swoole\Lock
,Swoft\Memory\Table
的建立;Process
的启动;Bean
容器基本初始化,以及项目启动流程须要的coreBean
的加载。这块涉及东西比较杂,为控制篇幅后续用单独文章介绍。
和Http
服务关系最密切的进程是Swoole
中的Worker进程(组)
,绝大部分业务处理都在该进程中进行。
对于每一个Swoole事件
,Swoft
都提供了对应的Swoole监听器
(对应@SwooleListener
注解)做为事件机制的封装。要理解Swoft
的HttpServer
是如何在Swoole
下运行的咱们重点须要关注下两个在两个Swoole
事件swoole.workerStart
和swoole.onRequest
。
swoole.workerStart
事件WorkerStart
事件在TaskWorker/Worker
进程启动时发生,每一个TaskWorker/Worker
进程里都会执行一次。
这是个关键节点,由于swoole.workerStart
回调以后新建的对象都是进程全局期的,使用的内存都属于特定的Task/Worker
进程,相互独立。也只有在这个阶段或之后初始化的部分才是能够被热重载的。
事件底层关键代码以下:
// Swoft\Bootstrap\Server\ServerTrait.php /** * @param bool $isWorker * @throws \InvalidArgumentException * @throws \ReflectionException */ protected function reloadBean(bool $isWorker) { BeanFactory::reload(); $initApplicationContext = new InitApplicationContext(); $initApplicationContext->init(); if($isWorker && $this->workerLock->trylock() && env('AUTO_REGISTER', false)){ App::trigger(AppEvent::WORKER_START); } }
这里作的事情有3点
Bean
容器:BeanFactory::reload();
就是Swoft
的Bean
容器初始化入口,注解的扫描也是在此处进行(实际上这个说法并不许确,Bean
容器真正的初始化阶段在Swoole Server
启动前的BootStrap
阶段就已经进行了,只不过那时进行的是少部分初始化,相对swoole.workerStart
中的初始化的Bean
数量,比重很小)。在workerStart
中初始化Bean
容器是Swoft
能够热更新代码的基础。initApplicationContext->init()
会注册Swoft
事件监听器(对应@Listener
),方便用户处理Swoft
应用自己的各类钩子。随后触发一个swoft.applicationLoader
事件,各组件经过该事件进行配置文件加载,HTTP/RPC
路由注册。swoole.onRequest
事件每一个HTTP
请求到来时仅仅会触发swoole.onRequest
事件。
框架代码自己都是由大量进程全局期和少许程序全局期的对象构成,而onReceive
中建立的对象譬如$request
和$response
都是请求期的,随着HTTP
请求的结束而回收。
事件底层关键代码以下:
/** * @param array ...$params * @return \Psr\Http\Message\ResponseInterface * @throws \InvalidArgumentException */ public function dispatch(...$params): ResponseInterface { /** * @var RequestInterface $request * @var ResponseInterface $response */ list($request, $response) = $params; try { // before dispatcher $this->beforeDispatch($request, $response); // request middlewares $middlewares = $this->requestMiddleware(); $request = RequestContext::getRequest(); $requestHandler = new RequestHandler($middlewares, $this->handlerAdapter); $response = $requestHandler->handle($request); } catch (\Throwable $throwable) { /* @var ErrorHandler $errorHandler */ $errorHandler = App::getBean(ErrorHandler::class); $response = $errorHandler->handle($throwable); } $this->afterDispatch($response); return $response; }
beforeDispatch($request, $response):
swoft.beforeRequest
事件。RequestHandler->handle($request):
Action
,具体处理能够参考RPC章节,原理基本相同。$afterDispatch($response):
HTTP
响应报文发送客户端并触发swoft.resourceRelease
(详情在链接池一文中说起)事件和swoft.afterRequest
事件总的来讲,纵观这几个生命周期点你须要搞清楚几件事:
Swoole
的Worker
进程是你绝大多数HTTP
服务代码的运行环境。Swoole
的Server
启动前完成,一部分在swoole.workerStart
事件回调中完成,前者没法热重载但可能被多个进程共享。Worker/Task
进程启动时执行一次, 不像PHP-FPM
每次请求都会执行一次,框架对象也不像PHP-FPM
会随请求返回而销毁。swoole.onRequest
事件,里面就是咱们的请求处理代码真正运行的地方,只有这事件内产生的对象才会在请求结束时被回收。生命周期和HTTP服务
基本一致,详情参考《Swoft源码剖析-RPC功能实现》