PHP-FPM vs Swoole - 学习/实践

本文对比了PHP-FPM和Swoole在处理PHP请求时的机制,PHP-FPM采用Master/Worker模型,每个Worker进程处理一个请求,而Swoole则利用多线程Reactor模式解决高并发问题,常驻内存以减少初始化时间。Swoole还支持协程和异步,适合构建高性能的HTTP服务器、TCP和WebSocket应用。通过改造,Swoole能直接处理请求,减少重复加载依赖,提高性能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.应用场景

主要用于对比两者,分析理解他们的异同点,实现原理和应用场景。

2.学习/操作

1.文档阅读

PHP-FPM vs Swoole | PHP 技术论坛

为什么Swoole可以加速php

React PHP vs Swoole PHP · Issue #389 · php-pm/php-pm · GitHub -- 国外开发者对swoole的测试

https://github.com/swoole/swoole-src/issues/1687

2.整理输出

基于「PHP-FPM vs Swoole | PHP 技术论坛」文章进行修改和补充

前几天看见有几篇讲 swoole 的文章,今天我也来凑个热闹。

水平有限,细节理解可能不到位,欢迎大家帮我补充、纠正。

PHP-FPM

早期版本的 PHP 并没有内置的 WEB 服务器,而是提供了 SAPI(Server API)给第三方做对接。

现在非常流行的 php-fpm 就是通过 FastCGI 协议来处理 PHP 与第三方 WEB 服务器之间的通信。

比如 Nginx + php-fpm 的组合,这种方式运行的 fpm 是 Master/Worker 模式,启动一个 Master 进程监听来自 Nginx 的请求,再 fork 多个 Worker 进程处理请求。每个 Worker 进程只能处理一个请求,

单一进程的生命周期大体如下:

  • 初始化模块。
  • 初始化请求。此处请求是请求 PHP 执行代码的意思,并非 HTTP 的请求。
  • 执行 PHP 脚本。
  • 结束请求。
  • 关闭模块。

对细节感兴趣的可以点这里看图

多进程模型是依赖进程数来解决并发问题,一个进程只能处理一个连接,当启动大量进程,进程调度消耗可能占 CPU 的百分之几十甚至 100%,比如 C10K 问题,多进程模型就力不从心了。

Swoole

Swoole 采用的也是 Master/Worker 模式,不同的是 Master 进程有多个 Reactor 线程,Master 只是一个事件发生器,负责监听 Socket 句柄的事件变化。

Worker 以多进程的方式运行,接收来自 Reactor 线程的请求,并执行回调函数(PHP 编写的)。

启动 Master 进程的流程大致是:

  • 初始化模块。
  • 初始化请求。因为 swoole 需要通过 cli 的方式运行,所以初始化请求时,不会初始化 PHP 的全局变量,如 $_SERVER, $_POST, $_GET 等。
  • 执行 PHP 脚本。包括词法、语法分析,变量、函数、类的初始化等,Master 进入监听状态,并不会结束进程。


Swoole 加速的原理

由 Reactor(epoll 的 IO 复用方式)负责监听 Socket 句柄的事件变化,解决高并发问题。
通过内存常驻的方式节省 PHP 代码初始化的时间,在使用笨重的框架时,用 swoole 加速效果是非常明显的。


对比不同

PHP-FPM

  • Master 主进程 / Worker 多进程模式。
  • 启动 Master,通过 FastCGI 协议监听来自 Nginx 传输的请求。
  • 每个 Worker 进程只对应一个连接,用于执行完整的 PHP 代码。
  • PHP 代码执行完毕,占用的内存会全部销毁,下一次请求需要重新再进行初始化等各种繁琐的操作。
  • 只用于 HTTP Server。


Swoole

  • Master 主进程(由多个 Reactor 线程组成)/ Worker 多进程(或多线程)模式
  • 启动 Master,初始化 PHP 代码,由 Reactor 监听 Socket 句柄的事件变化。
  • Reactor 主线程负责子多线程的均衡问题,Manager 进程管理 Worker 多进程,包括 TaskWorker 的进程。
  • 每个 Worker 接受来自 Reactor 的请求,只需要执行回调函数部分的 PHP 代码。
  • 只在 Master 启动时执行一遍 PHP 初始化代码,Master 进入监听状态,并不会结束进程。
  • 不仅可以用于 HTTP Server,还可以建立 TCP 连接、WebSocket 连接。


