30、Hyperf 3 快速使用 - Hyperf 3 协程 Guzzle HTTP 客户端的使用
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()
];
}
}
我是温新,本篇完成结束。
请登录后再评论