前言
Swoole
内核团队开设的专栏,会逐渐投入精力写文章介绍Swoole
的开发历程,实现原理,应用实践等,大家可以更好的交流,共同学习,建设PHP
生态。
协程调度
去年Swoole
推出了4.0
版本后,完整的支持PHP
协程,我们可以基于协程实现CSP
编程,身边的开发者惊呼,原来PHP代码还可以这样写。Swoole
的协程默认是基于IO调度,程序中有阻塞会自动让出当前协程,协程的各种优势我们不在这里展开讨论。如果是IO
密集型的场景,可以表现得很不错。但是对于CPU
密集型的场景,会导致一些协程因为得不到CPU
时间片被饿死。
抢占式调度
我们在今年年初就计划实现Swoole
的抢占式调度,以满足实现有些场景下的不均衡调度带来的问题。我们中间经历了几个版本,在这里和大家分享一下开发过程中的动机和解决办法。
我们目的是为了均衡调度每个协程的CPU
时间,比如协程3需要比较长的执行时间,我们必须把协程3的CPU
时间主动中断,而不依赖IO
事件,使得每个协程得到平均的执行时间。
起初,我们的想法是可以从PHP
的循环中自动检测执行实践,若达到限制,可以自动让出当前协程。因为毕竟很少有人一马平川的写出占用很多CPU
的代码,大都通过循环条件来控制。我们hook
循环指令,每次执行循环指令的时候,都来检查协程的执行时间,我们很欣喜的得到了最初的版本。但是这样做比较hack,而且opcode
经过opcache
优化后,情况会变得有些复杂。
后来我们使用PHP
的ticks
机制,也就是在PHP
代码编译期间,注入ticks
指令,可以执行相应的函数,我们可以在这些函数中检测处理协程的时间,达到抢占式的效果,但是这里有一个问题,PHP
的declare(ticks=N)
语法,只对当前脚本范围有效,也就是说项目稍微大点,require
或者include
进来的脚本,并不会自动注入ticks
指令,这样Swoole
开发者几乎是无法接受的。我们也试图给PHP
官方提一个PR,可以在扩展层设置一个全局默认的ticks
,但是官方不愿意采纳我们的提交,因为官方觉得这个功能对性能损耗比较大,而且有可能在