关于 PHP 8.1 的 Fiber RFC

最新的 PHP 8.1 增加了一个 Fiber 的提案,最近讨论的比较多。有不少好事者拿来说事儿,说是 “Fiber 进入内核之后,Swoole 的使用者就大幅减少“

实际上 Fiber 扩展进入内核后,由于它是一个非常底层的 API ,并不是直接可以使用的技术,不会对 Swoole 产生影响。真正和 Swoole 竞争的是应该是 Amphp 、ReactPHP 。Fiber 反而对 Swoole 是有好处的,PHP 内核开发者维护了协程切换的全局状态列表,Swoole PHPCoroutine 这部分的代码实现就变简单了。另外,其他扩展也会注意到协程的存在,使用 C 全局变量或栈上内存时考虑到协程切换的可能性,避免出现 Crash。ext-fiber 合并进来之后,也应标记为 alpha 状态,一些特殊情况能会引起崩溃,需要比较长的时间去收集解决这些问题。

最近这几年即便官方连续出了很多个大版本,PHP 还是一直是在走下坡路。有许多 PHP 开发者说是因为 PHP 性能不行,没有 JIT。于是 PHP8.0 加入了 JIT。还有人说 PHP 没有协程,所以 PHP8.1 要加入 Fiber。马上就会有人说 PHP 缺少多线程,按照现在这个节奏,可以预见未来有可能 PHP 的多线程扩展 parallels 也会合并到内核。PHP8 还加入了一个 FFI 模块,甚至可以直接使用 PHP 调用 C 库。

可是真的加入如此多的能力,PHP 就得到很大的改变了吗?

你们想要的 Fiber 是这样的:


1.png

实际上 PHP 8.1 Fiber 是这样的:


2.png

动态语言中除了 PHP 之外,Python、Ruby、Lua 在很早就有协程支持了,但实际上这些编程语言在协程并发编程方面并没有多出色。真正将协程技术发扬光大的是 Golang ,为什么 Golang 在协程编程方面的如此成功?这是因为它提供了完整的、成体系的一整套技术方案,从语言设计到编译器、协程调度器、标准库、调试器,这才是工业级的技术。在多线程技术方向,很多编程语言都有多线程支持,但真正被广泛使用、达到工业级水平的多线程系统只有 Java 。在 PHP 中真正能达到工业级水平的技术也就是 Apache+mod_php 和 PHP-FPM 。

协程的技术也是一样,PHP 开发者想要从传统的 LAMP/LNMP 短生命周期、串行编程的模式转型到 CSP 协程+通道并发编程,目前暂时也只有 Swoole 是相对来说最成熟的方案。用户真正需要的是一种完整的、系统性、成体系、简单易用、可靠的一整套技术方案。

PHP 8.1 加入 Fiber 我认为是一个仓促的决定。不如系统性地设计一下,从这些7个方面考虑:

EventLoop API
协程(对应 ext-fiber)
IO 调度器(Socket/FileSystem/ChildProcess/Signal/Timer/Stdout/Stdin)
CPU 调度器
现有同步阻塞 IO 扩展(redis、curl、php_stream、sockets、mysqli、pdo_mysql 等)和内置函数(sleep、shell_exec、sleep、gethostbyname 等)如何实现支持协程,变成异步非阻塞模式
协程通信(channel)
服务器:实现 PHP-FPM 协程版,或者提供一个新的协程 HttpServer

事件循环

EventLoop 是协程实现中最核心的基础设施,这里不是指具体实现,C 层面 select/epoll/poll ,PHP 层面 stream_select 或者 libevent/libuv/event 扩展都可以实现,如果 ZendVM 底层提供了 EventLoop,那么不同的框架、不同的库可以在同一个 Loop 中,协程调度器也可以构建在此之上。如果没有统一的 EventLoop 的基础设施,amphp 、 reactphp 等框架都需要各自实现,意味着你在使用 amphp 的程序时,无法使用 reactphp 实现的任何类库。

Node.js、Golang、Swoole 底层都有一个全局的 EventLoop,所有 IO 行为都会被注册到 EventLoop 中,事件触发后执行 callback 或者调度协程。

