资讯专栏INFORMATION COLUMN

Swoft 源码剖析 - Swoole和Swoft的那些事 (Http/Rpc服务篇)

张汉庆 / 2140人阅读

摘要:和服务关系最密切的进程是中的进程组,绝大部分业务处理都在该进程中进行。随后触发一个事件各组件通过该事件进行配置文件加载路由注册。事件每个请求到来时仅仅会触发事件。服务器生命周期和服务基本一致,详情参考源码剖析功能实现

作者:bromine
链接:https://www.jianshu.com/p/4c0...
來源:简书
著作权归作者所有,本文已获得作者授权转载,并对原文进行了重新的排版。
Swoft Github: https://github.com/swoft-clou...

Swoft源码剖析系列目录:https://segmentfault.com/a/11...
前言

SwoftPHPer圈中是一个门槛较高的Web框架,不仅仅由于框架本身带来了很多新概念和前沿的设计,还在于Swoft是一个基于Swoole的框架。SwoolePHPer圈内学习成本最高的工具没有之一,虽然Swoft的出现降低了Swoole的使用成本,但如果你对Swoole本身了解不够深入,仍然很难避免栽进种种"坑"中。

考虑到这个现状,也为降低阅读难度,后续几个和Swoole联系较为密切的机制,笔者会调整写作思路,将文章的定位从 「帮助读者深入理解Swoft」 调整为 「帮助读者理解Swoft和Swoole」,叙述节奏也会放慢。

三种PHP应用的Web模型

LNMPLAMP是绝大多数PHPer最熟悉的基础Web架构,这里以常见的LNMP作为例子描述一个常见 无Swoole应用的构件组成:Nginx充当Web Service, PHP-FPM维护一个进程池去运行Web项目。

对比更古老的CGI模型,PHP-FPM已经引入了进程常驻的概念,避免每次请求创建并销毁进程的开销以及拓展加载的开销,但是每个请求仍然要执行PHP RINITRSHUTDOWN 之间的所有流程,包括重新加载一次框架源码以及项目代码,造成极大的性能浪费。

这种模型的优点是简单成熟和稳定,一次运行随后销毁 带来的开发便捷性是PHP能够流行起来的原因之一。市面上绝大多数PHP项目使用的都是基于该种架构的变体。

LNMP-with-SwooleLNMP的一种变体,其在LNMP的基础上引入了Swoole组件。
PHP-FPM一样,Swoole有一套自己的进程管理机制。但由于代码变得高度常驻和编程思维需要从同步到异步的转变,所以Swoole和传统的基于PHP-FPMWeb框架亲和度很低,即使是适配升级过的老式Web框架,目前在Swoole上运行的表现往往并不好。

因此出现了这在这种折中方案,并没有直接将原有PHP代码运行在Swoole中,而是使用Swoole搭建了一个服务,系统通过接口与Swoole通信,从而为Web项目补充了异步处理的能力。我称呼这种同时使用PHP-FPMSwoole的系统为 半Swoole应用。因为接入简单,所以是绝大多数现有项目优先考虑的Swoole接入方案。

LNMP-with-Swoole模型虽然引入了Swoole和异步处理能力,但是核心还是PHP-FPM,实际上还远远没有发挥出Swoole的真正优势。

Swoole-HTTP-ServerLNMP-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的地位相当于传统模型中的NginxPHP-FPM之和。

一次加载常驻内存,不同的请求间基本上复用了onRequest以外的所有流程,使得每个请求的开销大大降低;异步IO的特性使得这种模型吞吐量远远高于传统的LNMP模型。另外相对于独立的Swoole服务,内嵌在Web系统中的Swoole使用更加的直接方便,支持更好。

Swoft 和 Swoole 的关系是什么 ?

Swoole是一个异步引擎,核心是为PHP提供异步IO执行的能力,同时提供一套异步编程可能会用到的工具集。

Swoole-HTTP-ServerSwoole的一个组件,是SwooleServer中的一种,提供了一个适合Swoole直接运行的HttpServer环境。

Swoft一个现代的Web框架Swoole亲和性高,同时也是上面提到的Swoole-HTTP-Server模型的一个实践。

Swoft管理着该Web模型中的Swoole,以及Swoole-HTTP-Server对开发者屏蔽Swoole的种种复杂操作细节,并作为一个Web框架向开发者提供各种Web开发需要用到的路由MVC数据库访问等功能组件等。

Swoft 是如何使用 Swoole 的 ?

最核心的就是HttpServer以及RpcServer

HTTP 服务器

Swoft直接使用的是Swoole内建的SwooleHttpServer,它已经处理好所有HTTP层面的所有东西,我们只需要关注应用本身,我们来看一下HTTP服务几个重要生命周期点。

Swoole 启动前

这个阶段进行的行为有几个特征

基础bootstrap行为:如必须的常量定义,Composer加载器引入,配置读取等;

