Runtime

4.1.0版本中,底层增加一个新的特性,可以在运行时动态将基于php_stream实现的扩展、PHP网络客户端代码一键协程化。底层替换了ZendVM Stream的函数指针,所有使用php_stream进行socket操作均变成协程调度的异步IO

目前有PHP原生RedisPDOMySQLi协程化的支持。

4.1版本仅支持tcpunix两种stream类型
4.2版本增加了对udpudgunixssltls类型的支持
4.2.3版本以前存在FILE_HOOK覆盖include/requireBUG, 请通过Swoole\Runtime::enableCoroutine(true, SWOOLE_HOOK_ALL ^ SWOOLE_HOOK_FILE);的方式屏蔽file hook
4.4.0 起支持 SWOOLE_HOOK_CURL, 暂未实验性选项, 默认不开启

函数原型

function Runtime::enableCoroutine(bool $enable = true, int $flags = SWOOLE_HOOK_ALL);
function Runtime::enableCoroutine(int $flags = SWOOLE_HOOK_ALL);
  • $enable:打开或关闭协程
  • $flags:选择要Hook的类型,可以多选,默认为全选。仅在$enable = true时有效

$flags参数在4.2或更高版本可用,请参考:开关选项
4.3.2或更高版本中, 支持上述第二种原型所示的方式调用(即第一个参数为flag)

配置选项

4.4版本中可以使用Coroutine::setCoroutine\Scheduler::set方法中设置hook_flags选项设置运行时协程Hook

Corouitne::set([
    'hook_flags' => SWOOLE_HOOK_ALL,
]);

可用列表

  • redis扩展
  • 使用mysqlnd模式的pdo_mysqlmysqli扩展,如果未启用mysqlnd将不支持协程化
  • soap扩展
  • file_get_contentsfopen
  • stream_socket_client (predis, php-amqplib)
  • stream_socket_server
  • stream_select (需要4.3.2以上版本)
  • fsockopen
  • proc_open (需要4.4.0以上版本)

不可用列表

  • mysql:底层使用libmysqlclient
  • mongo:底层使用mongo-c-client
  • pdo_pgsql
  • pdo_ori
  • pdo_odbc
  • pdo_firebird

实验性列表

  • curl:底层使用libcurl (即不能使用curl驱动的Guzzle), 但可通过显式设置SWOOLE_HOOK_CURL开启实验性的HOOK

使用实例

Swoole\Runtime::enableCoroutine();

go(function () {
    $redis = new redis;
    $retval = $redis->connect("127.0.0.1", 6379);
    var_dump($retval, $redis->getLastError());
    var_dump($redis->get("key"));
    var_dump($redis->set("key", "value2"));
    var_dump($redis->get("key"));
    $redis->close();
});

方法摆放位置

调用方法后当前进程内全局生效, 一般放在整个项目最开头以获得100%覆盖的效果 一旦开启,相关函数就无法在非协程环境使用

注意: 不建议放在onRequest等回调中开启, 会多次调用造成不必要的调用开销.



  • twosee

    火钳刘明

  • 宇润

    火钳刘明

  • 小虾米会飞

    file_get_contents不支持https

  • S
    S

    火钳刘明

  • dove

    使用了这个功能 用jmeter做了个并发测试 测试代码很简单 就用mysqli插入一个记录 分别测试不用swoole 发现吞吐量是120多,用swoole并开启Swoole\Runtime::enableCoroutine() 吞吐量是260多 用swoole不开启Swoole\Runtime::enableCoroutine() 吞吐量是 520多 不知道是什么原因 而且开启了Swoole\Runtime::enableCoroutine()还有1%报错 不知道谁能解释一下呢

  • Rango

    @dove 使用连接池。协程之间不可公用连接。Swoole\Runtime::enableCoroutine() 后是并发的创建,与php-fpm完全不同,php-fpm是串行的。

  • 小黑屋

    @Rango 每个请求创建一个PDO实例用于查询(协程之间不共用连接),开启Swoole\Runtime::enableCoroutine()的并发确实没有不开启高:


    //在收到一个完整的Http请求后,会回调此函数 $http->on('request', function ($request, \Swoole\Http\Response $response) use ($http) { /** * PDO开启\Swoole\Runtime::enableCoroutine(); * 10次取平均值 * ./ab.exe -c 100 -n 1000 http://192.168.56.1/ * Requests per second: 701.17 [#/sec] (mean) * Time per request: 142.618 [ms] (mean) * Time per request: 1.426 [ms] (mean, across all concurrent requests) * Transfer rate: 194.47 [Kbytes/sec] received */ /** * PDO未开启\Swoole\Runtime::enableCoroutine(); * 10次取平均值 * ./ab.exe -c 100 -n 1000 http://192.168.56.1/ * Requests per second: 1768.12 [#/sec] (mean) * Time per request: 56.557 [ms] (mean) * Time per request: 0.566 [ms] (mean, across all concurrent requests) * Transfer rate: 490.38 [Kbytes/sec] received */ //MYSQL8.0 $pdo = new \PDO('mysql:host=127.0.0.1;dbname=test;charset=utf8', 'root', 'ABCD1234@'); $sth = $pdo->prepare("select * from user where id=24391"); $sth->execute(); $res = $sth->fetchAll(); $response->end(var_export($res,true)); });

  • 忘却浮生

    @小黑屋 上面说了···你要用连接池

  • 小黑屋

    @忘却浮生 我用了Channel做连接池确实是正常了,没理解语义,而且这里有个要点,用协程客户端实现的连接池比PDO实现的连接池+开启\Swoole\Runtime::enableCoroutine()快不少

  • 晒了PI_MY

    楼上各位大佬,请问有连接池实现的参考吗,想学习一下怎么写

  • Don

    @小黑屋 channel连接池跟request这里是如何交互实现的呢?

  • jichengyang

    用 time php Test.php 测试了开启协程后的这协io函数哈 real user sys 的时间都要比没开要高些 是协程切换的消耗么?

  • 共共

    新人避坑指南: enableCoroutine后,关于soap扩展的一个坑大家注意: enable_coroutine = true, worker_num > 1 情况下如果动了soap默认配置(如关wsdl缓存或者改缓存有效期),会出问题

    //wsdl缓存一定要开启; 否则会出现 [WSDL] SOAP-ERROR: Parsing WSDL: Couldn't load from 'XXX' : failed to load external entity "XXX" 错误 ini_set("soap.wsdl_cache_enabled", 1);

    //缓存一定要存在硬盘,而不是内存; 否则 worker进程会出现异常退出现象 ini_set("soap.wsdl_cache", WSDL_CACHE_DISK);

    //缓存时间要足够长; 否则缓存失效后worker进程同样会出现异常退出现象 ini_set("soap.wsdl_cache_ttl", 0xFFFFFFFF);

    估计是由于缓存失效后soap会写某块共享内存导致的。 SOAP的默认配置前两项都没问题,最后一项ttl配置默认是1天,缓存失效后出问题很难察觉。