龙空技术网

PHP中异步执行http请求(Guzzle And Curl)

无忧的松鼠u1 162

前言:

当前各位老铁们对“curlphp”大概比较关切,大家都需要分析一些“curlphp”的相关资讯。那么小编在网上搜集了一些关于“curlphp””的相关内容,希望看官们能喜欢,咱们一起来了解一下吧!

我认为没有使用线程的任何好的用例 在 PHP 中,但 还有一些工具(swoole、workerman以及衍生的工具)不容忽视!可以在 PHP 中异步执行(至少一件事)——开箱即用!

考虑这样一个例子:

你想向外部(一个或 4 个不同的)服务发出 4 个请求——所有 4 个请求的“时间成本”是多少?

有两种可能情况:

我们可以同步处理请求——那么总时间就是所有后续请求成本的总和我们可以异步完成它们——总时间将等于最长请求的时间

请参见下图以可视化场景,假设我们必须发出 4 个请求,其中:

第一个的响应是立即的(我们可以忽略延迟)第二个响应在 1 秒后出现第三次的回应——2秒第四次的回应——3秒

什么适用于 PHP?

让我们检查一下(您可以在下面找到所执行测试的解释,结果用了3s多):

docker compose run phpSequence: 0 .. 3...endWoke up after 0 second(s) of sleep!Woke up after 1 second(s) of sleep!Woke up after 2 second(s) of sleep!Woke up after 3 second(s) of sleep!real    0m3.099s
这里究竟测试了什么?

作为“外部服务”,我使用了我称之为“睡眠服务器”的工具——你可以向该工具(服务)发送一个请求,并将秒数作为参数,它会在请求的时间后做出响应(更多信息和示例 可以在项目的自述文件中找到)。

测试PHP代码:

$client = new GuzzleHttp\Client();for($i = $sequenceStart; $i <= $sequenceStop; $i++) {    Utils::task(        fn() => $client->getAsync("{$sleepingUrl}/{$i}")            ->then($printResponse)            ->wait()    );}echo 'end' . PHP_EOL;

所有这些都被 docker-compose 绑定了:

services:  sleeping:    build: ";  php:    build: ./php    environment:      SLEEPING_URL: ";    depends_on:      - sleeping    ...

代码示例:

让我们将 PHP(php:8.1 )结果与 JS 代码进行比较。

代码的JS版本:

for (let i=sequenceStart; i<=sequenceStop; i++) {    fetch(`${sleepingUrl}/${i}`)        .then(response => response.text())        .then(result => console.log(result))}console.log('end')

测试

 docker compose run jsSequence: 0 .. 3      ...endWoke up after 0 second(s) of sleep!Woke up after 1 second(s) of sleep!Woke up after 2 second(s) of sleep!Woke up after 3 second(s) of sleep!real    0m3.182s

结果几乎相同!

事情要解释

我认为有两个(关于 PHP 代码):

为什么要执行这些请求? (请注意脚本输出中打印的“end”字符串——它是脚本的最后一行!)

为什么这些请求是异步执行的?

第一个有点奇怪——我的意思是——答案(至少对我而言)。 让我解释:

我们从 Util::task() 开始,因为它已在我的示例中使用 - 检查该方法(它在 GuzzleHttp\Promise 命名空间中声明)! 你可以在那里找到类似 $queue = self::queue()

按照此跟踪并检查 queue() 方法。 如果队列不存在,则会创建它: $queue = new TaskQueue() (在我们的例子中会发生什么)——让我们打开 TaskQueue 构造函数:

public function __construct($withShutdown = true){    if ($withShutdown) {        register_shutdown_function(function () {            if ($this->enableShutdown) {                // Only run the tasks if an E_ERROR didn't occur.                $err = error_get_last();                if (!$err || ($err['type'] ^ E_ERROR)) {                    $this->run();                }            }        });    }}

register_shutdown_function!起初它让我感到惊讶,如果 PHP 中有这样的可能性,为什么不使用它们(但是,我对此不确定)。

现在:

为什么这些请求是异步执行的?

因为 curl 库允许这样做!

-Z, —平行

与常规串行方式相比,使 curl 并行执行其传输。

PHP 也使用这个库——php 解释器可以用它编译,也可以作为扩展添加——PHP 扩展可以通过 php -m 检查:

php -m...curl...

Curl PHP 扩展提供了对系统 curl 库的访问——你可以使用 cli curl 接口(如果我检查正确的话):

docker run --rm -ti php:8.1 bash...# ldd /usr/local/bin/php | grep curl libcurl.so.4 => /usr/lib/x86_64-linux-gnu/libcurl.so.4 ...# ldd /usr/bin/curl | grep libcurl libcurl.so.4 => /usr/lib/x86_64-linux-gnu/libcurl.so.4 ...

它是一个用 C编写的工具,可以利用线程并且 PHP API 提供了一种利用 libcurl 库提供的多线程功能的方法!

接下来是下一部分——如果您想直接使用此功能,我的意思是按照 PHP API 描述它的方式,那么讨论的代码可能如下所示:

<?phprequire_once __DIR__ . '/../vendor/autoload.php';$sleepingUrl = getenv('SLEEPING_URL') ?: ";;$multi = curl_multi_init();$stillRunning = null;for ($i = 0; $i <= 3; $i++) {    $curl = curl_init("{$sleepingUrl}/{$i}");    curl_multi_add_handle($multi, $curl);}do {    $status = curl_multi_exec($multi, $stillRunning);    if ($stillRunning) {        curl_multi_select($multi);    }} while ($stillRunning && $status == CURLM_OK);

上面的脚本可以在这里找到并由(对不起那个小cli-monster)执行/测试:

docker compose run \  --entrypoint "/bin/bash -c \"time php bin/fetch_mcurl.php\"" php

你可以像那样使用它——但它看起来不会比上面提供的解决方案复杂得多:

Utils::task(    fn() => $client->getAsync("{$sleepingUrl}/{$i}")        ->then($printResponse)        ->wait());

对于我来说,通过 Guzzle 开发人员实现的 promise 接口的使用看起来好多了

标签: #curlphp