需要生成被所有Worker/Task进程共享的程序全局期的对象,如SwooleLock,SwoftMemoryTable的创建;

启动时,所有进程中合计只能执行一次的操作:如前置Process的启动;

Bean容器基本初始化,以及项目启动流程需要的coreBean的加载。

这块涉及东西比较杂,为控制篇幅后续用多带带文章介绍。

Http服务关系最密切的进程是Swoole中的Worker进程(组),绝大部分业务处理都在该进程中进行。
对于每个Swoole事件Swoft都提供了对应的Swoole监听器(对应@SwooleListener注解)作为事件机制的封装。要理解SwoftHttpServer是如何在Swoole下运行的我们重点需要关注下两个在两个Swoole事件swoole.workerStartswoole.onRequest

swoole.workerStart 事件

WorkerStart事件在TaskWorker/Worker进程启动时发生,每个TaskWorker/Worker进程里都会执行一次。
这是个关键节点,因为swoole.workerStart回调之后新建的对象都是进程全局期的,使用的内存都属于特定的Task/Worker进程,相互独立。也只有在这个阶段或以后初始化的部分才是可以被热重载的。
事件底层关键代码如下:

// SwoftBootstrapServerServerTrait.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();就是SwoftBean容器初始化入口,注解的扫描也是在此处进行(实际上这个说法并不准确,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 PsrHttpMessageResponseInterface
 * @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事件

总的来说,纵观这几个生命周期点你需要搞清楚几件事:

SwooleWorker进程是你绝大多数HTTP服务代码的运行环境。

一部分初始化和加载操作在SwooleServer启动前完成,一部分在swoole.workerStart事件回调中完成,前者无法热重载但可能被多个进程共享。

初始化代码只会在系统启动和Worker/Task进程启动时执行一次, 不像PHP-FPM每次请求都会执行一次,框架对象也不像PHP-FPM会随请求返回而销毁。

每次请求都会触发一次swoole.onRequest事件,里面就是我们的请求处理代码真正运行的地方,只有这事件内产生的对象才会在请求结束时被回收。

RPC服务器

生命周期和HTTP服务基本一致,详情参考《Swoft源码剖析-RPC功能实现》

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/29027.html

相关文章

  • Swoft 源码剖析 - 目录

    摘要:作者链接來源简书著作权归作者所有,本文已获得作者授权转载,并对原文进行了重新的排版。同时顺手整理个人对源码的相关理解,希望能够稍微填补学习领域的空白。系列文章只会节选关键代码辅以思路讲解,请自行配合源码阅读。 作者:bromine链接:https://www.jianshu.com/p/2f6...來源:简书著作权归作者所有,本文已获得作者授权转载,并对原文进行了重新的排版。Swoft...

    qpwoeiru96 评论0 收藏0
  • Swoft 源码剖析 - SwooleSwoft那些(Task投递/定时任务)

    摘要:作为定时任务的执行者,通过每唤醒自身一次,然后把执行表遍历一次,挑选当下需要执行的任务,通过投递出去并更新该任务执行表中的状态。 作者:bromine链接:https://www.jianshu.com/p/b44...來源:简书著作权归作者所有,本文已获得作者授权转载,并对原文进行了重新的排版。Swoft Github: https://github.com/swoft-clou.....

    vvpvvp 评论0 收藏0
  • Swoft 源码剖析 - RPC 功能实现

    摘要:值得一提的是目前的服务即服务,暂没有其他的服务功能,所以基本上相关的配置指代的就是。会将请求传递给各个中间件,最终最终传递给处理。源码剖析系列目录 作者:bromine链接:https://www.jianshu.com/p/411...來源:简书著作权归作者所有,本文已获得作者授权转载,并对原文进行了重新的排版。Swoft Github: https://github.com/swo...

    marser 评论0 收藏0
  • Swoft 源码剖析 - 代码自动更新机制

    摘要:作者链接來源简书著作权归作者所有,本文已获得作者授权转载,并对原文进行了重新的排版。文件重载管理进程注册了一个名为的该进程会在系统引导的最后一个阶段,即启动前启动。 作者:bromine链接:https://www.jianshu.com/p/e63...來源:简书著作权归作者所有,本文已获得作者授权转载,并对原文进行了重新的排版。Swoft Github: https://githu...

    iflove 评论0 收藏0
  • SwooleSwoft应用

    摘要:在中的应用官网源码解读号外号外欢迎大家我们开发组定了一个就线下聚一次的小目标上一篇源码解读反响还不错不少同学推荐再加一篇讲解一下中使用到的功能帮助大家开启的实战之旅服务器开发涉及到的相关技术领域的知识非常多不日积月累打好基础是很难真正 date: 2017-12-14 21:34:51title: swoole 在 swoft 中的应用 swoft 官网: https://www.sw...

    EscapedDog 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<