Swoole v4.6.0 版本发布,支持原生 curl 协程客户端

简介: Swoole v4.6.0 版本发布了,同样也是 2021 年的首个版本更新。作为一个 y 版本发布,此次更新也包含了不兼容的修改以及许多的新功能

作为一个 y 版本发布,此次更新也包含了不兼容的修改以及许多的新功能,下面就来看一看都有哪些改动?


向下不兼容改动


  • v4.6.0 版本开始将不再支持 PHP7.1

PHP 官方对于 PHP7.1 的支持也早已在 2019 年底结束。

  • Event::rshutdown() 标记为已弃用,请改用 Coroutine\run

在之前的版本中,如果在index.php中直接使用go创建协程

go(function () {
    var_dump(Co\System::gethostbyname('www.baidu.com'));
});

这样是正常的,但是在此版本中,就会收到废弃警告


PHP Deprecated:  Swoole\Event::rshutdown(): Event::wait() in shutdown function is deprecated in Unknown on line 0

推荐使用Coroutine\run来代替这种方式:


Swoole\Coroutine\run(function () {
    var_dump(Co\System::gethostbyname('www.baidu.com'));
});
Swoole\Coroutine\run(function () {
    go(function () {
        var_dump(Co\System::gethostbyname('www.baidu.com'));
    });
    go(function () {
        var_dump(Co\System::gethostbyname('www.zhihu.com'));
    });
});
  • 默认启用 Coroutine hook

使用了上面所说的Coroutine\run之后,也会迎来一个新的变更:默认启用 Coroutine hook,即自动设置SWOOLE_HOOK_ALL

use Swoole\Runtime;
Swoole\Coroutine\run(function () {
    $flags = Runtime::getHookFlags();
    assert($flags === SWOOLE_HOOK_ALL);
    var_dump($flags);
});

当然也可以自行设置所需要的 flag

Swoole\Coroutine\run(function () {
    $pid = pcntl_fork();
    var_dump($pid);
});
use Swoole\Runtime;
Runtime::setHookFlags(SWOOLE_HOOK_TCP);
Swoole\Coroutine\run(function () {
    $flags = Runtime::getHookFlags();
    assert($flags === SWOOLE_HOOK_TCP);
    var_dump($flags);
});
  • 使用协程时禁用不安全功能,包括 pcntl_fork/pcntl_wait/pcntl_waitpid/pcntl_sigtimedwait



在此版本使用上面的示例代码,你将会得到一个 Warning 错误

PHP Warning:  pcntl_fork() has been disabled for security reasons

  • 移除了 session_id 的最大限制,不再重复

将 Server 的 session_idint24 改为了 int64 ,这样可以持续自增,永不重复。

之前的int24时,session_id大约可以到 1600 万就可能会出现重复的问题。


新增 API & 增强


原生 curl 协程客户端 (SWOOLE_HOOK_NATIVE_CURL)

在这个版本中最大的变化莫过于支持了原生 curl 协程客户端,有什么用呢?

用过 SWOOLE_HOOK_CURL 的小伙伴应该知道,有一些不支持的选项,同时还会因为部分 SDK 的不兼容导致一些错误,例如:

PHP Notice:  Object of class Swoole\Curl\Handler could not be converted to int


PHP Warning: curl_multi_add_handle() expects parameter 2 to be resource, object given

原因是 hook 后的 curl 不再是一个 resource 类型,而是 object 类型。


出现这种问题也建议联系 SDK 方修改代码,因为在 PHP8curl 不再是 resource 类型,而是 object 类型

以及经常使用的阿里云 OSS SDK 也是不支持SWOOLE_HOOK_CURL的,会遇到一些奇奇怪怪的

那么从 v4.6.0 版本开始就可以使用 SWOOLE_HOOK_NATIVE_CURL 代替 SWOOLE_HOOK_CURL,来解决以上提到的问题

使用SWOOLE_HOOK_NATIVE_CURL需要在编译 Swoole 扩展时增加 --enable-swoole-curl 编译参数,开启该选项后将自动设置 SWOOLE_HOOK_NATIVE_CURL,关闭 SWOOLE_HOOK_CURL,同时 SWOOLE_HOOK_ALL 也会包含 SWOOLE_HOOK_NATIVE_CURL


pecl 的 v4.6.0 版本暂时没有增加这个选项,请使用手动编译开启,下个版本中会增加。

编译成功后使用--ri查看信息,就可以看到curl-native


$ php --ri swoole | grep curl
curl-native => enabled

从下面的例子就可以看出两者的不同


Swoole\Runtime::setHookFlags(SWOOLE_HOOK_CURL);
Swoole\Coroutine\run(function () {
    $curl = curl_init();
    var_dump(get_class($curl), (int) $curl);
    //PHP Notice:  Object of class Swoole\Curl\Handler could not be converted to int
    //string(19) "Swoole\Curl\Handler"
    //int(1)
});
Swoole\Coroutine\run(function () {
    $curl = curl_init();
    var_dump($curl, (int) $curl);
    //resource(4) of type (Swoole-Coroutine-cURL Handle)
    //int(4)
});

