workerman/http-client

說明

workerman/http-client 是一個異步 http 客戶端組件。所有請求響應異步非阻塞,內建連接池,消息請求和響應符合 PSR7 規範。

安裝:

composer require workerman/http-client

範例:

get post request 用法

use Workerman\Worker;

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

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

    $http->get('https://example.com/', function ($response) {
        var_dump($response->getStatusCode());
        echo $response->getBody();
    }, function ($exception) {
        echo $exception;
    });

    $http->post('https://example.com/', ['key1' => 'value1', 'key2' => 'value2'], function ($response) {
        var_dump($response->getStatusCode());
        echo $response->getBody();
    }, function ($exception) {
        echo $exception;
    });

    $http->request('https://example.com/', [
        'method' => 'POST',
        'version' => '1.1',
        'headers' => ['Connection' => 'keep-alive'],
        'data' => ['key1' => 'value1', 'key2' => 'value2'],
        'success' => function ($response) {
            echo $response->getBody();
        },
        'error' => function ($exception) {
            echo $exception;
        }
    ]);
};
Worker::runAll();

上傳檔案

<?php
use Workerman\Worker;

require_once 'vendor/autoload.php';

$worker = new Worker();
$worker->onWorkerStart = function () {
    $http = new Workerman\Http\Client();
    // 上傳檔案
    $multipart = new \Workerman\Psr7\MultipartStream([
        [
            'name' => 'file',
            'contents' => fopen(__FILE__, 'r')
        ],
        [
            'name' => 'json',
            'contents' => json_encode(['a'=>1, 'b'=>2])
        ]
    ]);
    $boundary = $multipart->getBoundary();
    $http->request('http://127.0.0.1:8787', [
        'method' => 'POST',
        'version' => '1.1',
        'headers' => ['Connection' => 'keep-alive', 'Content-Type' => "multipart/form-data; boundary=$boundary"],
        'data' => $multipart,
        'success' => function ($response) {
            echo $response->getBody();
        },
        'error' => function ($exception) {
            echo $exception;
        }
    ]);
};

Worker::runAll();

progress 流式返回

<?php
require_once __DIR__ . '/vendor/autoload.php';

use Workerman\Connection\TcpConnection;
use Workerman\Http\Client;
use Workerman\Protocols\Http\Chunk;
use Workerman\Protocols\Http\Request;
use Workerman\Protocols\Http\Response;
use Workerman\Worker;

$worker = new Worker('http://0.0.0.0:1234');
$worker->onMessage = function (TcpConnection $connection, Request $request) {
    $http = new Client();
    $http->request('https://api.ai.com/v1/chat/completions', [
        'method' => 'POST',
        'data' => json_encode([
            'model' => 'gpt-3.5-turbo',
            'temperature' => 1,
            'stream' => true,
            'messages' => [['role' => 'user', 'content' => 'hello']],
        ]),
        'headers' => [
            'Content-Type' => 'application/json',
            'Authorization' => 'Bearer sk-xxx',
        ],
        'progress' => function($buffer) use ($connection) {
            $connection->send(new Chunk($buffer));
        },
        'success' => function($response) use ($connection) {
            $connection->send(new Chunk('')); // 發送空的 chunk 代表 response 結束
        },
    ]);
    $connection->send(new Response(200, [
        //"Content-Type" => "application/octet-stream",
        "Transfer-Encoding" => "chunked",
    ], ''));
};
Worker::runAll();

Optinons 選項

<?php
require __DIR__ . '/vendor/autoload.php';
use Workerman\Worker;
$worker = new Worker();
$worker->onWorkerStart = function(){
    $options = [
        'max_conn_per_addr' => 128, // 每個域名最多維持多少並發連接
        'keepalive_timeout' => 15,  // 連接多長時間不通訊就關閉
        'connect_timeout'   => 30,  // 連接超時的時間
        'timeout'           => 30,  // 請求發出後等待響應的超時時間
    ];
    $http = new Workerman\Http\Client($options);

    $http->get('http://example.com/', function($response){
        var_dump($response->getStatusCode());
        echo $response->getBody();
    }, function($exception){
        echo $exception;
    });
};
Worker::runAll();

協程用法

注意
協程用法需要 workerman >= 5.1,http-client >= 3.0,安裝 swoole 或者 swow 擴展,或者安裝 composer require revolt/event-loop 以支持 Fiber 驅動

use Workerman\Worker;

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

$worker = new Worker();
$worker->eventLoop = \Workerman\Events\Swoole::class; // 或 \Workerman\Events\Swow::class 或 \Workerman\Events\Fiber::class
$worker->onWorkerStart = function () {
    $http = new Workerman\Http\Client();

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

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

    $response = $http->request('https://example.com/', [
        'method' => 'POST',
        'version' => '1.1',
        'headers' => ['Connection' => 'keep-alive'],
        'data' => ['key1' => 'value1', 'key2' => 'value2'],
    ]);
    echo $response->getBody();
};
Worker::runAll();

當不設置回調函數時,客戶端會用同步的方式返回異步請求結果,請求過程不阻塞當前進程,也就是可以並發處理請求。

注意:

1、專案首先要加載 require __DIR__ . '/vendor/autoload.php';

2、所有的異步程式碼只能在 workerman 啟動後的運行環境運行

3、支持基於 workerman 開發的所有專案,包括 Webman、GatewayWorker、PHPSocket.io 等

4、儘量將 client 對象保存起來重複使用,這樣能充分利用連接池提高性能,不要每次 new Workerman\Http\Client() 重複創建對象。

webman 中的用法

如果你需要在 webman 中使用異步 http 請求並將結果返回給前端,參考以下用法

<?php
namespace app\controller;

use support\Request;
use support\Response;
use Workerman\Protocols\Http\Chunk;

class IndexController
{
    public function index(Request $request)
    {
        // client 對象保存起來重複使用,可大幅度提高性能
        static $http;
        $connection = $request->connection;
        $http = $http ?: new \Workerman\Http\Client();
        $http->get('https://example.com/', function ($response) use ($connection) {
            $connection->send(new Chunk($response->getBody()));
            $connection->send(new Chunk('')); // 發送空的的 chunk 代表 response 結束
        });
        return response()->withHeaders([
            "Transfer-Encoding" => "chunked",
        ]);
    }
}

以上用法是先給客戶端返回一個帶 chunked 的 http 頭,然後將數據以 chunk 的方式發送給客戶端,當然也可以參考上面使用協程用法。

注意
以上程式碼為了重複使用 client 對象將其存儲在了方法作用域的靜態變數中,實際上也可以存儲到類的靜態成員中或者全局對象裡。

在 webman 中請求 OpenAI 介面並流式返回

參考 https://www.workerman.net/plugin/157