Swow 是一个使用 PHP 和 C 编写的高性能纯协程网络通信引擎。它致力于使用最小C核心及多数PHP代码以支持PHP高性能网络编程。
协程
Swow实现了一套有史以来最完整的PHP协程模型,它全面释放了PHP的真正实力,使得开发者可以做到以往难以想象的事情。
高性能
支持每秒百万次上下文切换。且由于Swow支持纯C协程与PHP协程混合运行,两者之间仅进行C栈单栈上下文切换,且Swow的事件调度器就是纯C协程,因此大部分协程切换都是单栈切换,其切换速度远超C + PHP的双栈切换。
高可控
现在协程可以使得PHP虚拟机像一个迷你操作系统一样,其中运行的协程近似于操作系统的进程/线程,开发者能够以超细粒度任意操控这些协程,如查看所有协程的运行状态、attach进入协程、单步调试跟踪、查看协程栈乃至每个栈帧、查看或修改协程内变量、中断协程或杀死协程等。
基于这样的可控性,开发者还可以借助WatchDog组件对陷入死循环或处于IO阻塞的协程进行警报、中断、让出甚至杀死等多种处理,以避免个别协程影响到程序整体稳定性。
并且,其与进程设计哲学的契合性,也决定了协程应用的健壮性极佳。正如单个进程的崩溃不会导致整个系统的崩溃一样,单个协程的崩溃也不会导致整个进程的崩溃,并且得益于PHP强大的异常机制和资源管理能力,与协程绑定的相关资源都能被安全地释放。你无需再为未捕获的异常殚精竭力,也不要再去捕获Throwable,遇到未知的错误请Let it crash。
易兼容
无需创建协程也无需判断是否在协程环境,在Swow中,所及之处,皆为协程,打开编辑器,即刻就可以开始书写你的代码!这也意味着它将更好地兼容已有的生态,我们再也不需要为PHPUnit重写一个由协程包裹的入口。
事件驱动
Swow基于协程事件库libcat开发,libcat又基于异步事件库libuv开发,得益于此,我们拥有了经过工业级验证的事件循环驱动,并且它支持几乎所有常见操作系统。因此,Swow也是首个能在原生Windows平台运行,且通过IOCP驱动的PHP协程网络编程引擎。由于libuv遵循Proactor模型,不久后我们又可以在Linux下免费获得由新特性io_uring带来的可观性能提升。
PHP可编程性
最小C核心意味着底层不再包揽所有事务,而是仅提供最细粒度的基础接口,即扩展层不再像是一个框架(framework),而更像是库(library),这将使PHP的编程能力最大化。
且随着PHP8和JIT的到来,我们为什么还要大量使用C或是C++来完成那些PHP也能做到的工作呢?更多地使用PHP而不是C、C++,也契合了PHP内核的未来发展方向。
更强的可编程性也带来了更多的可能性。试想一下,曾经你想写一个简单的WAF程序,通过检测IP或是解析HTTP头部来实施一些鉴权操作,但当你在回调中拿到请求对象时,底层已经替你完成了整个请求的接收,庞大的HTTP报文解析或许已经损害了你的程序性能。
但现在,Swow提供的Buffer模块使得PHP可以像C一样精细地进行内存管理,结合Socket模块和一些协议解析器,它允许你掌握小到每个字节的接收和解析,或许在未来开发者完全可以使用Swow编写高性能的网关程序,一切都可以通过PHP编程的方式改变,且它们全都是内存安全的。
现代化
面向对象
Swow在面向对象的道路上和PHP的进化路线如出一辙,不管是早期的PHP或是其它有悠久历史的扩展,对于API的设计大都是面向过程的,而经过多年发展,PHP社区早已是面向对象的天下,PHP也为内建API的面向对象支持而不懈努力。面向对象使得我们可以更好地基于Swow库进行二次封装,我们可以直接继承内部类并实现PSR接口以0成本支持PSR规范,这在实际应用中让我们的程序性能得到了极大的提升。
拥抱异常
Swow在错误处理方面和PHP的改革理念也是一致的,PHP8干掉了大量的notice、warning、error,转而使用基于异常机制的Error/Exception,极大地增强了程序的健壮性(不要再让错误的程序继续运行下去)。
曾经我们要求开发者在每个IO操作后检查返回值,否则程序就可能陷入非预期的错误状态中,这样的编码方式,无异于是一种历史的倒退,干掉if ($err != null)
,拥抱异常机制,才是正确的发展道路。
而基于上述改进,我们现在能以链式调用的方式书写代码,使代码变得更加简洁且富有节奏感。
绿色增强
Swow和Opcache非常相似,两者都允许开发者在几乎不改变代码的情况下,仅通过开启扩展就能使得应用程序获得免费大量的能力提升。即保证相同的代码具有一致的输出结果,但它们在底层所运行的指令或是系统调用可能不尽相同。
因此,Swow理所当然地支持所有SAPI(CLI,FPM等)。但需要注意的是,受制于FPM的模型,你无法简单地通过开启Swow直接获得性能上的提升,但它仍能在功能上对FPM进行增强(如并发请求接口、执行异步任务等)。
此外,即使是传统的同步阻塞应用,也可以使用Swow生态下的应用组件,如使用Swow提供的Debugger工具对程序进行断点调试分析等。
线程安全
Swow支持在PHP的ZTS(Zend Thread Safety)版本下运行,即基于内存隔离的多线程支持。
这意味着它可以和parallel、pthreads等多线程扩展很好地结合使用,但Swow本身不会提供任何多线程支持。
学习成本
Swow是否又是一个全新的轮子?答案是否定的。如果你熟悉如Swoole这样的协程库,那么你几乎无需任何学习成本就可以上手Swow,反之,Swow可能更易于上手,因为它更现代化,面向对象、异常机制、纯协程、零异步回调等一切特性都只为了让你的编码更加从容优雅。
此外,如果你的项目使用了社区流行协程框架,那么它的升级成本可能仅和由PHP5升级到PHP7相当,并且你还可能因此获得20%的免费性能提升和可观的内存占用缩减。
现阶段的Swow非常适合技术先驱和极客们尝鲜,它将走在PHP异步协程技术的前沿,拥抱变化甚至引领变化。
编程理念
Swow遵循CSP的并发模型,而不是Callback模型。在实现上来说,协程是异步回调的超集,通过创建新的协程可以模拟异步回调,而反之则不行。
两个问题
Swoole/Swow对于并发网络编程支持的技术抉择终结于克服两个巨大的现实技术难题:一个是回调地狱,另一个是生态。
一个选择
PHP异步网络编程技术的奠基者Swoole早期就尝试了异步回调模型,但在实际开发中该技术常常制造出难以维护的代码,而协程技术可以解决嵌套异步回调代码可维护性极差的问题并减轻开发者的心智负担。
而选择有栈协程技术而不是无栈协程,则是为了更好地复用已有的庞大的PHP生态。在这一点上,其它已知的异步事件库都走在了与PHP原有生态更加割裂的方向上。在Swow中,我们可以完美复用大量的PHP网络设施库及基于它们开发的海量PHP包支持而无需修改任何代码。
协程思维
此外,在纯协程的编程理念中,我们不应再以异步回调的方式去思考代码实现。以实现一个有限次数的定时任务为例,我们不再应该先想到异步定时器,使用异步定时器实现我们需要借助全局变量上下文保存状态并对其计数,在执行指定次数后删除定时器,而在协程的方式下,我们只是简单地新建一个协程并for循环指定次数,每次循环通过sleep()挂起指定的时间,再执行任务。
显然协程方式的好处是代码直观且上下文信息不会丢失。异步定时器的每次执行都需要一个全新的回调堆栈,在异步混合协程的实现下,每次回调都必须创建一个新的协程,此时纯协程实现的各种好处就不言而喻了。