前言

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

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

三种PHP应用的Web模型

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

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

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

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

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

LNMP-with-Swoole模型虽然引入了Swoole和异步处理能力,但是核心还是PHP-FPM,实际上还远远没有发挥出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使用更加的直接方便,支持更好。

Swoft 和 Swoole 的关系是什么 ?

  1. Swoole是一个异步引擎,核心是为PHP提供异步IO执行的能力,同时提供一套异步编程可能会用到的工具集。
  2. Swoole-HTTP-Server是Swoole的一个组件,是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注解)作为事件机制的封装。要理解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点

  1. 初始化Bean容器:
    上文中的BeanFactory::reload();就是Swoft的Bean容器初始化入口,注解的扫描也是在此处进行(实际上这个说法并不准确,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. Swoole的Worker进程是你绝大多数HTTP服务代码的运行环境。
    2. 一部分初始化和加载操作在Swoole的Server启动前完成,一部分在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. 百万年薪python之路 -- HTML基础

    一. Web标准 web标准: w3c:万维网联盟组织,用来制定web标准的机构(组织) web标准:制作网页遵循的规范 web标准规范的分类:结构标准.表现标准.行为标准. 结构:html.表示:c ...

  2. 百万年薪python之路 -- MySQL数据库之 MySQL行(记录)的操作(一)

    MySQL的行(记录)的操作(一) 1. 增(insert) insert into 表名 value((字段1,字段2...); # 只能增加一行记录 insert into 表名 values(字 ...

  3. Log4j slf4j 配置简单介绍

    Log4j slf4j 配置简单介绍 先借鉴一篇很好的文章 为什么要使用SLF4J而不是Log4J import org.slf4j.Logger; import org.slf4j.LoggerFa ...

  4. SteamVR Plugin

    使用HTC vive基于unity做虚拟现实,需要用到steamVR插件,最近查找了很多资料,稍微做一下总结. 做虚拟现实无非是头显在场景中的camera功能以及手柄的操作功能. (一)camera以 ...

  5. WinDbg命令系统

    WinDbg命令系统 WinDbug三种命令 WinDbug是一个强大的调试器,大部分很多功能都是通过命令来实现的,命令在命令窗口中输入,主要分为以下三类: 标准命令 标准命令提供了调试器的基本功能, ...

  6. swift ARC中的strong、weak、unowned

    Swift 用自动引用计数ARC(Automatic Reference Counting)方式来跟踪和管理app的内存使用.这使得内存管理成为swift内部的机制,不需要认为考虑.ARC会自动释放那 ...

  7. CSAPP:代码优化【矩阵运算】

    编程除了使程序在所有可能的情况下都正确工作,还需要考虑程序的运行效率,上一节主要介绍了关于读写的优化,本节将对运算的优化进行分析.读写优化 编写高效程序需要做到以下两点: 选择一组合适的算法和数据结构 ...

  8. 前端技术之:JavaScript Test 断言库

    expect 声称可以写更好的断言. https://github.com/mjackson/expect   chai 可以写BDD样式的断言,也可以写TDD样式的断言,可用于Node.js与浏览器 ...

  9. pymssql的Connection相关特性浅析

    关于Python的pymssql模块,之前研究时总结了"pymssql默认关闭自动模式开启事务行为浅析"这篇博客,但是在测试过程中又发现了几个问题,下面对这些问题做一些浅析,如有不 ...

  10. JS 暴虐算法查找

      @dd|ad|fds|d@dd|ad|fds|d@dd|ad|fds|d@   var e = [];     window.onload = function () {         var ...