30、Hyperf 3 快速使用 - Hyperf 3 协程 Guzzle HTTP 客户端的使用

作者: 温新

分类: 【Hyperf 3 基础系列】

阅读: 450

时间: 2023-04-25 16:07:01

hi,我是温新,一名 PHPer

Hypref 版本:Hyperf 3.0

学习目标:掌握 Hyperf 3 协程 HTTP 客户端的使用

hyperf/guzzle 组件基于 Guzzle 进行协程处理,通过 Swoole HTTP 客户端作为协程驱动替换到 Guzzle 内,以达到 HTTP 客户端的协程化。

HTTP 客户端的使用

准备工作

1、准备两个项目,一个是 hyperf 项目(简称 A),另外一个是其他项目(简称 B);

2、目的:A 项目调用 B 项目的接口获取数据。

3、安装 A 项目中,安装如下组件:

composer require hyperf/guzzle

4、B 项目,我这里使用的是 Laravel,然后在 routes/api.php 文件中添加如下内容:

<?php
// B 项目
// routes/api.php
    
Route::get('/test', function () {
    return [
        'name' => '王美丽',
        'age'  => 22
    ];
});

使用 Swoole 配置

先来做一个了解,后续会使用到。使用到的时候对着案例看即可。

<?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('/');

基础使用

<?php
// App\Controller\Test\TestHttpController.php
    
namespace App\Controller\Test;

use App\Guzzle\TestHttp;
use Hyperf\Guzzle\ClientFactory;
use Hyperf\HttpServer\Annotation\Controller;
use Hyperf\HttpServer\Annotation\GetMapping;

#[Controller]
class TestHttpController
{

    public array $options = [];
    public $client;

    public function __construct(private ClientFactory $clientFactory)
    {
        $this->client = $this->clientFactory->create($this->options);
    }

    #[GetMapping('/gz/index')]
    public function index()
    {
        $result = $this->client->get('http://la10study.test/api/test');

        return json_decode($result->getBody(), true);
    }
}

测试

$ curl http://192.168.31.90:9501/gz/index
{"name":"王美丽","age":22}

使用连接池

Hyperf 除了实现了 Hyperf\Guzzle\CoroutineHandler 外,还基于 Hyperf\Pool\SimplePool 实现了 Hyperf\Guzzle\PoolHandler

使用连接池的原因:

1、主机 TCP 的连接数是有上限,当并发大到超过这个上限值时,就会导致请求无法建立连接;

2、TPC 连接结束后,会有一个 TIME-WAIT 阶段,因此无法实时释放连接。

综合这些原因,导致实际并发可能远低于 TCP 上限值,因此,需要一个连接池来维持这个阶段,尽量减少 TIME-WAIT 造成的影响,让 TCP 连接进行复用。

使用方式一

<?php

namespace App\Controller\Test;

use GuzzleHttp\Client;
use Hyperf\HttpServer\Annotation\Controller;
use Hyperf\HttpServer\Annotation\GetMapping;
use Hyperf\Utils\Coroutine;
use GuzzleHttp\HandlerStack;
use Hyperf\Guzzle\PoolHandler;
use Hyperf\Guzzle\RetryMiddleware;
#[Controller]
class TestHttpController
{
    public $client;

    public function __construct()
    {
        $handler = null;
        // 判断当前是否处于协程环境
        if (Coroutine::inCoroutine()) {
            $handler = make(PoolHandler::class, [
                'options' => [
                    'max_connections' => 50
                ]
            ]);
        }

        // 请求重试
        $retry = make(RetryMiddleware::class, [
            'retries' => 1,
            'delay'   => 10,
        ]);

        $stack = HandlerStack::create($handler);
        $stack->push($retry->getMiddleware(), 'retry');
        $this->client = make(Client::class, [
            'config' => [
                'config' => [
                    'handler' => $stack
                ]
            ]
        ]);
    }

    #[GetMapping('/gz/index')]
    public function index()
    {
        $result = $this->client->get('http://la10study.test/api/test');

        return $result->getBody()->getContents();
    }
}

方式二:使用 HandlerStackFactory

基础使用

<?php

namespace App\Controller\Test;

use GuzzleHttp\Client;
use Hyperf\Guzzle\HandlerStackFactory;
use Hyperf\HttpServer\Annotation\Controller;
use Hyperf\HttpServer\Annotation\GetMapping;

#[Controller]
class TestHttpController
{
    #[GetMapping('/gz/index')]
    public function index()
    {
        $factory = new HandlerStackFactory();
        $tasck   = $factory->create();

        $client = make(Client::class, [
            'config' => [
                'handle' => $tasck,
            ]
        ]);
        $response = $client->get('http://la10study.test/api/test');

        return [
            'code'    => $response->getStatusCode(),
            'body'    => $response->getBody()->getContents(),
            'content' => $response->getReasonPhrase()
        ];
    }
}

postman 测试结果

# get http://192.168.31.90:9501/gz/index 
{
    "code": 200,
    "body":"{\"name\":\"\王\美\丽\",\"age\":22}",
    "content": "OK"
}

集合修改配置使用

<?php

namespace App\Controller\Test;

use GuzzleHttp\Client;
use Hyperf\Guzzle\HandlerStackFactory;
use Hyperf\HttpServer\Annotation\Controller;
use Hyperf\HttpServer\Annotation\GetMapping;

#[Controller]
class TestHttpController
{
    #[GetMapping('/gz/index')]
    public function index()
    {
        $options = [
            // guzzle http里的配置信息
            'base_uri' => 'http://la10study.test',
            'handler'  => (new HandlerStackFactory())->create(),
            'timeout'  => 5,
            // swoole的配置信息,内容会覆盖guzzle http里的配置信息
            'swoole'   => [
                'timeout'            => 10,
                'socket_buffer_size' => 1024 * 1024 * 2,
            ],
        ];

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

        // options 中配置了域名,则请求时不需要携带域名
        $response = $client->get('/api/test');

        return [
            'code'    => $response->getStatusCode(),
            'body'    => $response->getBody()->getContents(),
            'content' => $response->getReasonPhrase()
        ];
    }
}

我是温新,本篇完成结束。

请登录后再评论