天天看點

workerman5.0 異步非阻塞HTTP協程用戶端

作者:不秃頭程式員
workerman5.0 異步非阻塞HTTP協程用戶端

概述

Workerman 5.0 版本中的異步HTTP協程用戶端元件是一個基于PHP協程的高性能HTTP用戶端,它能夠充分利用PHP的異步特性來提高HTTP請求的效率和性能。這個元件允許開發者在編寫PHP代碼時,以同步的方式發送異步HTTP請求,進而使得編寫的代碼更加簡潔易懂,同時也能夠處理大量的并發請求。

這個元件的特點包括:
  • 異步非阻塞:所有的請求和響應都是異步進行的,不會阻塞主線程,這意味着可以同時處理多個HTTP請求和響應。
  • 内置連接配接池:為了提高效率和性能,該元件内置了連接配接池,可以複用TCP連接配接,減少建立和關閉連接配接的開銷。
  • 符合PSR-7規範:消息請求和響應都符合PSR-7規範,這使得它能夠與遵循該規範的其他PHP元件和庫無縫內建。
  • 支援多種協定:除了HTTP和HTTPS協定,該元件還支援WebSocket、WSS等協定,使其能夠應對更多的應用場景。

安裝

注意:5.0需要PHP版本大于8.2.4及以上版本

通過php -v檢視你目前PHP版本資訊

php -v
PHP 8.2.18 (cli) (built: Apr 11 2024 19:20:54) (NTS)
Copyright (c) The PHP Group
Zend Engine v4.2.18, Copyright (c) Zend Technologies
    with Zend OPcache v8.2.18, Copyright (c), by Zend Technologies           

workerman 5.0 安裝

composer require workerman/workerman v5.0.0-beta.7           
workerman5.0 異步非阻塞HTTP協程用戶端

revolt/event-loop 安裝

Revolt是什麼?

Revolt是并發PHP應用程式的堅如磐石的事件循環。通常的PHP應用程式将大部分時間花在等待I/O上。雖然PHP是單線程的,但可以使用協作多任務來允許并發性,方法是使用等待時間來做不同的事情。

PHP的傳統同步執行流程很容易了解。一次隻做一件事。如果查詢資料庫,則發送查詢并等待資料庫伺服器的響應。一旦你有了答案,你就可以開始做下一件事。

ReactPHP和其他庫已經在PHP中提供了很長一段時間的協作多任務。然而,它們的事件驅動特性與許多現有的接口不相容,需要不同的思維模型。PHP 8.1内置了fibers,它提供了協作多線程。調用可以是異步的,沒有promise或回調,同時仍然允許非阻塞I/O。

每個使用協同多任務的應用程式都需要一個排程器(也稱為事件循環),這個包提供了這個排程器。Revolt是結合了React和ReactPHP的事件循環實作的多年經驗的結果。然而,它并不是一個用于編寫并發PHP應用程式的成熟架構,而隻是提供了必要的公共基礎。不同的(強烈的)固執己見的庫可以在它的基礎上建構,React和ReactPHP将繼續共存。

Revolt 支援事件
  • Defer 回調在事件循環的下一次疊代中執行。如果有延遲排程,事件循環不會在疊代之間等待。
  • Delay 在指定的秒數後執行回調。秒的分數可以表示為浮點數。
  • Repeat 在指定的秒數後重複執行回調。秒的分數可以表示為浮點數。
  • Stream readable 當流上有資料要讀取或連接配接關閉時,将執行回調。
  • Stream writable 當寫緩沖區中有足夠的空間來接受要寫入的新資料時,就會執行回調。
  • Signal 當程序從作業系統接收到特定信号時執行回調。
安裝
composer require revolt/event-loop           
workerman5.0 異步非阻塞HTTP協程用戶端

workerman/http-client安裝

workerman/http-client是一個異步http用戶端元件。所有請求響應異步非阻塞,内置連接配接池,消息請求和響應符合PSR7規範。更多了解:https://www.workerman.net/doc/workerman/components/workerman-http-client.html
composer require workerman/http-client           
workerman5.0 異步非阻塞HTTP協程用戶端

編寫代碼

http.php僞代碼

<?php
/**
 * @desc 僞代碼
 * @author Tinywan(ShaoBo Wan)
 * @date 2024/11/14 15:14
 */
declare(strict_types=1);

use Workerman\Worker;

require_once '../vendor/autoload.php';

try {
    $worker = new Worker();
    $worker->onWorkerStart = function () {
        $http = new Workerman\Http\Client();

        $response = $http->get('https://www.tinywan.com/');
        var_dump($response->getStatusCode());
        echo $response->getBody() . PHP_EOL;

        $response = $http->post('https://www.tinywan.com/', ['key1' => 'value1', 'key2' => 'value2']);
        var_dump($response->getStatusCode());
        echo $response->getBody() . PHP_EOL;

        $response = $http->request('https://www.tinywan.com/', [
            'method' => 'GET',
            'version' => '1.1',
            'headers' => ['Connection' => 'keep-alive'],
            'data' => ['key1' => 'value1', 'key2' => 'value2'],
        ]);
        echo $response->getBody() . PHP_EOL;
    };
    Worker::runAll();
} catch (Throwable $throwable) {
    var_dump($throwable->getMessage());
}           

啟動服務

/var/www/workerman5.x-demo/coroutine # php http.php start
Workerman[http.php] start in USER mode
---------------------------------------- WORKERMAN -----------------------------------------
Workerman version:5.0.0-beta.7    PHP version:8.2.18     Event-loop:Workerman\Events\Revolt
----------------------------------------- WORKERS ------------------------------------------
proto   user            worker          listen          processes    state            
tcp     root            none            none            1             [OK]            
--------------------------------------------------------------------------------------------
Press Ctrl+C to stop. Start success.           
注意:這裡使用Event-loop:Workerman\Events\Revolt事件