SWOOLE_HOOK_SOCKETS

增加了对 ext-sockets 的协程化支持

const N = 8;
$GLOBALS['time'] = [];
$s = microtime(true);
Swoole\Runtime::setHookFlags(SWOOLE_HOOK_SOCKETS);
Swoole\Coroutine\run(function () {
    $n = N;
    while($n--) {
        go(function() {
            $s = microtime(true);
            $domain = 'www.baidu.com';
            $sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
            socket_connect($sock, $domain, 80);
            socket_write($sock, "GET / HTTP/1.0\r\nHost: $domain\r\nConnection: close\r\nKeep-Alive: off\r\n\r\n");
            $html = '';
            while(true) {
                $data = socket_read($sock, 8192);
                if ($data == '') {
                    break;
                }
                $html .= $data;
            }
            socket_close($sock);
            $GLOBALS['time'][] = microtime(true) - $s;
        });
    }
});
echo "Done\n";
var_dump(microtime(true) - $s, array_sum($GLOBALS['time']) / 3);


支持 Server 事件回调函数传递对象风格的参数

默认不启用。通过设置 event_object 参数进行启用,以下事件回调将使用对象风格

onConnect 为例,具体内容可参考文档 回调对象

$server->on('Connect', function (Swoole\Server $server, int $fd, int $reactorId) {
    var_dump($fd);
});
$server->set([
    'event_object' => true,
]);
$server->on('Connect', function (Swoole\Server $serv, Swoole\Server\Event $object) {
    var_dump($object);
});

支持重复 header

支持重复设置相同 $key 的 HTTP 头,并且 $value 支持多种类型,如 arrayobjectintfloat,底层会进行 toString 转换,并且会移除末尾的空格以及换行

$http = new Swoole\Http\Server('0.0.0.0', 9501);
$http->on('request', function ($request, $response) {
    $response->header('Test-Value', [
        "a\r\n",
        'd5678',
        "e  \n ",
        null,
        5678,
        3.1415926,
    ]);
    $response->header('Foo', new SplFileInfo('bar'));
});
$http->start();
$ curl -I http://127.0.0.1:9501
HTTP/1.1 200 OK
Test-Value: a
Test-Value: d5678
Test-Value: e
Test-Value: 5678
Test-Value: 3.1415926
Foo: bar
Server: swoole-http-server
Connection: keep-alive
Content-Type: text/html
Date: Wed, 06 Jan 2021 05:16:17 GMT
Content-Length: 39

协程死锁检测

默认开启,可以通过在 Coroutine::set 中设置 enable_deadlock_check 进行关闭

EventLoop 终止后,如果存在协程死锁,底层会输出相关堆栈信息:


===================================================================
 [FATAL ERROR]: all coroutines (count: 1) are asleep - deadlock!
===================================================================
 [Coroutine-2]
--------------------------------------------------------------------
#0  Swoole\Coroutine::printBackTrace() called at [@swoole-src/library/core/Coroutine/functions.php:74]
#1  Swoole\Coroutine\deadlock_check()
#2  curl_getinfo() called at [/mnt/c/code/php/hyperf-skeleton/vendor/aliyuncs/oss-sdk-php/src/OSS/Http/RequestCore.php:492]

更新日志


下面是完整的更新日志

