https://www.jianshu.com/p/4c0f625d5e11

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

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

三种PHP应用的Web模型

LNMP模型

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

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

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

LNMP-with-Swoole模型

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模型

Swoole-HTTP-Server和LNMP-with-Swoole相比有巨大的变化,这种模型中充当WebServer角色的构件不仅仅有nginx,应用本身也包含了一个内建WebServer,不过由于Swoole Http Server不是专业的Http Server,对Http的处理不完善 ,因此仍然需要使用Nginx作为静态资源服务器以及反代,Swoole Http Server仅仅处理PHP相关的Http流量。

一方面由于Swoole已经包含了WebServer,不再需要实现cgi或者fast-cgi的通用协议去和WebServer通信,另一方面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的?

最核心的就是HttpServerr以及RpcServer

Http服务器

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

swoole启动前

这个阶段进行的行为有几个特征
1.基础bootstrap行为:如必须的常量define,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.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();就是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请求的结束而回收。
事件底层关键代码如下:

   /**
* Do dispatcher
*
* @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,具体处理可以参考RPC章节,原理基本相同。
  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服务基本一致,详情参考《[原创]Swoft源码剖析-RPC功能实现》

Swoft源码剖析系列目录:https://www.jianshu.com/p/2f679e0b4d58

作者:bromine
链接:https://www.jianshu.com/p/4c0f625d5e11
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。

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

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

    前言 Swoft在PHPer圈中是一个门槛较高的Web框架,不仅仅由于框架本身带来了很多新概念和前沿的设计,还在于Swoft是一个基于Swoole的框架.Swoole在PHPer圈内学习成本最高的工具 ...

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

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

  3. tensorflow学习笔记3:写一个mnist rpc服务

    本篇做一个没有实用价值的mnist rpc服务,重点记录我在调试整合tensorflow和opencv时遇到的问题: 准备模型 mnist的基础模型结构就使用tensorflow tutorial给的 ...

  4. NET Core微服务之路:自己动手实现Rpc服务框架,基于DotEasy.Rpc服务框架的介绍和集成

    本篇内容属于非实用性(拿来即用)介绍,如对框架设计没兴趣的朋友,请略过. 快一个月没有写博文了,最近忙着两件事;    一:阅读刘墉先生的<说话的魅力>,以一种微妙的,你我大家都会经常遇见 ...

  5. 通过 Netty、ZooKeeper 手撸一个 RPC 服务

    说明 项目链接 微服务框架都包括什么? 如何实现 RPC 远程调用? 开源 RPC 框架 限定语言 跨语言 RPC 框架 本地 Docker 搭建 ZooKeeper 下载镜像 启动容器 查看容器日志 ...

  6. 性能工具之Jmeter压测Hprose RPC服务

    概述 Hprose(High Performance Remote Object Service Engine),国人开发的一个远程方法调用的开源框架.它是一个先进的轻量级的跨语言跨平台面向对象的高性 ...

  7. 基于netty轻量的高性能分布式RPC服务框架forest<下篇>

    基于netty轻量的高性能分布式RPC服务框架forest<上篇> 文章已经简单介绍了forest的快速入门,本文旨在介绍forest用户指南. 基本介绍 Forest是一套基于java开 ...

  8. 基于netty轻量的高性能分布式RPC服务框架forest<上篇>

    工作几年,用过不不少RPC框架,也算是读过一些RPC源码.之前也撸过几次RPC框架,但是不断的被自己否定,最近终于又撸了一个,希望能够不断迭代出自己喜欢的样子. 顺便也记录一下撸RPC的过程,一来作为 ...

  9. Thrift 个人实战--Thrift RPC服务框架日志的优化

    前言: Thrift作为Facebook开源的RPC框架, 通过IDL中间语言, 并借助代码生成引擎生成各种主流语言的rpc框架服务端/客户端代码. 不过Thrift的实现, 简单使用离实际生产环境还 ...

随机推荐

  1. FPGA烧完程序之后,检测不到网口的

    原因:未给phy芯片添加复位 解决方法:在程序顶部添加一个输出信号output e_reset,使其值一直为高. output e_reset, 'b1;

  2. Java设计模式(14)——行为模式之不变模式(Immutable)

    一.概述 概念 分类:弱不变模式(子类可变)和强不变模式(子类也是不可变) 应用场景 java.lang.String是一个经典的强不变类 二.分析 与享元模式的关系

  3. 【转】Odoo装饰器: one装饰

    one装饰器的作用是对每一条记录都执行对应的方法,相当于traditional-style中的function,无返回值! 应用举例: 定义的columns now = fields.Datetime ...

  4. CF 1042 F. Leaf Sets

    F. Leaf Sets http://codeforces.com/contest/1042/problem/F 题意: 将所有的叶子节点分配到尽量少的集合,一个可行的集合中两两叶子节点的距离< ...

  5. 移动onenet基础通信套件V1.08版本的AT指令测试

    1. 本次测试版本V1.08,AT+MIPLCREATE,首先需要一个配置文件.该指令创建一个基础通信套件的实例 2. 看下CGFID=2的配置,这个连接类型,UDP是1还是0?用户名和密码是什么?哪 ...

  6. SpringBoot学习:添加JSP支持

    项目下载地址:http://download.csdn.NET/detail/aqsunkai/9805821 (一)pom中添加依赖: <!-- https://mvnrepository.c ...

  7. FCL中你不得不知的几种委托

    FCL中丰富的类库信息极大的方便了我们的编码,很多我们日常经常用到的类型,FCL中已经帮我们定义好,下面要介绍的就是FCL中定义好的几种委托类型,直接使用它们不仅能提高我们的编码效率,而且还能让我们的 ...

  8. 11gR2RAC更换CRS磁盘组文档

    磁盘(pv)准备     在生产环境中,提前从存储上划分一些磁盘挂载到RAC系统的两个节点上(node1,node2).     新增加磁盘组为(hdisk14--hdisk24) 1.1磁盘使用规划 ...

  9. 2019年猪年海报PSD模板-第四部分

    14套精美猪年海报,免费猪年海报,下载地址:百度网盘,https://pan.baidu.com/s/1WUO4L5PHIHG5hAurv52_2A                        

  10. 《Git学习指南》学习笔记(二)

    第三章 提交究竟是什么 每次提交都会生成一个40位的散列值.只要知道散列值,我们就可以恢复到该次提交,这个操作也被称之为检出(checkout)操作. 访问权限与时间戳 Git会保存每个文件原有的访问 ...