前言

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

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

三种PHP应用的Web模型

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

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

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

LNMP-with-Swoole 是 LNMP的一种变体,其在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 的关系是什么 ?

  1. Swoole是一个异步引擎,核心是为PHP提供异步IO执行的能力,同时提供一套异步编程可能会用到的工具集。
  2. Swoole-HTTP-ServerSwoole的一个组件,是SwooleServer中的一种,提供了一个适合Swoole直接运行的HttpServer环境。
  3. Swoft一个现代的Web框架,和Swoole亲和性高,同时也是上面提到的Swoole-HTTP-Server模型的一个实践。

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

Swoft 是如何使用 Swoole 的 ?

最核心的就是HttpServer以及RpcServer

HTTP 服务器

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

Swoole 启动前

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

  1. 基础bootstrap行为:如必须的常量定义,Composer加载器引入,配置读取等;
  2. 需要生成被所有Worker/Task进程共享的程序全局期的对象,如Swoole\Lock,Swoft\Memory\Table的创建;
  3. 启动时,所有进程中合计只能执行一次的操作:如前置Process的启动;
  4. 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进程,相互独立。也只有在这个阶段或以后初始化的部分才是可以被热重载的。
事件底层关键代码如下:

// 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点

  1. 初始化Bean容器:
    上文中的BeanFactory::reload();就是SwoftBean容器初始化入口,注解的扫描也是在此处进行(实际上这个说法并不准确,Bean容器真正的初始化阶段在Swoole Server启动前的BootStrap阶段就已经进行了,只不过那时进行的是少部分初始化,相对swoole.workerStart中的初始化的Bean数量,比重很小)。在workerStart中初始化Bean容器是Swoft可以热更新代码的基础。
  2. 初始化的应用上下文
    initApplicationContext->init()会注册Swoft事件监听器(对应@Listener),方便用户处理Swoft应用本身的各种钩子。随后触发一个swoft.applicationLoader事件,各组件通过该事件进行配置文件加载,HTTP/RPC路由注册。
  3. 服务注册
    具体内容会在服务治理章节讲述。

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;
}
  1. beforeDispatch($request, $response):
    设置请求上下文,并触发一个swoft.beforeRequest事件。
  2. RequestHandler->handle($request):
    执行各个 中间件 和请求对应的 Action。
  3. $afterDispatch($response):
    整理HTTP响应报文发送客户端并触发swoft.resourceRelease事件和swoft.afterRequest事件

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

    1. SwooleWorker进程是你绝大多数HTTP服务代码的运行环境。
    2. 一部分初始化和加载操作在SwooleServer启动前完成,一部分在swoole.workerStart事件回调中完成,前者无法热重载但可能被多个进程共享。
    3. 初始化代码只会在系统启动和Worker/Task进程启动时执行一次, 不像PHP-FPM每次请求都会执行一次,框架对象也不像PHP-FPM会随请求返回而销毁。
    4. 每次请求都会触发一次swoole.onRequest事件,里面就是我们的请求处理代码真正运行的地方,只有这事件内产生的对象才会在请求结束时被回收。

      RPC服务器

      生命周期和HTTP服务基本一致

      以上是文章全部内容,有需要学习交流的友人请加入Swoole交流群的咱们一起,有问题一起交流,一起进步!前提是你是学技术的。感谢阅读!

      点此加入该群