阻塞 IO 函数

PHP 提供的很多 IO 操作函数都是阻塞的,如果在协程中发生阻塞,就会导致并发失效。退化成和普通 PHP-FPM 一样的串行模式。协程实现中必须要考虑到如何解决这个问题。

Amphp 和 ReactPHP 目前(2021年)采用的实现方式,是使用 PHP 代码实现基于协程的异步非阻塞 IO 版本,在 2018 年之前 Swoole 也是采用这个模式。这样做最大的问题是,

1.成本太高,无法复用 PHP 生态,重复造轮子,需要重新实现 Redis、MySQL、CURL、Http2、WebSocket、Kafka 等大量网络 IO 库
2.质量不高,不像同步阻塞的版本经过大规模验证
3.兼容性差,如果用户使用了一个第三方库,其中包含了阻塞 IO 的客户端调用,就前功尽弃了
4.学习成本高,用户需要学习一套全新的 API ,这对 PHP 开发者非常不友好
5.PHP 实现的版本可能还会存在性能问题,在 Swoole 中由于是使用 C 实现的不存在这一点

所以 Swoole 在 4.1 版本(2018年)开始采用了全新的实现方式,会 Hook 掉 PHP 扩展中的函数指针,通过很少的工作量就彻底解决了这个问题。PHP 开发者直接使用同步阻塞客户端的 API 即可,底层会自动替换为非阻塞的协程版本。比如下面的代码

3.png

Swoole 会替换 PHP 内置的 sleep 和 file_get_contents 函数,变成协程版本,上面的程序就变成了完全并发的了,对用户来说是无感知的。

CPU 调度器

由于 PHP 是动态解释执行的编程语言,在实现协程 CPU 调度器方面比 Golang 有优势。Golang 需要在编译器内做很多工作,控制单个协程占用的 CPU 时间,避免几个协程耗尽 CPU 资源。PHP 可以在 VM 层面直接实现中断,精准控制每个协程最大可执行的时间。

Golang 使用 GPM 模型解决了这个问题,如果一部分协程持续占用 CPU ,调度器会创建更多 Thread 执行新的任务,退化为操作系统调度。PHP 由于不支持多线程,暂时无法实现 GPM 模型,目前 Swoole 所采作用的 VM 中断调度实现是最优解

在 Swoole 的实现中,底层创建了一个中断线程,每 5ms 会产生一个中断信号,在中断函数中判断当前协程执行的时长,如果超过了规定的 10ms 最大执行时间,会自动让出 CPU 切换至其他可执行的协程。

4.png

以上程序会交替执行,每个协程最大执行时间不超过 10ms

我认为正确的方式

创建多个 RFC ,把这些问题讨论清楚,在 PHP9 版本中提供完整的协程方案实现。不求做到 Golang 的程度,至少要能达到生产可用。这样 PHP 才会有大的改变。不过这可能就真要取代 Swoole 了 [哭笑]。

再介绍一下 Swoole 现在做到什么程度:

完整的协程+通道实现
提供了 CPU 调度器,即使是密集计算的程序,也可以使用协程,调度器会按照10ms时间片切换协程
支持绝大部分 PHP 的常用扩展和内置函数,LAMP 时代的代码可以不用修改直接 copy 到协程里运行,而且是异步非阻塞的方式,是真正的并发,我认为这才是黑科技,PHP 协程方案的关键技术
curl 扩展也可以协程化,包括 curl 和 curl_multi,guzzle 可以直接用,腾讯云、阿里云的 PHP SDK 可以直接在协程中使用
提供了 PGSQL 协程实现,基于 pgsql 官方 C 库的异步 API 实现
提供了 ZooKeeper 协程实现,是基于官方的 C 库 插入了 Hook 代码实现
Kafka 协程库的实现
支持协程的新一代调试器:yasd
支持 PHP7.2-8.0 所有版本,ext-fiber 只支持 8.0 以上版本

