Swoole和Swoft的那些事 (Http/Rpc服务篇)
https://www.jianshu.com/p/4c0f625d5e11
Swoft在PHPer圈中是一个门槛较高的Web框架,不仅仅由于框架本身带来了很多新概念和前沿的设计,还在于Swoft是一个基于Swoole的框架。Swoole在PHPer圈内学习成本最高的工具没有之一,虽然Swoft的出现降低了Swoole的使用成本,但如果你对Swoole本身了解不够深入,仍然很难避免栽进种种"坑"中。
考虑到这个现状,也为降低阅读难度,后续几个和Swoole联系较为密切的机制,笔者会调整写作思路,将文章的定位从 「帮助读者深入理解Swoft」 调整为 「帮助读者理解Swoft和Swoole」,叙述节奏也会放慢。
三种PHP应用的Web模型
LNMP和LAMP是绝大多数PHPer最熟悉的基础Web架构,这里以常见的LNMP作为例子描述一个常见 无Swoole应用的构件组成:Nginx充当Webservice,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相比有巨大的变化,这种模型中充当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的关系是什么?
- 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,数据库访问等功能组件等。
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.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请求的结束而回收。
事件底层关键代码如下:
/**
* 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;
}
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事件
,里面就是我们的请求处理代码真正运行的地方,只有这事件内产生的对象才会在请求结束时被回收。
RPC服务器
生命周期和Http服务基本一致,详情参考《[原创]Swoft源码剖析-RPC功能实现》
Swoft源码剖析系列目录:https://www.jianshu.com/p/2f679e0b4d58
作者:bromine
链接:https://www.jianshu.com/p/4c0f625d5e11
來源:简书
简书著作权归作者所有,任何形式的转载都请联系作者获得授权并注明出处。
Swoole和Swoft的那些事 (Http/Rpc服务篇)的更多相关文章
- Swoft 源码剖析 - Swoole和Swoft的那些事 (Http/Rpc服务篇)
前言 Swoft在PHPer圈中是一个门槛较高的Web框架,不仅仅由于框架本身带来了很多新概念和前沿的设计,还在于Swoft是一个基于Swoole的框架.Swoole在PHPer圈内学习成本最高的工具 ...
- Swoft源码之Swoole和Swoft的分析
这篇文章给大家分享的内容是关于Swoft 源码剖析之Swoole和Swoft的一些介绍(Task投递/定时任务篇),有一定的参考价值,有需要的朋友可以参考一下. 前言 Swoft的任务功能基于Swoo ...
- tensorflow学习笔记3:写一个mnist rpc服务
本篇做一个没有实用价值的mnist rpc服务,重点记录我在调试整合tensorflow和opencv时遇到的问题: 准备模型 mnist的基础模型结构就使用tensorflow tutorial给的 ...
- NET Core微服务之路:自己动手实现Rpc服务框架,基于DotEasy.Rpc服务框架的介绍和集成
本篇内容属于非实用性(拿来即用)介绍,如对框架设计没兴趣的朋友,请略过. 快一个月没有写博文了,最近忙着两件事; 一:阅读刘墉先生的<说话的魅力>,以一种微妙的,你我大家都会经常遇见 ...
- 通过 Netty、ZooKeeper 手撸一个 RPC 服务
说明 项目链接 微服务框架都包括什么? 如何实现 RPC 远程调用? 开源 RPC 框架 限定语言 跨语言 RPC 框架 本地 Docker 搭建 ZooKeeper 下载镜像 启动容器 查看容器日志 ...
- 性能工具之Jmeter压测Hprose RPC服务
概述 Hprose(High Performance Remote Object Service Engine),国人开发的一个远程方法调用的开源框架.它是一个先进的轻量级的跨语言跨平台面向对象的高性 ...
- 基于netty轻量的高性能分布式RPC服务框架forest<下篇>
基于netty轻量的高性能分布式RPC服务框架forest<上篇> 文章已经简单介绍了forest的快速入门,本文旨在介绍forest用户指南. 基本介绍 Forest是一套基于java开 ...
- 基于netty轻量的高性能分布式RPC服务框架forest<上篇>
工作几年,用过不不少RPC框架,也算是读过一些RPC源码.之前也撸过几次RPC框架,但是不断的被自己否定,最近终于又撸了一个,希望能够不断迭代出自己喜欢的样子. 顺便也记录一下撸RPC的过程,一来作为 ...
- Thrift 个人实战--Thrift RPC服务框架日志的优化
前言: Thrift作为Facebook开源的RPC框架, 通过IDL中间语言, 并借助代码生成引擎生成各种主流语言的rpc框架服务端/客户端代码. 不过Thrift的实现, 简单使用离实际生产环境还 ...
随机推荐
- FPGA烧完程序之后,检测不到网口的
原因:未给phy芯片添加复位 解决方法:在程序顶部添加一个输出信号output e_reset,使其值一直为高. output e_reset, 'b1;
- Java设计模式(14)——行为模式之不变模式(Immutable)
一.概述 概念 分类:弱不变模式(子类可变)和强不变模式(子类也是不可变) 应用场景 java.lang.String是一个经典的强不变类 二.分析 与享元模式的关系
- 【转】Odoo装饰器: one装饰
one装饰器的作用是对每一条记录都执行对应的方法,相当于traditional-style中的function,无返回值! 应用举例: 定义的columns now = fields.Datetime ...
- CF 1042 F. Leaf Sets
F. Leaf Sets http://codeforces.com/contest/1042/problem/F 题意: 将所有的叶子节点分配到尽量少的集合,一个可行的集合中两两叶子节点的距离< ...
- 移动onenet基础通信套件V1.08版本的AT指令测试
1. 本次测试版本V1.08,AT+MIPLCREATE,首先需要一个配置文件.该指令创建一个基础通信套件的实例 2. 看下CGFID=2的配置,这个连接类型,UDP是1还是0?用户名和密码是什么?哪 ...
- SpringBoot学习:添加JSP支持
项目下载地址:http://download.csdn.NET/detail/aqsunkai/9805821 (一)pom中添加依赖: <!-- https://mvnrepository.c ...
- FCL中你不得不知的几种委托
FCL中丰富的类库信息极大的方便了我们的编码,很多我们日常经常用到的类型,FCL中已经帮我们定义好,下面要介绍的就是FCL中定义好的几种委托类型,直接使用它们不仅能提高我们的编码效率,而且还能让我们的 ...
- 11gR2RAC更换CRS磁盘组文档
磁盘(pv)准备 在生产环境中,提前从存储上划分一些磁盘挂载到RAC系统的两个节点上(node1,node2). 新增加磁盘组为(hdisk14--hdisk24) 1.1磁盘使用规划 ...
- 2019年猪年海报PSD模板-第四部分
14套精美猪年海报,免费猪年海报,下载地址:百度网盘,https://pan.baidu.com/s/1WUO4L5PHIHG5hAurv52_2A
- 《Git学习指南》学习笔记(二)
第三章 提交究竟是什么 每次提交都会生成一个40位的散列值.只要知道散列值,我们就可以恢复到该次提交,这个操作也被称之为检出(checkout)操作. 访问权限与时间戳 Git会保存每个文件原有的访问 ...