workerman/http-client

Descrizione

workerman/http-client è un componente client HTTP asincrono. Tutte le richieste e risposte sono asincrone e non bloccanti, con un pool di connessione integrato. Le richieste e le risposte dei messaggi soddisfano gli standard PSR7.

Installazione:

composer require workerman/http-client

Esempi:

Utilizzo di richiesta GET e POST

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();

Caricamento di un file

<?php
use Workerman\Worker;

require_once 'vendor/autoload.php';

$worker = new Worker();
$worker->onWorkerStart = function () {
    $http = new Workerman\Http\Client();
    // Caricamento di un file
    $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();

Restituzione progressiva del flusso

<?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('')); // Invia un chunk vuoto per indicare la fine della response
        },
    ]);
    $connection->send(new Response(200, [
        //"Content-Type" => "application/octet-stream",
        "Transfer-Encoding" => "chunked",
    ], ''));
};
Worker::runAll();

Opzioni

<?php
require __DIR__ . '/vendor/autoload.php';
use Workerman\Worker;
$worker = new Worker();
$worker->onWorkerStart = function(){
    $options = [
        'max_conn_per_addr' => 128, // Massimo numero di connessioni concorrenti per ogni dominio
        'keepalive_timeout' => 15,  // Durata massima in cui una connessione può rimanere inattiva prima di chiudersi
        'connect_timeout'   => 30,  // Tempo di timeout per la connessione
        'timeout'           => 30,  // Tempo di timeout per ricevere una risposta dopo l'invio della richiesta
    ];
    $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();

Utilizzo delle coroutine

Attenzione
L'utilizzo delle coroutine richiede workerman>=5.1, http-client>=3.0, l'installazione dell'estensione swoole o swow, oppure l'installazione di composer require revolt/event-loop per supportare il driver Fiber

use Workerman\Worker;

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

$worker = new Worker();
$worker->eventLoop = \Workerman\Events\Swoole::class; // Oppure \Workerman\Events\Swow::class o \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();

Quando non si impostano funzioni di callback, il client restituisce il risultato della richiesta asincrona in modo sincrono, senza bloccare il processo corrente, permettendo così di gestire richieste concorrenti.

Attenzione:

  1. Il progetto deve prima caricare require __DIR__ . '/vendor/autoload.php';

  2. Tutto il codice asincrono può essere eseguito solo nell'ambiente di esecuzione avviato da workerman

  3. Supporta tutti i progetti sviluppati su Workerman, inclusi Webman, GatewayWorker, PHPSocket.io, ecc.

  4. Cercare di mantenere l'oggetto client per riutilizzarlo, in modo da sfruttare appieno il pool di connessione e migliorare le prestazioni, evitando di creare ogni volta un nuovo oggetto con new Workerman\Http\Client().

Utilizzo in webman

Se hai bisogno di utilizzare richieste HTTP asincrone in webman e restituire i risultati al frontend, fai riferimento al seguente esempio:

<?php
namespace app\controller;

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

class IndexController
{
    public function index(Request $request)
    {
        // Mantieni l'oggetto client per riutilizzarlo, ciò può migliorare notevolmente le prestazioni
        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('')); // Invia un chunk vuoto per indicare la fine della response
        });
        return response()->withHeaders([
            "Transfer-Encoding" => "chunked",
        ]);
    }
}

L'esempio sopra restituisce un'intestazione HTTP con chunked al client e poi invia i dati in formato chunk al client, ovviamente si può anche fare riferimento all'utilizzo delle coroutine descritto sopra.

Attenzione
Il codice sopra memorizza l'oggetto client come una variabile statica all'interno del metodo per riutilizzarlo, ma può anche essere memorizzato come membro statico della classe o in un oggetto globale.

Richiesta dell'interfaccia OpenAI in webman e restituzione fluida

Fai riferimento a https://www.workerman.net/plugin/157