當前位置: 妍妍網 > 碼農

誰說PHP不能異步和並列執行?

2024-05-17碼農

場景

在處理需要遠端介面呼叫的大量數據時,我們面臨一個關鍵問題:序列處理導致的效率低下。如果每個介面呼叫需要1秒,那麽即使是10條數據,也需要10秒來完成,這還沒有考慮到網路延遲和介面提供方可能出現的問題。在序列執行的情況下,一旦介面呼叫遇到問題,整個處理時間會成倍增加,這不僅降低了程式的響應速度,也增加了系統的不穩定性。

為了解決這個問題,我們可以采取以下幾種最佳化策略:

  • 異步執行 :透過異步呼叫遠端介面,可以讓程式在等待介面響應的同時繼續執行其他任務,從而提高整體的處理速度。

  • 並列處理 :利用多執行緒或多行程技術,同時發起多個遠端介面呼叫,顯著減少總的處理時間。

  • 現有方案

    遠端介面案例

    假設第三方或者遠端介面呼叫虛擬碼如下:

    <?php
    publicfunctionsync(): \support\Response
    {
    sleep(1);
    return json(['data' => date('Y-m-d H:i:s')]);
    }

    介面呼叫存取地址: http://127.0.0.1:8888/index/sync

    業務系統案例

    假設業務系統呼叫虛擬碼

    <?php
    declare(strict_types=1);
    foreach (range(110as $key) {
    $list[] = file_get_contents("http://127.0.0.1:8888/index/sync");
    }
    print_r($list);

    呼叫輸出

    [x] [系統呼叫耗時時間] 10.138074159622
    Array
    (
    [0] => {"data":"2024-05-16 22:38:00"}
    [1] => {"data":"2024-05-16 22:38:01"}
    [2] => {"data":"2024-05-16 22:38:02"}
    [3] => {"data":"2024-05-16 22:38:03"}
    [4] => {"data":"2024-05-16 22:38:04"}
    [5] => {"data":"2024-05-16 22:38:05"}
    [6] => {"data":"2024-05-16 22:38:06"}
    [7] => {"data":"2024-05-16 22:38:07"}
    [8] => {"data":"2024-05-16 22:38:08"}
    [9] => {"data":"2024-05-16 22:38:09"}
    )

    可以看出上面是按順序呼叫介面,總共耗時 10.14

    異步並列呼叫

    這個庫提供了一個小而簡單的PHP PCNTL擴充套件的包裝器。它允許並列執行不同的行程,並具有易於使用的API。官方地址: https://github.com/spatie/async

    安裝

    您可以透過composer安裝該軟體包

    composer require spatie/async

    註意:該擴充套件庫異步並列執行需要所需的擴充套件 pcntl posix 。沒有安裝在您當前的PHP執行時中, Pool 將自動回退到同步執行任務。

    Pool 類有一個靜態方法 isSupported ,你可以呼叫它來檢查你的平台是否能夠執行異步行程。

    require'../vendor/autoload.php';
    useSpatie\Async\Pool;
    var_dump(Pool::isSupported());

    支持異步行程則打印 true ,否則為 false

    使用

    <?php
    /**
     * @author Tinywan(ShaoBo Wan)
     * @date 2024/5/21 14:00
     */

    declare(strict_types=1);
    require'../vendor/autoload.php';
    useSpatie\Async\Pool;
    $timeOne = microtime(true);
    $pool = Pool::create();
    foreach (range(110as $item) {
    $pool[] = async(function()use($item){
    return file_get_contents("http://127.0.0.1:8888/index/sync");
    })->then(function(string $output)use(&$list){
    // Handle success
    $list[] = $output;
    })->catch(function(Throwable $exception){
    // Handle exception
    echo'[x] [異常] ' . $exception->getMessage() . PHP_EOL;
    });
    }
    await($pool);
    $timeTwo = microtime(true);
    echo'[x] [系統呼叫耗時時間] ' . ($timeTwo - $timeOne) . PHP_EOL,
    print_r($list);



    呼叫輸出

    [x] [系統呼叫耗時時間] 4.3443310260773
    Array
    (
    [0] => {"data":"2024-05-16 22:53:47"}
    [1] => {"data":"2024-05-16 22:53:47"}
    [2] => {"data":"2024-05-16 22:53:47"}
    [3] => {"data":"2024-05-16 22:53:47"}
    [4] => {"data":"2024-05-16 22:53:47"}
    [5] => {"data":"2024-05-16 22:53:47"}
    [6] => {"data":"2024-05-16 22:53:47"}
    [7] => {"data":"2024-05-16 22:53:47"}
    [8] => {"data":"2024-05-16 22:53:48"}
    [9] => {"data":"2024-05-16 22:53:49"}
    )

    可以看出上面是按並列呼叫介面,總共耗時 4.34 秒。節省了差不多一半多時間