- Published on
2021 年 PHP 界 Swoole 和 Fiber 的那些瓜
- Authors
- Name
- ttyS3
我是在知乎上偶然看到这么个瓜。
rfc 投票地址: https://wiki.php.net/rfc/fibers
内部讨论记录: https://externals.io/message/113430
在 rfc 投票地址 地址上老灯看到有 46 票通过, 13票反对。 我们去 https://wiki.php.net/rfc/fibers 看看这 13 票反对的都是哪些人。
parallel的作者 krakjoe 反对是可以理解的,人家直言希望自己的扩展被合并成内核,而不是 Fiber.
至于rasmus (Rasmus Lerdorf,PHP 创始人) 吧,我觉得现在版本的 PHP 开发应该跟他没什么关系了,他说啥都行,高兴就好。
所以,13反对票里,有3个是 Swoole 相关方面 的人,有一个甚至还是 Swoole 的创始人。
- doubaokun Swoole开发者
- tianfenghan Swoole开发者, Rango(韩天峰)
- twosee Swoole开发者,Twosee(陈曹奇昊)
老灯的观点与知乎上某些观点比较一致:
拒绝提案的理由是没有做到完善?我觉得这个站不住脚,不能说这个提案目前没有像 Swoole 一样弄一整套就不合并吧?(难道只有 Swoole 这种整套的才行?)
另一个理由是,普通开发者用不上,只有amphp 和 reactphp 这类项目用得上?这我觉得也站不住脚。你永远不知道用户能创造出什么框架出来。以 Go 语言为例, unsafe 代码普通人都用不上, 那 Go 就不用支持 unsafe 了?感觉这些理由很“主观”和“自以为是”。
老灯就两个提问摘取一些回答围观:
《如何看待Swoole作者声称Fiber提案对非Amp以外的项目毫无价值?》
老外对于这个说法也是相当的不happy的,见 https://www.reddit.com/r/PHP/comments/m1w32y/maintainer_of_swoole_about_the_fibers_rfc_i_am/
作者:鲁严波
链接:https://www.zhihu.com/question/448805077/answer/1778652091\ 来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
看了一圈,大家都是在讨论PHP和Swoole,但在回答这个问题之前,我们需要了解一下背景。
1协程的出现
摩尔定律逐步失效,多线程编程开始流行。
然后多线程世界中的开发者(比如C++、Java开发者)发现了两件事情:
第一,多线程写代码还是很蛋疼,需要考虑互斥、同步,很容易出现海森堡bug。
第二,多线程也不是万灵药,在IO密集场景下,线程调度成为了多线程的瓶颈。
发现了这些问题后。开发人员(重新)捡起了协程这个概念。
协程对于不支持多线程的语言(比如PHP)或者多线程支持不太好的语言(嗯,Python的GIL)都是一个机会。
本来支持多线程要做很多改造,从内核到C扩展都需要配合改造。现在好了,一步到位,直接进入协程时代。
但是协程,也出现了两个流派:
1.1无栈协程
JS的ayncio、python官方的asyncio、这次PHP的generator都是此类代表。
特点就是,协程切换是手动触发,从而免除了大部分互斥的操作(没有yield就不会被切换,那就基本没有互斥了)
但是很显然,上面的方案中除了JS的async发展起来了,其他的都不是很流行。
这就要说到他们的缺点了,需要改代码。
一旦底层框架使用了无栈协程,上层都要跟着改造。比如底层一个协程yield一个generator/promise,那么上层就要yield这个generator/promise,一直到顶层的协程调度器来处理这个generator/promise。
站在广大程序员的角度,事情变得很复杂了:
我就把DB driver改成了异步的,你就要我把DAO层、业务逻辑层、Web渲染层都要改一遍?!
所以现有业务系统切换到无栈协程的成本非常高。
就个人经历来说,我写过amphp、写过JS/TS、很早之前也试过Python3的asyncio。
体验就是,作为开发者,需要严格注意返回的是generator还是一个普通的对象/value。
要是写错,那么线上就炸了…… 而且IDE的智能提示,在这儿也无能为力。
所以,一个技术,如果只能大师才能用得起来,那么它就无法大规模流行。无栈协程就处于这样尴尬的场景。
1.2有栈协程
代表有gevent的monky patch,golang,Java的loom,还有
大佬的swoole(协程部分)和这次的Fiber,他们本质上都是有栈协程。
有栈协程的特点就是,协程的切换是Runtime控制的,不用手动一层一层yield。
比如一个函数,发起IO调用,那么Runtime就能把这个协程挂起,然后调度到别的协程,IO完成后,这个协程继续执行。
整个过程中,协程都“感知不到”发生了切换。
这时候,开发人员才能安全的使用协程。
底层切换到协程,上层不用做任何改造。
还是上面的例子,DB driver切换到协程版本,基本上业务代码不用动。
基本上Golang的并发模型就是这样子的,就像
之前说的“Golang就是高级的PHP”。
2 Fiber和Swoole
好,聊完了协程,我们再看下Fiber和Swoole:
个人认为,Fiber作为有栈协程的尝试,是PHP协程标准化的一个进步。虽然IO事件部分还是缺失的,但方向是对的,IO事件部分后面会逐步补上。
Swoole是一个非常好的先驱者,但Swoole在推进协程标准化上做得不多。标准化阵地就在这儿,Swoole不占领,Fiber就会来占领。
从生态角度、后续PHP协程的发展前景来看,还是希望
大佬以及Swoole团队能够推进标准化。
Fiber现阶段对于Amp以外的项目价值很少,但是随着周边生态,比如IO事件系统、协程调度系统、协程同步语义逐步建立起来后,PHP开发者还是会逐步使用的,长远来看,还是需要注意这个提案的影响。
所以建议Swoole团队能够及时把自己的修改合并到上游,争取到更大的PHP内核发言权。
3 番外
3.1 CPU密集型
我们说了这么久的协程,但是协程要解决的是IO密集型的问题。那么CPU密集型的呢?目前看起来主要靠多进程来实现(至少对于众多脚本语言来说是这样)。
在这一块上,PHP还缺少一个多进程的管理器,目前PHP的parallel做了一些尝试,但是目前看起来不是很好用。
3.2 by-pass内核
让我们目前再放长远一点,现在计算机结构中,越来越多的by-pass kernel的方式出现:
内核处理不好线程调度,那么搞协程,用户自己来调度。(无栈协程改造成本太大,所以都采用有栈协程)
内核处理不好网络包,那么搞DPDK,用户自己来处理网络包。(用户自己要独占网卡,很容易出问题;所以需要一个好的共享网卡的by-pass网络栈,现在DPDK的KNI是一个不错的尝试,但是显然不够)
在by-pass kernel这一块,还有没有更加好玩的东西呢?这是一个值得思考的问题。
话说到这儿了,那就再多说两句。
3.3 计算机技术的价值
本科时候,计算机导论是乐嘉锦院长教的,他说过一句话,大意如下:
现在我们写程序,都是计算机结构都已经定好了,然后我们再往里面填代码。真正应该做的是:根据需求设计计算机,而不是被计算机结构框住。
当时觉得,这句话大而无当,没啥意思。现在想来,这句话契合了最近几年计算机的发展途径:
从上面内核不能满足需求,我们by-pass内核;再到CPU不能满足需求,我们开始设计AI芯片。
这后面的逻辑是啥呢?我觉得可以汇总成一句话:
计算机技术和其他技术一样,都要满足客户需求,把价值交付给客户。
作者:匿名用户
链接:https://www.zhihu.com/question/448805077/answer/1778546652\ 来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
跑题说一些我知道的。
- swoole 团队曾经发邮件想表达希望合并到内核。得到的回复当然是你得写提案和有充足的单元测试,必须是 c 语言,并且只希望提供协程 API 部分。swoole 团队的人似乎认定内核组太保守,之后就不了了之了。其实内核组没错,php 社区是很公平的社区,提案起草要经过内部讨论和投票才能合并到内核。合并特性内容可控,通过率才会高。
- swoole 团队基于积累经验开发了 swow,算是 swoole 的简化版,只实现了协程相关,事件驱动用了外部的。大概是去年(2020) 开始的项目,说是会努力合并到内核。作者 twosee 也努力开发,一边也参与内核的贡献积累影响力。
- fiber 是去年九月左右开始起草的提案,12 月正式内部讨论,当时 swoole 相关的群有人分享了这一消息。然而,swoole 社区并没有太在意,觉得自己的 swoole 才是最强的。这个月月初 fiber 正式开始投票,开局就是11-2,投反对票的两人均来自 swoole 社区。
- 最引起社区注意的是,投票阶段 swoole 社区的同学突然邮件内部全员说不支持 fiber,理由主要是两个: fiber 进入内核最大的收益者是 amphp 社区;fiber 太简单,完整的协程支持需要考虑各种各样。
- swow 作者看到 fiber 这么随便就提案合并,说今年也会尝试提案,不管 fiber 是否通过。
viproot 程序员
希望swoole的作者能多考虑php的未来,而不是swoole的未来,swoole虽然扩展了php的能力,但是换句话说swoole对php语言发展并没有做出太大贡献
作者:涛叔
链接:https://www.zhihu.com/question/448805077/answer/1777527343\ 来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
我实名反对
的回答。PHP开发者社区好久没有这样的瓜了。容我为大家细细道来。
我也是 PHP 出身,给 Swoole 贡献过代码。我甚至在2017年的时候也想给 PHP 添加 Fiber 性,参见这里。限于技术水平,我的方案有缺陷,没有得到认可。后来就不了了之了。再后来,我转用Go语言,不怎么关注PHP。
直到昨天我才知道又有开发者尝试给PHP添加 Fiber 特性了。我惊喜万分。于是看了一下它的RFC。他们甚至还在RFC中提到了我之前写的RFC,我之前写的RFC
The prior Fiber RFC did not support context switching within internal calls (
array_map
,preg_replace_callback
, etc.) or opcode handlers (foreach
,yield from
, etc.). This could result in a crash if a function using fibers was used in any user code called from C code or in extensions that overridezend_execute_ex
such as Xdebug.
写的也很中肯。当时我只想处理纯PHP代码Fiber,不想管复杂的C函数栈问题。被拒绝也是预料之中的事。
好了,背景就这么多。我们说正题。
在吃瓜之前,我们得先看看瓜长什么样。不然大家会限入文化和意识形态之争。
第一个要澄清的是并行跟并发的概念。并行可以理解成同时执行,这个需要硬件支持。你如果你的CPU只有一核,那程序不可能并行。并发可以理解成共同执行,主要是一个软件概念。我们就是在单核CPU上也能同时写文档、听音乐。为什么,因为操作系统一般都支持按时间片调度,不同的程序可以交替执行一段时间(时间片)。因为切换的速度特别快,大家感觉不出来罢了。当然了,如果你的CPU有多核,那就可以并行执行,不用交替切换了。但CPU的核数是有限的,你想运行的程序却很多,所以现在的计算机都是同执行并行和并发操作。而并行就是并发的一种实现方式。
总之,并行跟迸发是操作系统的基本概念,学过的同学应该都清楚。我们今天说的Fiber是为了解决并发问题,而不是并行问题。大家在吃瓜之前务必要记住这一点。
回到Fiber。Fiber也叫纤程,其实跟协程是一个概念。
我们讲并发编程无非就是进程、线程、协程。无论是进程还是线程,都可以并发执行,都有自己的栈空间。所以不同线程调用同一函数基本是不会相互影响的(没有使用全局量)。同时,进程跟线程的调度是同操作系统完成的,调度消耗资源比较多、比较慢;而因为有独立的栈空间和其他相关信息,进程和线程也会占用很多系统资源。所以说,我们没法在系统里创建很多进程和线程。即便我们有足够多的内存,我们强行启动成千上万个进程和线程,操作系统在调度的时候也会非常吃力。
为了解决这个问题,人们决定不让操作系统调度,自己上。于是就有了「用户态」线程这样的概念,这就是所谓的协程。从某种成度上看,协程就是一种线程,一种需要用户自行调度的线程。因为不需要操作系统的介入,调度的效率会比较高,资源的消耗比较小。所以现在有些人一提起高并发编程就要用协程。
这里的用户自行调度是相对于操作系统来说的。这个用户可以是程序员,也可以是编译器或运行时。不同的语言处理手法也各不相同。像是Go语言,就是在运行时完成协程调度的。协程在执行的时候如果遇到阻塞(比如网络数据没收到等)就会自动暂停执行,等相关资源就绪之后运行时会按某种规则唤醒。像PHP的generator就需要yield来暂停,再通过resume来恢复。
PHP本身是多进程模式(不完全对)。从5.4开始,PHP支持所谓的generator。我个人认为PHP的generator最开始是为了解决大批量数据处理时的内存占用问题。看如下代码:
<?php
function xrange($start, $end, $step = 1) {
for ($i = $start; $i <= $end; $i += $step) {
yield $i;
}
}
foreach (xrange(1, 1000000) as $num) {
echo $num, "\n";
}
在 foreach 中,调用 xrange 会返回一个 generator 对象,而非直接分配一个有一百万元素的数组。foreach 在每次循环的时候都会调用 generator 对像的 send 方法来获取下一个元素。而这个 generator 会执行 xrange 方法,一直走到 yield。这个 yield 可以理解为临时的 return。xrange执行到 yield 会暂停,send 方法就会返回 yield 后面的 $i。当 foreach 再次调用 send 的时候,generator 会从上一次 yield 的地方继续向下执行。
所以说,generator 对象一不小心给 PHP 添加了暂停函数的特技。基于此,人们想给PHP添加协程功能。最基础的思路在这里,作者是nikic,本身就是 generator 的开发者。从那以后,PHP社区出现了好多基于 generator 的协程框架。最著名的要属https://amphp.org,而amphp社区正是本次php-fibers RFC的推动者。
有了 generator 为什么还要引入 fiber 呢?那是因为当前的 generator 不支持暂停子函数调用。
一般来说我们的代码都是分层的,比如是 api -> controller -> service -> model -> util。可能从 api 调 controller 一直调到 model 都没有阻塞操作,但 model 调 util 可能需要访问 db 连接,数据可能没有传回,在这种情况下,我们希望暂停从 api 到 util 的整个调用栈,等数据传回的时候再继续执行。很可惜,generator 不支持这样的操作。
如果要实现暂停从 api 到 util 的整个调用栈,我们就必需在调用 api 函数之前准备好一个独立的函数调用栈,用这个独立的栈保存各层函数调用的各种数据。这显然是非常复杂的。像 nodejs 等好多语言直接决定不支持这种暂停效果,不会为协程分配独立栈空间。这种叫 stackless coroutine。php了 generator 也采用这种策略。
这种策略的好处就是实现简单,性能高,对应的功能也简单。而且不能实现暂停从 api 到 util 的整个调用栈的效果。如果你要用,必须重写现有的代码。如果你用nodejs,你不得不处理各种async/await逻辑。用async/await写过业务逻辑的人肯定不会愉快。关于这个问题,有人专门写了这篇文章What Color is Your Function?
而我们要说的 fiber 就是为了解决 generator 不能暂停从 api 到 util 的整个调用栈这个问题的。
这个问题其实非常复杂。PHP本身有Zend虚拟机,Zend虚拟机有自己的栈。PHP还有好多功能是通过扩展实现的,而扩展都是用C开发的,所以会用到C运行时的栈。如果要实现Fiber,必须同时分配 C和Zend的栈,确实有难度。但amphp社区的开发者做到了。而且得到了PHP的认可。
我给大家举个使用 fiber 的例子,好让大家有个直观的感受
<?php
function hi(): int {
echo Fiber::suspend(1);
echo Fiber::suspend(2);
echo Fiber::suspend(3);
}
$fiber = new Fiber(function (): void {
hi();
echo Fiber::suspend(4);
});
echo $fiber->start();
echo $fiber->resume('a');
echo $fiber->resume('b');
echo $fiber->resume('c');
echo $fiber->resume('d');
我们定义了一个Fiber,并在fiber中调用了hi函数。
当我们调用 start 函数的时候,fiber 开始执行,一直执行到 hi 函数的第一个 Fiber::suspend。这个 suspend 跟前面的 yield 是类拟的,他会把参数1返给 start 函数。从直观上看,我们调了 start 函数,得到了一个返回值1。跟普通的函数调用没什么两样。但请大家注意hi函数的第一行,调用suspend函数不会返回也就是说echo也不会执行。从直观上看,这一行是调了suspend函数,期待返回一个值并奖其输出。
前面的start()返回后echo会输出suspend返回的数字1整个fiber就进入了暂停状态。此时程序输出1.
如果我们调一下resume函数,传入参数'a'。fiber 就会恢复执行,而且是从上一次suspend的地方继续执行。从效果上看仿佛是之前的suspend函数调用结束了,并返回了一个'a'。这个时候hi第一行的echo就会输出'a',然后继续执行到第二个supend。
总而言之,就是实现了之前所说的暂停从 api 到 util 的整个调用栈这个效果**。**
等等,这个事情跟
有什么关系呀?为什么他要跳出来反对呢?
Fiber 本质上赋予了 PHP 在用户态临时暂停任意函数调用的特技。如果配合外部事件调度框架(可以完全用PHP实现),就可以实现 swoole 的大多数功能。而且,更关键的点在于,PHP社区现有庞在的类库几乎只做很少的改动就能适配到 fiber 上进而获得并发执行的能力。这跟swoole需要内置各种协程版类库形成了鲜明的对比。最后,因为 fiber 会合并到 php 内核,由官方背书。一旦投票通过,会得到整个社区的支持。届时,swoole 就会被逐步边缘化。
所以,swoole 相关的人就开始反对了。关于反对的内容,建议大家直接看原始讨论邮件,不要相信一家之言
- https://externals.io/message/113430
- https://externals.io/message/11341
- https://externals.io/message/112538
就我观察,swoole是很难合并到 php 内核的。因为swoole太重了,更多是个框架,而不是语言特性。可以说swoole从一开始就没有想好怎么做才能合到php内核。swoole开源快十年了。一直不去解决如何合并到内核的问题。现在有人提供了相关方案,自己就坐不住了。
Fiber作为一个简单的特性,反而为大家提供了无限的想像空间。fiber框架提出一开始,大家基本都在讨论如何改进这个提案,如何让他快速合并到内核。唯有swoole相关的人,采用了完全对立的态度,基本上是为了反对而反对。
fiber本身跟swoole没有竞争关系。说到底fiber只是一个增强型的generator。但是,基于fiber,我们可以用纯php打造一套跟swoole竞争的框架,而且现有的php类库也可以很容易地完成适配,这可能是 swoole 社区最害怕的。
从目前投票来看,fiber 特性可能要被通过了。到时候看 swoole 社区如何应对吧。
作者:匿名用户
链接:https://www.zhihu.com/question/448805077/answer/1777447935\ 来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
大家都是写异步协程框架,Swoole一言不合就AMP不行,ReactPHP不行。那你行你倒是提个提案?难怪国内无论知乎还是论坛Swoole人见人骂,Diss同行,恶心同行的方式真是一绝。
现在开始说为了PHP的发展了,你Swoole搞了那么多年,你咋不想着促进PHP协程的发展呢?你咋不想着提个提案?现在有人抛出来fiber,觉得影响你在国内的一亩三分地了,开始跳起来了?开始装圣人了?
目测fiber最终会通过。
-----------------------------------------------分割线-----------------------------------------
完整的邮件请查看
https://externals.io/message/113430externals.io
与
https://externals.io/message/113419externals.io
此外,不得不说一下,在邮件里,Dan Ackroyd询问
Please could you disclose the commercial interests of the Swoole
maintainers, and the ties to the for profit companies that provide
services implementing Swoole?
Having people vote on RFCs who have ties to companies that provide
services via commercial entities isn't unprecedented. For example
zend.com has had employees who are involved in voting on RFCs, but to
a large extent that was done openly, with all of them using zend.com
email addresses.
而rango回答声称:
We have no commercial purpose on the swoole open source project. This is a purely technical project.
《如何看待Swoole作者声称Swoole没有商业目的,反对Fiber提案毫无私心?》
作者:匿名用户
链接:https://www.zhihu.com/question/448805077/answer/1777447935\ 来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
大家都是写异步协程框架,Swoole一言不合就AMP不行,ReactPHP不行。那你行你倒是提个提案?难怪国内无论知乎还是论坛Swoole人见人骂,Diss同行,恶心同行的方式真是一绝。
现在开始说为了PHP的发展了,你Swoole搞了那么多年,你咋不想着促进PHP协程的发展呢?你咋不想着提个提案?现在有人抛出来fiber,觉得影响你在国内的一亩三分地了,开始跳起来了?开始装圣人了?
目测fiber最终会通过。
-----------------------------------------------分割线-----------------------------------------
完整的邮件请查看
https://externals.io/message/113430externals.io
与
https://externals.io/message/113419externals.io
此外,不得不说一下,在邮件里,Dan Ackroyd询问
Please could you disclose the commercial interests of the Swoole
maintainers, and the ties to the for profit companies that provide
services implementing Swoole?
Having people vote on RFCs who have ties to companies that provide
services via commercial entities isn't unprecedented. For example
zend.com has had employees who are involved in voting on RFCs, but to
a large extent that was done openly, with all of them using zend.com
email addresses.
而rango回答声称:
We have no commercial purpose on the swoole open source project. This is a purely technical project.
作者:吃的
链接:https://www.zhihu.com/question/448978565/answer/1779597343\ 来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
有无私心只有他本人才知道,我们不恶意揣测,但swoole的总总做法,圈内早以是有所耳闻的。关于此次fiber提案,swoole作者的看法大致是
1、因为协程化困难、工作量大,希望把fiber推迟到php9。这显然不是社区要的,目前php处在这尴尬的局面,正是需要强有力技术栈来推动发展,一个协程最小化核心的实现你建议推迟到php9,再过个几年?等社区把一切都安排好了,大部分人都不用php了再上吗?这是何居心啊。
2、因为fiber只是协程最小化核心,普通开发用不上,大家要的是开箱即用、完整式的协程栈。话是没错,但协程需要的几个技术点目前php社区都有,基于进程的io 多路复用、event loop 、协程调度、非阻塞网络库,那一个没有?只是高效率低效率,最佳实践的问题罢了。至少amphp、reactphp 、symfony process都很明确需要这种最小化核心的,别忽悠大伙儿。
3、python、ruby等动态语言在加入fiber后并没有在并发编程上取得多大成绩。这个就仁者见仁 智者见智了,但我们想说的是,“有总比没有好,我可以不用但php不能没有”,这是大家的心声,以后的事谁能预料呢?
总的来说,在协程化生态上目前swoole是走在最前面的,作为一名普通开发,希望swoole能提供你们的宝贵经验,让php能走点更远一点,而不是在这内耗。
我觉得加入好啊.
最小的,其它的实现可以慢慢来
swoole那套太复杂了,为了实现携程加入了太多的新东西
不如直接go
简单的东西也不如workerman之类的解决办法