執行結果

int(200)
int(405)
int(200)
           

http-client 協程異步并發

<?php
/**
 * @desc 僞代碼
 * @author Tinywan(ShaoBo Wan)
 * @date 2024/11/14 15:14
 */
declare(strict_types=1);

use Workerman\Worker;
use \Workerman\Connection\TcpConnection;
use \Workerman\Protocols\Http\Request;

require_once '../vendor/autoload.php';

// 建立一個Worker監聽8217端口,使用http協定通訊
$httpWorker = new Worker("http://0.0.0.0:8217");

// 啟動8個程序對外提供服務
$httpWorker->count = 8;

// 接收到浏覽器發送的資料時回複給浏覽器
$httpWorker->onMessage = function (TcpConnection $connection, Request $request) {
    $http = new \Workerman\Http\Client();

    $count = 50;
    $result = [];
    while ($count--) {
        $startTime = microtime(true);
        echo '開始時間:' . $startTime . PHP_EOL;
        $response = $http->get('https://api.tinywan.com/systems/website');
        $endTime = microtime(true);
        echo '結束時間:' . $endTime . PHP_EOL;
        $result[] = sprintf('第%d個 | 耗時%s秒 | 狀态碼%d', $count, $endTime - $startTime, $response->getStatusCode());
    }
    $connection->send(json_encode($result));
};

// 運作worker
Worker::runAll();           

執行結果(未完,待續……)

[
    "第49個 | 耗時0.53055596351624秒 | 狀态碼200",
    "第48個 | 耗時0.11245107650757秒 | 狀态碼200",
    "第47個 | 耗時0.13903284072876秒 | 狀态碼200",
    "第46個 | 耗時0.21533107757568秒 | 狀态碼200",
    "第45個 | 耗時0.11252999305725秒 | 狀态碼200",
    "第44個 | 耗時0.15401482582092秒 | 狀态碼200",
    "第43個 | 耗時0.10678505897522秒 | 狀态碼200",
    "第42個 | 耗時0.099571943283081秒 | 狀态碼200",
    "第41個 | 耗時0.12561202049255秒 | 狀态碼200",
    "第40個 | 耗時0.10563802719116秒 | 狀态碼200",
    "第39個 | 耗時0.11354088783264秒 | 狀态碼200",
    "第38個 | 耗時0.1011688709259秒 | 狀态碼200",
    "第37個 | 耗時0.20793986320496秒 | 狀态碼200",
    "第36個 | 耗時0.1089551448822秒 | 狀态碼200",
    "第35個 | 耗時0.092885971069336秒 | 狀态碼200",
    "第34個 | 耗時0.10161209106445秒 | 狀态碼200",
    "第33個 | 耗時0.20717096328735秒 | 狀态碼200",
    "第32個 | 耗時0.20546984672546秒 | 狀态碼200",
    "第31個 | 耗時0.11044192314148秒 | 狀态碼200",
    "第30個 | 耗時0.12123489379883秒 | 狀态碼200",
    "第29個 | 耗時0.11858606338501秒 | 狀态碼200",
    "第28個 | 耗時0.17020511627197秒 | 狀态碼200",
    "第27個 | 耗時0.095601081848145秒 | 狀态碼200",
    "第26個 | 耗時0.11254286766052秒 | 狀态碼200",
    "第25個 | 耗時0.19883704185486秒 | 狀态碼200",
    "第24個 | 耗時0.20402407646179秒 | 狀态碼200",
    "第23個 | 耗時0.10713791847229秒 | 狀态碼200",
    "第22個 | 耗時0.11914396286011秒 | 狀态碼200",
    "第21個 | 耗時0.094994068145752秒 | 狀态碼200",
    "第20個 | 耗時0.10224294662476秒 | 狀态碼200",
    "第19個 | 耗時0.11380887031555秒 | 狀态碼200",
    "第18個 | 耗時0.10260701179504秒 | 狀态碼200",
    "第17個 | 耗時0.28102612495422秒 | 狀态碼200",
    "第16個 | 耗時0.10411977767944秒 | 狀态碼200",
    "第15個 | 耗時0.10007786750793秒 | 狀态碼200",
    "第14個 | 耗時0.095911979675293秒 | 狀态碼200",
    "第13個 | 耗時0.094711065292358秒 | 狀态碼200",
    "第12個 | 耗時0.09196400642395秒 | 狀态碼200",
    "第11個 | 耗時0.10585021972656秒 | 狀态碼200",
    "第10個 | 耗時0.092756032943726秒 | 狀态碼200",
    "第9個 | 耗時0.23688101768494秒 | 狀态碼200",
    "第8個 | 耗時0.20317101478577秒 | 狀态碼200",
    "第7個 | 耗時0.10062909126282秒 | 狀态碼200",
    "第6個 | 耗時0.20747685432434秒 | 狀态碼200",
    "第5個 | 耗時0.11253881454468秒 | 狀态碼200",
    "第4個 | 耗時0.12536215782166秒 | 狀态碼200",
    "第3個 | 耗時0.17252016067505秒 | 狀态碼200",
    "第2個 | 耗時0.12250709533691秒 | 狀态碼200",
    "第1個 | 耗時0.10875701904297秒 | 狀态碼200",
    "第0個 | 耗時0.10053586959839秒 | 狀态碼200"
]           
倉庫位址:https://github.com/Tinywan/workerman5.x-demo