热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

关于PHP8.1的FiberRFC

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

最新的 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 是这样的:

在这里插入图片描述

实际上 PHP 8.1 Fiber 是这样的:

在这里插入图片描述

动态语言中除了 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 也是采用这个模式。这样做最大的问题是,

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

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

在这里插入图片描述

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 切换至其他可执行的协程。

以上程序会交替执行,每个协程最大执行时间不超过 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 协程
图

创建 PHP 协程
图

在 PHP 代码中创建协程
图

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

点关注,不迷路

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


推荐阅读
  • Python全局解释器锁(GIL)机制详解
    在Python中,线程是操作系统级别的原生线程。为了确保多线程环境下的内存安全,Python虚拟机引入了全局解释器锁(Global Interpreter Lock,简称GIL)。GIL是一种互斥锁,用于保护对解释器状态的访问,防止多个线程同时执行字节码。尽管GIL有助于简化内存管理,但它也限制了多核处理器上多线程程序的并行性能。本文将深入探讨GIL的工作原理及其对Python多线程编程的影响。 ... [详细]
  • javax.mail.search.BodyTerm.matchPart()方法的使用及代码示例 ... [详细]
  • 多线程基础概览
    本文探讨了多线程的起源及其在现代编程中的重要性。线程的引入是为了增强进程的稳定性,确保一个进程的崩溃不会影响其他进程。而进程的存在则是为了保障操作系统的稳定运行,防止单一应用程序的错误导致整个系统的崩溃。线程作为进程的逻辑单元,多个线程共享同一CPU,需要合理调度以避免资源竞争。 ... [详细]
  • 本文介绍了如何使用Python的Paramiko库批量更新多台服务器的登录密码。通过示例代码展示了具体实现方法,确保了操作的高效性和安全性。Paramiko库提供了强大的SSH2协议支持,使得远程服务器管理变得更加便捷。此外,文章还详细说明了代码的各个部分,帮助读者更好地理解和应用这一技术。 ... [详细]
  • Python多线程编程技巧与实战应用详解 ... [详细]
  • 本文深入解析了Java 8并发编程中的`AtomicInteger`类,详细探讨了其源码实现和应用场景。`AtomicInteger`通过硬件级别的原子操作,确保了整型变量在多线程环境下的安全性和高效性,避免了传统加锁方式带来的性能开销。文章不仅剖析了`AtomicInteger`的内部机制,还结合实际案例展示了其在并发编程中的优势和使用技巧。 ... [详细]
  • 在Python多进程编程中,`multiprocessing`模块是不可或缺的工具。本文详细探讨了该模块在多进程管理中的核心原理,并通过实际代码示例进行了深入分析。文章不仅总结了常见的多进程编程技巧,还提供了解决常见问题的实用方法,帮助读者更好地理解和应用多进程编程技术。 ... [详细]
  • 在Python网络编程中,多线程技术的应用与优化是提升系统性能的关键。线程作为操作系统调度的基本单位,其主要功能是在进程内共享内存空间和资源,实现并行处理任务。当一个进程启动时,操作系统会为其分配内存空间,加载必要的资源和数据,并调度CPU进行执行。每个进程都拥有独立的地址空间,而线程则在此基础上进一步细化了任务的并行处理能力。通过合理设计和优化多线程程序,可以显著提高网络应用的响应速度和处理效率。 ... [详细]
  • 深入解析Netty:基础理论与IO模型概述
    深入解析Netty:基础理论与IO模型概述 ... [详细]
  • 从无到有,构建个人专属的操作系统解决方案
    操作系统(OS)被誉为程序员的三大浪漫之一,常被比喻为计算机的灵魂、大脑、内核和基石,其重要性不言而喻。本文将详细介绍如何从零开始构建个人专属的操作系统解决方案,涵盖从需求分析到系统设计、开发与测试的全过程,帮助读者深入理解操作系统的本质与实现方法。 ... [详细]
  • 开发心得:利用 Redis 构建分布式系统的轻量级协调机制
    开发心得:利用 Redis 构建分布式系统的轻量级协调机制 ... [详细]
  • 为何Serverless将成为未来十年的主导技术领域?
    为何Serverless将成为未来十年的主导技术领域? ... [详细]
  • 在本次分享中,我将详细介绍我的网络数据爬取项目,包括使用Scrapy-Redis进行分布式爬取的具体配置和多台机器的协同工作。此外,还将探讨从Scrapy到Scrapy-Redis的迁移过程,以及在实际爬取过程中遇到的各种反爬虫策略及其应对方法。 ... [详细]
  • 当前,众多初创企业对全栈工程师的需求日益增长,但市场中却存在大量所谓的“伪全栈工程师”,尤其是那些仅掌握了Node.js技能的前端开发人员。本文旨在深入探讨全栈工程师在现代技术生态中的真实角色与价值,澄清对这一角色的误解,并强调真正的全栈工程师应具备全面的技术栈和综合解决问题的能力。 ... [详细]
  • 这篇文章将揭示 Vue 和 React 组件库中五个鲜为人知的强大工具。这些工具均以纯 JavaScript 实现,功能卓越。其中,async-validator 是一个数据验证插件,不仅预置了 URL 和电子邮件的验证规则,还支持异步验证功能。 ... [详细]
author-avatar
国邮国旅刘峰
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有