php 异步并行

如果你有一批数据需要调用远程接口处理,而远程接口处理时间很长,比如需要1秒左右,那10条数据就是10秒,你的程序就要10S才能结束,而这样的话一旦接口提供方有点问题,就需要20秒 30秒甚至更久,这样就给我们带来了极大隐患,虽然我们可以使用设置超时来避免这样的长久等待,但是如果串行化不解决的话,程序始终是要长时间等所有任务都结束才能继续判断后面的结果的。

所以这里引入异步概念,用 composer require spatie/async 安装后的 Pool 里面的create 和 async 以及await来解决问题。

例子如下:

我们接口提供方是这样的代码

<?php
sleep(1);
echo json_encode(range(1,15));
exit;

以往我们的调用是这样的

foreach (range(1, 10) as $v) {
  $list[] = file_get_contents("http://localhost/index.php?act=" . $v);
}

那就是按顺序调用接口,统计下来耗时 10021 ms 也就是10秒

现在我们可以这样做

<?php
include "vendor/autoload.php";

use Spatie\Async\Pool;
$pool = Pool::create();
foreach (range(1, 10) as $item) {
    $pool[] = async(function () use ($item) {
      return file_get_contents("http://localhost/index.php?act=" . $item);
    })->then(function (string $output) use(&$list) {
      $list[] = $output;
    });
}
await($pool);

发现耗时 1341 ms,也就是说基本上是9-10个在同时并行请求,节省了很多时间。

我测试了一下如果不是在循环里面调用接口,而是简单的做一个item*2的数学运算,那同步可快多了,异步可慢多了。

<?php
include "vendor/autoload.php";

use Spatie\Async\Pool;
$pool = Pool::create();
foreach (range(1, 10) as $item) {
    $pool[] = async(function () use ($item) {
      return $item * 2;
    })->then(function (string $output) use(&$list) {
      $list[] = $output;
    });
}
await($pool);

耗时 324 ms

而同步的嘛,只要 0.0019 ms

<?php
foreach (range(1, 10) as $v) {
  $list[] = 2 * $v;
}

毕竟这种情况下多余的异步开销反而不如同步啊。

适用场景就是多数据量,长耗时任务被阻塞等待,那这个时候用这个东西缓解一下压力,提高一下速度还是不错的。