看很多ThinkPHP框架的程序商城等系统,现在都用PHP+Swoole协程来运行。在说Swoole前我们先了解下传统PHP模式。
互联网发展早期,大部分项目的业务逻辑并没有那么复杂,技术生态相对比较简单,也没有 Composer 这样的包管理系统,程序代码足够简单。大部分 PHP 项目没有框架,或者使用一些轻量级框架,比如 Yii、CI、Yaf 、ThinkPHP 等,这种场景下 PHP-FPM/Apache+Mod-PHP 性能是足够的。短生命周期反而带来了非常大的优势,1. 热加载,2. 不会产生内存泄漏,这两个特性使得 PHP 超越其他编程语言,成为 Web 领域最受欢迎的服务器端技术。
在做好 Cache ,优化好 SQL 的前提下,使用 PHP-FPM 单机可以提供数百甚至上千 QPS 的并发处理能力。即便是像 Facebook 、Baidu 这种量级企业的核心系统也是游刃有余的。
随着时代的进步发展,互联网系统变得越来越复杂,程序的业务逻辑越来越重,这时简单的框架或者封装,已经不能满足需求,功能更强大、设计感更强、封装层次更深、拥有微服务/服务治理能力的重量级框架越来越受到追捧,比如 Java 的 Spring,PHP 的 Laravel、Symfony ,这些框架提供的功能更丰富,框架设计更加灵活,随之而来的是运行这些框架的程序也需要更多的计算资源。
一个大型的复杂项目可能在启动时:
由于 PHP-FPM 是短生命周期的模式,除了程序文件的编译可以使用 opcache 解决之外,其他的都是在请求开始执行,请求结束后全部释放,这就带来了严重的性能损耗。以 Laravel 框架为例,只是一个 hello world 级别的接口,压测时 QPS 可能 100 都不到。如果程序再复杂点,恐怕只有几十 QPS 的程度。这样的并发能力无法支撑拥有海量用户的大型互联网系统,导致了 PHP 逐渐被 Java、Golang 编程语言替代,走向没落。使得 PHP 只被应用在一些历史遗留项目,或者访问量不高的管理后台程序 。
而 Swoole 这样的技术正是解决 PHP 短生命周期性能问题的利器,基于 Swoole 提供的对象内存常驻能力,使得 Laravel、Symfony 这样重量级框架的项目,也可以得到良好的性能。
Laravel 官方也意识到了这个问题,适时地推出了 Laravel Octane ,基于 Swoole 实现常驻内存的 Web 服务。国内也诞生了 Swoft、 Hyperf 这样完全基于 Swoole 协程的 PHP 框架。ThinkPHP 框架也提供了 Swoole 协程的支持。还有很多基于 Swoole 实现的优秀框架,这里就不一一列举了。
其实除了 Swoole 之外,PHP 生态还有 Workerman 这样的优秀国产开源项目、纯 PHP 实现的常驻内存服务框架,也可以解决上面 PHP-FPM 性能问题。为什么还要使用 Swoole ?这就要说 Swoole 提供的协程了。
一个 Web 程序的并发能力,除了计算性能之外,还有一个不可忽视的问题,那就是 IO 等待导致的阻塞,程序代码中可能会查询 MySQL,或者使用 curl 请求其他外部接口,甚至直接通过外网请求第三方服务。若响应时间稳定可控,在极短的时间内可以返回,这是没问题的。不会影响整体的并发能力。但是如果一旦出现响应超时、变慢,整个系统可能就会发生雪崩。并发能力直线下降。举个例子,一个接口请求一个外部接口,正常情况下 100ms 可以返回,如果程序启动了 200 个进程,并发能力就是 2000 QPS,如果出现某些突发原因,响应时间下降到 1s ,这时并发能力就降低到了 200 QPS,相当于并发能力下降了 10 倍。这就可能会带来灾难性后果。这样的问题光是我自己,在生产环境遇到的就不是一次两次了。
而基于 Swoole 协程模式的 Web 程序不会有这种问题。单个 IO 等待过长只会影响当前一个请求所在的协程,其他请求可以继续向下执行。所以不会出现并发能力严重下降。协程的开销极小,多一个协程不过是多一点点内存占用,启动几万甚至几十万个协程也不会给系统带来多大的负担。
PHP 官方在 8.1 也推出了协程的实现,也就是 Fiber 特性,ReactPHP 和 Amphp 基于 Fiber 实现了一些类似于 Swoole 的功能。这样 PHP 开发者又多了一个选择,那为什么还要继续用 Swoole 呢?
这就要说到 Swoole 提供的 Runtime Hook 技术了。Swoole 除了提供协程特性之外,还提供了一个非常强大的功能:Runtime Hook,它会在运行时自动替换 PHP 语言内置的阻塞 IO 函数,使得这些函数变成非阻塞、协程调度的,包括最常用的 sleep、gethostbyname、proc_open、shell_exec、mysqli、pdo_mysql、curl、redis、file_get_contents、stream、sockets、soap 这些扩展/函数全部支持。使得 PHP 代码一行不用改,就能变为协程版本。甚至十几年前的代码拿过来都可以运行在 Swoole 协程模式下,非阻塞地运行。这是革命性的,使得 PHP 生态获得了重生,可以像 Golang 这样拥有完整的协程生态。
在 Swoole4 尤其是 4.4 版本之后,Swoole 协程变得非常稳定了。一方面是底层进行了大量重构,代码质量得到了较大的提高,也裁剪掉了各种各样与核心无关的组件,另一方面增加了数千个单元测试脚本,测试覆盖率提升到 70% 以上。这两年已经已经有大量生产项目使用了 Swoole4 协程,验证了它的稳定性。可以看到 Swoole 项目在 Github 上的发版节奏明显放缓了,从过去的一周一个版本到现在2-3个月一个版本,开发团队主要精力是在解决一些长尾问题。进入了一个稳步爬升期。