以上主要针对核心运行机制作对比,列举的不同,暂时就想到这几点了,如果有漏掉的重点,欢迎大家帮我补充啦~

评论也是引人思考:

整理如下:

TBD

考虑一个问题:

为什么都是常驻内存的方式,PHP-FPM与Swoole的性能差距有些大呢?

下面的解答并没有切中要害, 实际上web应用中在处理http请求的表现孰强孰弱未可知~

建议, 先去把swoole和php-fpm的官方文档都阅读一下, 应该会有一个初步大的答案.

高性能 ≠ 多线程

高性能 ≠ 异步

高性能 ≠ 常驻

高性能 ≠ 复用资源
如果您真的很喜欢 Swoole 建议你先阅读官方文档,理解「概述」所说的事情!

php-fpm vs swoole 本身定位并不是相同的,这两个比较没有说服力。其次,swoole 跟多的是解决一些 php 覆盖不到的问题,例如协程、异步,还有就是协议通信这一块,其次就是 php-fpm 薄弱的地方,例如 Windows 环境,虽然 swoole 也正在改善 Windows 支持,但是依旧不好。与其说 swoole 来和 php-fpm 对比,不如说 swoole 是在拓展 php 的更多可行性。

解答:

Nginx 与 php-fpm

php-fpm 是 FastCGI 的一种实现。

通常我们是将 Nginx 的 PHP 处理部分代理到 php-fpm的端口上,交给 php-fpm 来处理。

而 php-fpm 同样是通过预先加载配置,然后给到子进程的方式的,它会对进程做一些管理。

Swoole

那么问题来了, php-fpm 虽然实现了 FastCGI ,但是,它在处理请求的时候,依然要重新运行一个脚本,像 Laravel 一样的框架,一开始就要加载很多的依赖和文件,依然是一个不小的开销。

我们看一下 Laravel 的 public/index.php 的源码。

require __DIR__.'/../bootstrap/autoload.php';
$app = require_once __DIR__.'/../bootstrap/app.php';
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
$response = $kernel->handle(
    $request = Illuminate\Http\Request::capture()
);
$response->send();
$kernel->terminate($request, $response);

看看前面两条语句,这需要加载多少个依赖啊,这都是大把大把的时间和资源啊,每一次请求都需要加载一边,真是心疼啊。

那么,我们为什么不能像之前一样,能够不重新加载配置文件的 FastCGI ,来一个不用加载这么多的依赖的方式呢?

当然可以,这时候 Swoole 就派上用场了。

既然是通过 $app->make 的方式来生成一个新的Kernel 对象,那么 Application 的对象 $app 自然是不会有什么改变的了。

所以,我们可以在收到请求之前,就把 $app 给生成好,这样就会快了,不是么?

「实际上,还要处理一些超全局变量的问题,需要在每次接收新请求时,将这些超全局变量还原等,

具体可以参见:swoole 适配一些php-fpm web开发框架的适配,如thinkphp,laravel,yii

推荐看下:swoole (P1. 1-1 课程导学)_哔哩哔哩_bilibili ,这个教程有点老了,是关于swoole 2.x版本,但是依然有学习借鉴的价值」

我们可以对它进行一个简单的改造。「这里的改造,并不完全,实际上还会很多坑,比如请求的参数也要处理/还原,否则一直是第一次请求的信息/数据,等等, 而且加载文件这块,可能放在workstart事件更合适」

require __DIR__.'/../bootstrap/autoload.php';
$app = require_once __DIR__.'/../bootstrap/app.php';
$serv = new \Swoole\Server\Http('127.0.0.1', 9501);
$serv->on('request', function ($req, $res) use ($app) {
    $kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
    $response = $kernel->handle(
        $request = Illuminate\Http\Request::capture()
    );
    $res->end($response);
    $kernel->terminate($request, $response);
});
$serv->start();

好了,我们现在就可以通过执行这个脚本来监听9501端口了。

然后就像 Nginx 配置 php-fpm一样来配置它就可以了。「不再需要Nginx和PHP-FPM,因为swoole已经提供了http服务器,而且支持php-cgi程序」

这样我们可以看到,在收到请求之前,就已经把依赖加载干净了,剩下的就是处理请求了。

当然我的这个改动很简陋,根本无法用于生产环境的,只是提供一个例子。

后续补充

...

3.问题/补充

TBD

后续补充

...

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值