PHP 作为一个社区驱动的开源项目,背后没有商业公司支持,没有 Golang、Java、Node.js(v8) 这样充足的研发资源投入,需要依赖全世界各地的贡献者提交代码,在产品化方面还是做的不够好。国内有一些人一直在 diss Swoole 有商业公司,但正是因为有商业收入,才保证了我们在 Swoole 开源项目研发上的连续性,在产品化方面也会做的更好。

我对 Fiber 的担忧

Fiber 集成到 PHP 中之后,会有很多 PHP 的框架或者类库创建自己的协程方案,由于 PHP 只提供了 Coroutine Context 的实现,其他几个方面并没有提供,在 PHP 生态中将会出现很多流派的 EventLoop、AsyncIO 、NetworkClient 多种多样的实现。就拿 sleep 函数来说,现在 Amphp 和 ReactPHP 分别叫做 amp\delay 和 react\sleep 。

没有统一的标准,意味着社区的高度分裂。一旦确定了方向,技术的发展演进是非常快的,多样性是一件好事,但也会带来更多新问题,再想统一是很困难的一件事情。即便是 Symfony、Laravel 这样处于顶端的 PHP 框架,也不具备能够 100% 覆盖整个 PHP 生态的能力,这将走向失控。

基于 Swoole/Swow 的方案,实际上依旧是 PHP 原先的生态,大家使用的依然是最熟悉的那些 PHP 函数和库,异步编程和同步阻塞 IO 编程的生态是一致的。比如在 Swoole 协程中可以直接使用阿里云、腾讯云、AWS 提供的 SDK,Fiber 生态下情况就会比较复杂。

Swow 是一个从 Swoole 项目中剥离与协程无关特性,使用 Swoole 协程设计方案的全新实现,与 php-src 保持一致使用了 C 语言实现,目前正在准备 RFC 提案,贡献到 PHP 内核中

Swoole 与 Fiber 的差别

Fiber 只是协程 Context 管理的一种实现,更像是 Generator 的升级版
Swoole 是完整的协程 Runtime & Framework,更像是 Golang

Swoole 是否会使用 ext-fiber ?

暂时不会。有两方面的原因:1. Swoole 的实现是双层协程设计,底层是 C 协程,上层是 PHP 协程。而 Fiber 的实现耦合在一起的。Swoole 是内核协程化设计,在 core 层面对协程操作进行了封装,外层只需要调用 API 即可,不需要关心发生阻塞 IO 时协程如何切换,PHP 层实际上只是 wrapper ,2. Fiber 是以扩展方式加入 PHP 内核的,并不是 ZendAPI,地位等同于 curl/mysql 等扩展库。在 PHP 中扩展依赖管理做的很糟糕。处理不好容易出现找不到 符号(symbol not found),而 Swoole 同样也是 PHP 的一个扩展,它与 ext-fiber 是平级关系,协程是 Swoole 的核心部分,不太好依赖另外一个库的实现。

当然 Swoole 会对齐 ext-fiber 在 PHP 协程切换部分的代码,保证一致性。由于 PHP 暂时还未提供 EventLoop 的基础设施,Swoole 扩展提供的功能和其他基于 ext-fiber 扩展实现的 PHP 协程类库,不在同一个 Loop 中,也无法实现共存。

创建 C 协程


5.png

创建 PHP 协程


6.png

在 PHP 代码中创建协程


7.png

我们也会持续关注 ext-fiber ,在未来某个节点,ext-fiber 足够成熟稳定,并且迁移到 ZendAPI 中时,Swoole 也会考虑使用 ext-fiber

点关注,不迷路

好了各位,以上就是这篇文章的全部内容了,能看到这里的人呀,都是人才。之前说过,PHP方面的技术点很多,也是因为太多了,实在是写不过来,写过来了大家也不会看的太多,所以我这里把它整理成了PDF和文档,如果有需要的可以点这里进阶PHP月薪30k>>>架构师成长路线【视频、面试文档免费获取】

24020938-0e1d4138a0a22218.png

24020938-7cffd24782dd56f9.png
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,126评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,254评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,445评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,185评论 1 278
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,178评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,970评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,276评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,927评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,400评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,883评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,997评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,646评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,213评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,204评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,423评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,423评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,722评论 2 345

推荐阅读更多精彩内容