Runtime::enableCoroutine

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/require的BUG, 请通过Swoole\Runtime::enableCoroutine(true, SWOOLE_HOOK_ALL ^ SWOOLE_HOOK_FILE);的方式屏蔽file hook

函数原型

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

$flags参数在4.2或更高版本可用,请参考:开关选项

可用列表

  • redis扩展
  • 使用mysqlnd模式的pdomysqli扩展,如果未启用mysqlnd将不支持协程化
  • soap扩展
  • file_get_contentsfopen
  • stream_socket_client (predis)
  • stream_socket_server
  • fsockopen

不可用列表

  • mysql:底层使用libmysqlclient
  • curl:底层使用libcurl (即不能使用CURL驱动的Guzzle
  • mongo:底层使用mongo-c-client
  • pdo_pgsql
  • pdo_ori
  • pdo_odbc
  • pdo_firebird

使用实例

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%覆盖的效果, 协程内外会自动切换模式, 不影响PHP原生环境使用.

注意: 不建议放在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这里是如何交互实现的呢?