Swoole协程抢占式调度

栏目: PHP · 发布时间: 5年前

内容简介:Swoole内核团队开设的专栏,会逐渐投入精力写文章介绍Swoole的开发历程,实现原理,应用实践等,大家可以更好的交流,共同学习,建设PHP生态。去年我们在今年年初就计划实现

前言

Swoole内核团队开设的专栏,会逐渐投入精力写文章介绍Swoole的开发历程,实现原理,应用实践等,大家可以更好的交流,共同学习,建设 PHP 生态。

协程调度

去年 Swoole 推出了4.0版本后,完整的支持 PHP 协程,我们可以基于协程实现 CSP 编程,身边的开发者惊呼,原来PHP代码还可以这样写。 Swoole 的协程默认是基于IO调度,程序中有阻塞会自动让出当前协程,协程的各种优势我们不在这里展开讨论。如果是 IO 密集型的场景,可以表现得很不错。但是对于 CPU 密集型的场景,会导致一些协程因为得不到 CPU 时间片被饿死。

抢占式调度

我们在今年年初就计划实现 Swoole 的抢占式调度,以满足实现有些场景下的不均衡调度带来的问题。我们中间经历了几个版本,在这里和大家分享一下开发过程中的动机和解决办法。

起初,我们的想法是可以从 PHP 的循环中自动检测执行实践,若达到限制,可以自动让出当前协程。因为毕竟很少有人一马平川的写出占用很多 CPU 的代码,大都通过循环条件来控制。我们 hook 循环指令,每次执行循环指令的时候,都来检查协程的执行时间,我们很欣喜的得到了最初的版本。但是这样做比较hack,而且 opcode 经过 opcache 优化后,情况会变得有些复杂。

后来我们使用 PHPticks 机制,也就是在 PHP 代码编译期间,注入 ticks 指令,可以执行相应的函数,我们可以在这些函数中检测处理协程的时间,达到抢占式的效果,但是这里有一个问题, PHPdeclare(ticks=N) 语法,只对当前脚本范围有效,也就是说项目稍微大点, require 或者 include 进来的脚本,并不会自动注入 ticks 指令,这样 Swoole 开发者几乎是无法接受的。我们也试图给 PHP 官方提一个 PR ,可以在扩展层设置一个全局默认的 ticks ,但是官方不愿意采纳我们的提交,因为官方觉得这个功能对性能损耗比较大,而且有可能在 PHP8 移除 这个功能。其实经过实测这个性能损耗并不大,而且我们已经在生产环境验证,并取得了显著的效果,即可以让出某些 CPU 密集的逻辑部分,使得服务整个相应时间更加均衡。

想要做抢占式调度,对于 PHP 来说,有两个途径

  1. 单线程的 PHP 的执行流,通过执行指令做文章,可以在 PHP 执行流程中注入逻辑,以检查执行时间,再急上 Swoole 的协程能力,可以在不同的协程中切换,以达到抢占 CPU 的目的。
  2. 考虑开线程,负责检查当前执行协程执行时间。

经过以上办法的尝试,注入指令的路数基本是无法得到官方的支持,我们只能另谋出路,多开一个线程,只负责检查当前协程。具体的做法是,利用 PHP-7.1.0 引入的 VM interrupt 机制,默认每隔5ms检查一下当前协程是否达到最大执行时间,默认为10ms,如果超过,则让出当前协程,达到被其他协程 抢占 的目的。

来一段代码

<?php
Co::set(['enable_preemptive_scheduler' => 1]);
$start = microtime(1);
echo "start\n";
$flag = 1;

go(function () use (&$flag) {
    echo "coro 1 start to loop\n";
    $i = 0;
    for (;;) {
        if (!$flag) {
            break;
        }
        $i++;
    }
    echo "coro 1 can exit\n";
});
    
$end = microtime(1);
$msec = ($end - $start) * 1000;
echo "use time $msec\n";
go(function () use (&$flag) {
    echo "coro 2 set flag = false\n";
    $flag = false;
});
echo "end\n";

//输出结果
start
coro 1 start to loop
use time 11.121988296509
coro 2 set flag = false
end
coro 1 can exit

可以发现,代码逻辑可以从第一个协程的死循环中自动 yield 出来,执行第二个协程,如果没有这个特性,第二个协程永远不会被执行,导致被饿死。而这样做,第二个协程可以顺利被执行,最后执行结束后,第一个协程也会接着继续往下执行。达到我们的第二个协程主动 抢占 第一个协程 CPU 的效果。

这个特性在生产环境非常有用,尤其是对于实时系统或者响应时间比较敏感的场景。

最后

感谢大家对 Swoole 的长期支持和关注。


以上所述就是小编给大家介绍的《Swoole协程抢占式调度》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

生命3.0

生命3.0

[美] 迈克斯·泰格马克 / 汪婕舒 / 浙江教育出版社 / 2018-6 / 99.90元

《生命3.0》一书中,作者迈克斯·泰格马克对人类的终极未来进行了全方位的畅想,从我们能活到的近未来穿行至1万年乃至10 亿年及其以后,从可见的智能潜入不可见的意识,重新定义了“生命”“智能”“目标”“意识”,并澄清了常见的对人工智能的误解,将帮你构建起应对人工智能时代动态的全新思维框架,抓住人类与人工智能共生演化的焦点。 迈克斯·泰格马克不仅以全景视角探讨了近未来人工智能对法律、战争、就业和......一起来看看 《生命3.0》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

随机密码生成器
随机密码生成器

多种字符组合密码

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码