workerman/http-client

Descripción

workerman/http-client es un componente de cliente http asíncrono. Todas las respuestas de solicitudes son asíncronas y no bloqueantes, con un pool de conexiones integrado, y las solicitudes y respuestas cumplen con la norma PSR7.

Instalación:

composer require workerman/http-client

Ejemplo:

Uso de solicitudes GET y 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();

Subida de archivos

<?php
use Workerman\Worker;

require_once 'vendor/autoload.php';

$worker = new Worker();
$worker->onWorkerStart = function () {
    $http = new Workerman\Http\Client();
    // Subida de archivos
    $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();

Devolución de flujo progresivo

<?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('')); // Enviar un chunk vacío representa el final de la respuesta
        },
    ]);
    $connection->send(new Response(200, [
        //"Content-Type" => "application/octet-stream",
        "Transfer-Encoding" => "chunked",
    ], ''));
};
Worker::runAll();

Opciones

<?php
require __DIR__ . '/vendor/autoload.php';
use Workerman\Worker;
$worker = new Worker();
$worker->onWorkerStart = function(){
    $options = [
        'max_conn_per_addr' => 128, // Máximo de conexiones concurrentes por dominio
        'keepalive_timeout' => 15,  // Tiempo para cerrar la conexión si no hay comunicación
        'connect_timeout'   => 30,  // Tiempo de espera para la conexión
        'timeout'           => 30,  // Tiempo de espera para recibir respuesta tras hacer la solicitud
    ];
    $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();

Uso de Coroutines

Nota
El uso de coroutines requiere workerman>=5.1, http-client>=3.0, instalar la extensión swoole o swow, o instalar composer require revolt/event-loop para soportar el controlador Fiber.

use Workerman\Worker;

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

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

Cuando no se establecen funciones de retorno, el cliente devolverá los resultados de las solicitudes asíncronas de manera sincrónica, el proceso de solicitud no bloquea el proceso actual, lo que permite manejar solicitudes de forma concurrente.

Notas:

  1. El proyecto debe cargar primero require __DIR__ . '/vendor/autoload.php';

  2. Todo el código asíncrono solo puede ejecutarse en el entorno de ejecución después de que workerman se inicie.

  3. Se admiten todos los proyectos desarrollados sobre workerman, incluidos Webman, GatewayWorker, PHPSocket.io, etc.

  4. Es recomendable mantener el objeto cliente para su reutilización, esto puede maximizar el uso del pool de conexiones y mejorar el rendimiento, en lugar de crear repetidamente un objeto new Workerman\Http\Client() cada vez.

Uso en webman

Si necesitas usar solicitudes http asíncronas en webman y devolver resultados al frontend, consulta el siguiente uso.

<?php
namespace app\controller;

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

class IndexController
{
    public function index(Request $request)
    {
        // Mantener el objeto client para reutilizar, puede mejorar considerablemente el rendimiento
        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('')); // Enviar un chunk vacío representa el final de la respuesta
        });
        return response()->withHeaders([
            "Transfer-Encoding" => "chunked",
        ]);
    }
}

El uso anterior consiste en devolver primero un encabezado http con chunked al cliente, y luego enviar los datos en forma de chunk al cliente, también se puede consultar el uso anterior de coroutines.

Nota
El código anterior ha almacenado el objeto cliente en una variable estática en el ámbito del método para su reutilización; en realidad, también se puede almacenar en un miembro estático de la clase o en un objeto global.

Solicitud a la API de OpenAI y devolución en flujo en webman

Consulta https://www.workerman.net/plugin/157