向下不兼容改动

  • 移除了session id的最大限制,不再重复 (#3879) (@matyhtf)
  • 使用协程时禁用不安全功能,包括pcntl_fork/pcntl_wait/pcntl_waitpid/pcntl_sigtimedwait (#3880) (@matyhtf)
  • 默认启用 coroutine hook (#3903) (@matyhtf)

移除

  • 不再支持 PHP7.1 (4a963df) (9de8d9e) (@matyhtf)

废弃

  • Event::rshutdown() 标记为已弃用,请改用 Coroutine\run (#3881) (@matyhtf)

新增 API

  • 支持 setPriority/getPriority (#3876) (@matyhtf)
  • 支持 native-curl hook (#3863) (@matyhtf) (@huanghantao)
  • 支持 Server 事件回调函数传递对象风格的参数,默认不传递对象风格的参数 (#3888) (@matyhtf)
  • 支持 hook sockets 扩展 (#3898) (@matyhtf)
  • 支持重复 header (#3905) (@matyhtf)
  • 支持 SSL sni (#3908) (@matyhtf)
  • 支持 hook stdio (#3924) (@matyhtf)
  • 支持 stream_socket 的 capture_peer_cert 选项 (#3930) (@matyhtf)
  • 添加 Http\Request::create/parse/isCompleted (#3938) (@matyhtf)
  • 添加 Http\Response::isWritable (db56827) (@matyhtf)

增强

  • Server 的所有时间精度都从 int 修改为 double (#3882) (@matyhtf)
  • 在 swoole_client_select 函数里面检查 poll 函数的 EINTR 情况 (#3909) (@shiguangqi)
  • 添加协程死锁检测 (#3911) (@matyhtf)
  • 支持使用 SWOOLE_BASE 模式在另一个进程中关闭连接 (#3916) (@matyhtf)
  • 优化 Server master 进程与 worker 进程通信的性能,减少内存拷贝 (#3910) (@huanghantao) (@matyhtf)

修复

  • 当 Coroutine\Channel 被关闭时,pop 出里面所有的数据 (960431d) (@matyhtf)
  • 修复使用 JIT 时的内存错误 (#3907) (@twose)
  • 修复 port->set() dtls 编译错误 (#3947) (@Yurunsoft)
  • 修复 connection_list 错误 (#3948) (@sy-records)
  • 修复 ssl verify (#3954) (@matyhtf)
  • 修复 Table 递增和递减时不能清除所有列问题 (#3956) (@matyhtf) (@sy-records)
  • 修复使用 LibreSSL 2.7.5 编译失败 (#3962) (@matyhtf)
  • 修复未定义的常量 CURLOPT_HEADEROPT 和 CURLOPT_PROXYHEADER (swoole/library#77) (@sy-records)

内核

  • 默认情况下忽略 SIGPIPE 信号 (9647678) (@matyhtf)
  • 支持同时运行 PHP 协程和 C 协程 (c94bfd8) (@matyhtf)
  • 添加 get_elapsed 测试 (#3961) (@luolaifa000)
  • 添加 get_init_msec 测试 (#3964) (@luffluo)
目录
相关文章
|
5月前
|
PHP 调度 容器
Swoole 源码分析之 Coroutine 协程模块
协程又称轻量级线程,但与线程不同的是;协程是用户级线程,不需要操作系统参与。由用户显式控制,可以在需要的时候挂起、或恢复执行。
78 1
Swoole 源码分析之 Coroutine 协程模块
|
网络协议 关系型数据库 MySQL
swoole协程框架?
Swoole是一个高性能的PHP扩展,可以用于构建异步、并发和高性能的网络应用。它提供了许多底层网络通信和多进程管理的功能,使得开发者可以更轻松地编写高性能的服务器程序。
98 0
Generator(生成器),入门初基,Coroutine(原生协程),登峰造极,Python3.10并发异步编程async底层实现
普遍意义上讲,生成器是一种特殊的迭代器,它可以在执行过程中暂停并在恢复执行时保留它的状态。而协程,则可以让一个函数在执行过程中暂停并在恢复执行时保留它的状态,在Python3.10中,原生协程的实现手段,就是生成器,或者说的更具体一些:协程就是一种特殊的生成器,而生成器,就是协程的入门心法。
Generator(生成器),入门初基,Coroutine(原生协程),登峰造极,Python3.10并发异步编程async底层实现
|
存储 监控 调度
运筹帷幄决胜千里,Python3.10原生协程asyncio工业级真实协程异步消费任务调度实践
我们一直都相信这样一种说法:协程是比多线程更高效的一种并发工作方式,它完全由程序本身所控制,也就是在用户态执行,协程避免了像线程切换那样产生的上下文切换,在性能方面得到了很大的提升。毫无疑问,这是颠扑不破的业界共识,是放之四海而皆准的真理。
运筹帷幄决胜千里,Python3.10原生协程asyncio工业级真实协程异步消费任务调度实践
|
调度 Python
Python3的原生协程(Async/Await)和Tornado异步非阻塞
我们知道在程序在执行 IO 密集型任务的时候,程序会因为等待 IO 而阻塞,而协程作为一种用户态的轻量级线程,可以帮我们解决这个问题。协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存,在调度回来的时候,恢复先前保存的寄存器上下文和栈。因此协程能保留上一次调用时的状态,即所有局部状态的一个特定组合
Python3的原生协程(Async/Await)和Tornado异步非阻塞
|
关系型数据库 MySQL 大数据
利用原生swoole协程批量向数据库插入1000W条数据
一直再听说高并发,没有体验过真正的高并发,这次体验下1000w数据表(不知道算不算大数据),刚好体验下mysql索引的性能;
199 0
|
前端开发 NoSQL Java
使用 Kotlin + WebFlux/RxJava 2 实现响应式以及尝试正式版本的协程
使用 Kotlin + WebFlux/RxJava 2 实现响应式以及尝试正式版本的协程
825 0
使用 Kotlin + WebFlux/RxJava 2 实现响应式以及尝试正式版本的协程
|
网络性能优化 PHP
PHPMQTT v1.3.1 版本发布,MQTT 协议解析 & 协程客户端
一些用户遇到了握手失败的情况,联系了我之后进行了测试
220 0
|
网络性能优化 PHP
PHPMQTT v1.3.0 版本发布,MQTT 协议解析 & 协程客户端
v1.3.0 版本新增了一个 Message 类簇
217 0