Swoft 源码剖析 - Swoole和Swoft的那些事 (Http/Rpc服务篇)的更多相关文章

  1. Swoft源码之Swoole和Swoft的分析

    这篇文章给大家分享的内容是关于Swoft 源码剖析之Swoole和Swoft的一些介绍(Task投递/定时任务篇),有一定的参考价值,有需要的朋友可以参考一下. 前言 Swoft的任务功能基于Swoo ...

  2. Swoole和Swoft的那些事 (Http/Rpc服务篇)

    https://www.jianshu.com/p/4c0f625d5e11 Swoft在PHPer圈中是一个门槛较高的Web框架,不仅仅由于框架本身带来了很多新概念和前沿的设计,还在于Swoft是一 ...

  3. swoft| 源码解读系列一: 好难! swoft demo 都跑不起来怎么破? docker 了解一下呗~

    title: swoft| 源码解读系列一: 好难! swoft demo 都跑不起来怎么破? docker 了解一下呗~description: 阅读 sowft 框架源码, swoft 第一步, ...

  4. swoft| 源码解读系列二: 启动阶段, swoft 都干了些啥?

    date: 2018-8-01 14:22:17title: swoft| 源码解读系列二: 启动阶段, swoft 都干了些啥?description: 阅读 sowft 框架源码, 了解 sowf ...

  5. jQuery之Deferred源码剖析

    一.前言 大约在夏季,我们谈过ES6的Promise(详见here),其实在ES6前jQuery早就有了Promise,也就是我们所知道的Deferred对象,宗旨当然也和ES6的Promise一样, ...

  6. Nodejs事件引擎libuv源码剖析之:高效线程池(threadpool)的实现

    声明:本文为原创博文,转载请注明出处. Nodejs编程是全异步的,这就意味着我们不必每次都阻塞等待该次操作的结果,而事件完成(就绪)时会主动回调通知我们.在网络编程中,一般都是基于Reactor线程 ...

  7. Apache Spark源码剖析

    Apache Spark源码剖析(全面系统介绍Spark源码,提供分析源码的实用技巧和合理的阅读顺序,充分了解Spark的设计思想和运行机理) 许鹏 著   ISBN 978-7-121-25420- ...

  8. 基于mybatis-generator-core 1.3.5项目的修订版以及源码剖析

    项目简单说明 mybatis-generator,是根据数据库表.字段反向生成实体类等代码文件.我在国庆时候,没事剖析了mybatis-generator-core源码,写了相当详细的中文注释,可以去 ...

  9. STL"源码"剖析-重点知识总结

    STL是C++重要的组件之一,大学时看过<STL源码剖析>这本书,这几天复习了一下,总结出以下LZ认为比较重要的知识点,内容有点略多 :) 1.STL概述 STL提供六大组件,彼此可以组合 ...

随机推荐

  1. CMMS系统中工单派案&调度

    系统为客户经理提供一个有效的调度控制台,由客户经理负责将需要外派现场处理的工单进行统一的分配调度,系统显示每个技术人员的时间表,根据专业技能.可用性.距离或其他资格标准筛选技术服务人员,并向调度人员提 ...

  2. Unity调用Android

    1.Unity调用Android 根据unity用户手册,unity可以采用native code(c/c++),但是也提供了C#调用方法,本文对此进行简单讲解. 2.Unity获取android类 ...

  3. Andriod项目结构

    在Android Studio第一次创建一个项目,点击左侧的Project选项卡,在Project窗口下有几个不同的视图,默认的是名为Android的视图: 对于大部分的开发工作,我们只要在Andro ...

  4. (六)添加adbmingling

    给环境变量Path添加adb命令路径,即adb.exe所在的目录 C:\Users\LIU Liang\AppData\Local\Android\Sdk\platform-tools

  5. 【原创】怎样才能写出优雅的 Java 代码?这篇文章告诉你答案!

    本文已经收录自 JavaGuide (59k+ Star):[Java学习+面试指南] 一份涵盖大部分Java程序员所需要掌握的核心知识. 本文比较简短,基本就是推荐一些对于写好代码非常有用的文章或者 ...

  6. kali2019里安装Burp Suite安装破解版加汉化版

    Burpsuite是一个强大web漏洞挖掘工具,截断代理,解码和编码,Fuzzy进行各种注入和暴力破解 插件扩展,有多个模块 Burp Suite没有中文版的,我英语又不好,我虽然精通Burp Sui ...

  7. 最强中文NLP预训练模型艾尼ERNIE官方揭秘【附视频】

    “最近刚好在用ERNIE写毕业论文” “感觉还挺厉害的” “为什么叫ERNIE啊,这名字有什么深意吗?” “我想让艾尼帮我写作业” 看了上面火热的讨论,你一定很好奇“艾尼”.“ERNIE”到底是个啥? ...

  8. 一篇文章让你彻底理解java中抽象类和接口

    目录 1.我所理解的抽象类 2.我所理解的接口 3.抽象类和接口本质区别 相信大家都有这种感觉:抽象类与接口这两者有太多相似的地方,又有太多不同的地方.往往这二者可以让初学者摸不着头脑,无论是在实际编 ...

  9. ETCD节点故障恢复

    我在微服务组里面主要负责配置中心的构建,我们的配置中心使用到了ETCD.在我们的内网环境中搭建了三个节点的ETCD,不过这三个节点的ETCD都搭建在同一台机器上.后来机器资源不够了系统直接kill了E ...

  10. 【XSY1986】【BZOJ1455】罗马游戏

    就是一模板题. 合并就直接merge. pop就是将自己的值设为一,再将自己的左右儿子合并即可. 查询直接找到堆顶,输出. 模板左偏树 代码: #include<bits/stdc++.h> ...