hyperf/docs/en/guzzle.md

5.7 KiB

Guzzle HTTP Client

The hyperf/guzzle component is based on Guzzle for coroutine processing, and is replaced into Guzzle through the Swoole HTTP client as a coroutine driver to achieve the coroutineization of the HTTP client.

Installation

composer require hyperf/guzzle

Application

Just set the Hyperf\Guzzle\CoroutineHandler in this component into the Guzzle client as a handler to convert into a coroutine operation. In order to facilitate the creation of the Guzzle object of the coroutine, we provide a factory class Hyperf\Guzzle\ClientFactory to conveniently create the client. Example is as follow:

<?php 
use Hyperf\Guzzle\ClientFactory;

class Foo {
    /**
     * @var \Hyperf\Guzzle\ClientFactory
     */
    private $clientFactory;
    
    public function __construct(ClientFactory $clientFactory)
    {
        $this->clientFactory = $clientFactory;
    }
    
    public function bar()
    {
        // $options is equivalent to the $config parameter of the GuzzleHttp\Client constructor
        $options = [];
        // $client is a coroutineized GuzzleHttp\Client object
        $client = $this->clientFactory->create($options);
    }
}

Use ^7.0 version

The component's dependency on Guzzle has been changed from ^6.3 to ^6.3 | ^7.0. The ^7.0 version can be installed by default, but the following components will conflict with ^7.0:

  • hyperf/metric

You can actively perform the following actions to resolve conflicts

composer require "promphp/prometheus_client_php:2.2.1"
  • overtrue/flysystem-cos

Due to the dependent library depends on guzzlehttp/guzzle-services, and it does not support ^7.0, it cannot be resolved temporarily.

Use Swoole configuration

Sometimes we want to modify the Swoole configuration directly, so we also provide related configuration items. But this configuration cannot take effect in the Curl Guzzle client, so use it carefully.

This configuration will replace the original configuration. For example, the timeout below will be replaced by 10.

<?php
use GuzzleHttp\Client;
use Hyperf\Guzzle\CoroutineHandler;
use GuzzleHttp\HandlerStack;

$client = new Client([
    'base_uri' => 'http://127.0.0.1:8080',
    'handler' => HandlerStack::create(new CoroutineHandler()),
    'timeout' => 5,
    'swoole' => [
        'timeout' => 10,
        'socket_buffer_size' => 1024 * 1024 * 2,
    ],
]);

$response = $client->get('/');

Connection Pool

Hyperf not only implements Hyperf\Guzzle\CoroutineHandler, but also implements Hyperf\Guzzle\PoolHandler based on Hyperf\Pool\SimplePool.

Why

There is an upper limit on the number of host TCP connections. When our concurrency exceeds this upper limit, the request cannot be established normally. In addition, there will be a TIME-WAIT after the TCP connection ends, so the connection cannot be released in time. Therefore, we need a connection pool to maintain this stage, minimize the impact of TIME-WAIT, and allow TCP connections to be reused.

Application

<?php
use GuzzleHttp\Client;
use Hyperf\Utils\Coroutine;
use GuzzleHttp\HandlerStack;
use Hyperf\Guzzle\PoolHandler;
use Hyperf\Guzzle\RetryMiddleware;

$handler = null;
if (Coroutine::inCoroutine()) {
    $handler = make(PoolHandler::class, [
        'option' => [
            'max_connections' => 50,
        ],
    ]);
}

// Default retry middleware
$retry = make(RetryMiddleware::class, [
    'retries' => 1,
    'delay' => 10,
]);

$stack = HandlerStack::create($handler);
$stack->push($retry->getMiddleware(), 'retry');

$client = make(Client::class, [
    'config' => [
        'handler' => $stack,
    ],
]);

In addition, the framework also provides HandlerStackFactory to conveniently create the above $stack.

<?php
use Hyperf\Guzzle\HandlerStackFactory;
use GuzzleHttp\Client;

$factory = new HandlerStackFactory();
$stack = $factory->create();

$client = make(Client::class, [
    'config' => [
        'handler' => $stack,
    ],
]);

Use ClassMap to replace GuzzleHttp\Client

If the third-party component does not provide an interface that can replace the Handler, we can also use the ClassMap to directly replace the Client to achieve the purpose of coroutineization of client.

Of course, you can also use SWOOLE_HOOK to achieve the same purpose.

Example is as follow:

class_map/GuzzleHttp/Client.php

<?php
namespace GuzzleHttp;

use GuzzleHttp\Psr7;
use Hyperf\Guzzle\CoroutineHandler;
use Hyperf\Utils\Coroutine;

class Client implements ClientInterface
{
    // Omitted other unchanged codes

    public function __construct(array $config = [])
    {
        $inCoroutine = Coroutine::inCoroutine();
        if (!isset($config['handler'])) {
            // The corresponding Handler can choose CoroutineHandler or PoolHandler as needed
            $config['handler'] = HandlerStack::create($inCoroutine ? new CoroutineHandler() : null);
        } elseif ($inCoroutine && $config['handler'] instanceof HandlerStack) {
            $config['handler']->setHandler(new CoroutineHandler());
        } elseif (!is_callable($config['handler'])) {
            throw new \InvalidArgumentException('handler must be a callable');
        }

        // Convert the base_uri to a UriInterface
        if (isset($config['base_uri'])) {
            $config['base_uri'] = Psr7\uri_for($config['base_uri']);
        }

        $this->configureDefaults($config);
    }
}

config/autoload/annotations.php

<?php

declare(strict_types=1);

use GuzzleHttp\Client;

return [
    'scan' => [
        // ...
        'class_map' => [
            Client::class => BASE_PATH . '/class_map/GuzzleHttp/Client.php',
        ],
    ],
];