207 lines
6.8 KiB
PHP
207 lines
6.8 KiB
PHP
<?php
|
|
|
|
namespace App\Platform\Tools;
|
|
|
|
use GuzzleHttp\Client;
|
|
use GuzzleHttp\ClientInterface;
|
|
use GuzzleHttp\Exception\ConnectException;
|
|
use GuzzleHttp\Exception\RequestException;
|
|
use GuzzleHttp\HandlerStack;
|
|
use GuzzleHttp\Middleware;
|
|
use GuzzleHttp\RetryMiddleware;
|
|
use App\Utils\Log;
|
|
use Psr\Http\Message\RequestInterface;
|
|
use Psr\Http\Message\ResponseInterface;
|
|
use Hyperf\Guzzle\CoroutineHandler;
|
|
|
|
/**
|
|
* Tools client - send request to wpic tools
|
|
*/
|
|
class HttpClient
|
|
{
|
|
const MAX_RETRIES = 3;
|
|
|
|
use RequestTrait;
|
|
|
|
static function create(): ClientInterface
|
|
{
|
|
|
|
$stack = HandlerStack::create(new CoroutineHandler());
|
|
|
|
// 添加自动重试中间件
|
|
$stack->push(Middleware::retry(self::retryPolicy(), self::retryDelay()));
|
|
|
|
$config = [
|
|
'headers' => [
|
|
'Accept' => 'application/json; charset=utf-8',
|
|
'Content-Type' => 'application/json; charset=utf-8',
|
|
'Hash' => self::getHash()
|
|
],
|
|
'base_uri' => self::getHost(),
|
|
'connect_timeout' => 10,
|
|
'timeout' => 60,
|
|
'handler' => $stack,
|
|
'debug' => true
|
|
];
|
|
|
|
return new Client($config);
|
|
}
|
|
|
|
/**
|
|
* 重试策略
|
|
*
|
|
* @return callable
|
|
*/
|
|
public static function retryPolicy(): callable
|
|
{
|
|
return function (int $retries, RequestInterface $request, ResponseInterface $response = null, \Throwable $exception = null): bool {
|
|
// 记录异常信息
|
|
if ($exception) {
|
|
dump($exception->getMessage());
|
|
Log::error("重试异常: " . $exception->getMessage());
|
|
}
|
|
|
|
// 检查是否为连接异常或请求异常(包括超时)
|
|
$should_retry = false;
|
|
|
|
if ($exception) {
|
|
// 处理连接异常(包括连接超时)
|
|
if ($exception instanceof ConnectException) {
|
|
$should_retry = true;
|
|
}
|
|
// 处理请求异常(包括请求超时)
|
|
elseif ($exception instanceof RequestException) {
|
|
// 如果是超时导致的请求异常
|
|
if (strpos($exception->getMessage(), 'timed out') !== false) {
|
|
$should_retry = true;
|
|
}
|
|
// 如果有响应,检查状态码
|
|
elseif ($exception->hasResponse()) {
|
|
$status_code = $exception->getResponse()->getStatusCode();
|
|
$should_retry = in_array($status_code, [429, 500, 502, 503, 504]);
|
|
}
|
|
}
|
|
}
|
|
|
|
// 首先检查重试次数
|
|
if ($retries >= self::MAX_RETRIES) {
|
|
return false;
|
|
}
|
|
|
|
// 检查状态码 (需要先确保 $response 不为 null)
|
|
$status_code_error = $response && in_array($response->getStatusCode(), [429, 500, 502, 503, 504]);
|
|
|
|
// 如果是应该重试的异常、状态码错误或响应为 null,则重试
|
|
return $should_retry || $status_code_error || is_null($response);
|
|
};
|
|
}
|
|
|
|
/**
|
|
* 重试延迟策略
|
|
*
|
|
* @return callable
|
|
*/
|
|
public static function retryDelay(): callable
|
|
{
|
|
return function (int $retries, ResponseInterface $response = null): int {
|
|
$delay = 0;
|
|
|
|
if (is_null($response) || !$response->hasHeader('Retry-After')) {
|
|
$delay = RetryMiddleware::exponentialDelay($retries) * 5;
|
|
} else {
|
|
$retryAfter = $response->getHeaderLine('Retry-After');
|
|
$delay = $retryAfter;
|
|
|
|
if (!is_numeric($retryAfter)) {
|
|
$delay = (new \DateTime($retryAfter))->getTimestamp() - time();
|
|
}
|
|
$delay = (int)$delay * 1000;
|
|
}
|
|
|
|
$seconds = $delay / 1000;
|
|
$message = "请求失败, 此为第 $retries 次尝试,{$seconds} 秒之后重试";
|
|
|
|
// 如果有响应,添加状态码信息
|
|
if ($response) {
|
|
$code = $response->getStatusCode();
|
|
dump($response->getBody()->getContents());
|
|
$message = "请求失败, 远程服务器响应码为 $code, 此为第 $retries 次尝试,{$seconds} 秒之后重试";
|
|
}
|
|
|
|
dump($message);
|
|
Log::error($message);
|
|
|
|
return $delay;
|
|
};
|
|
}
|
|
|
|
static function get(string $path, array $query = []): array
|
|
{
|
|
try{
|
|
$response = static::create()->request('GET', $path, ['query' => $query]);
|
|
// dump($response);
|
|
return static::parse($response);
|
|
}catch(\Throwable $exception) {
|
|
dump($exception->getMessage());
|
|
dump($exception->getTraceAsString());
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
static function post(string $path, array $query = [], array $data = []): array
|
|
{
|
|
try{
|
|
$response = static::create()->request('POST', $path, ['query' => $query, 'body' => json_encode($data)]);
|
|
// dump($response);
|
|
// dump(static::parse($response));
|
|
return static::parse($response);
|
|
}catch(\Throwable $exception){
|
|
dump($exception->getMessage());
|
|
dump($exception->getTraceAsString());
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
static function put(string $path, array $query = [], array $data = [])
|
|
{
|
|
try{
|
|
$response = static::create()->request('PUT', $path, ['query' => $query, 'body' => json_encode($data)]);
|
|
// dump($response);
|
|
return static::parse($response);
|
|
}catch(\Throwable $exception){
|
|
dump($exception->getMessage());
|
|
dump($exception->getTraceAsString());
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
static function delete(string $path)
|
|
{
|
|
try{
|
|
$response = static::create()->request('DELETE', $path);
|
|
// dump($response);
|
|
return static::parse($response);
|
|
}catch(\Throwable $exception){
|
|
dump($exception->getMessage());
|
|
dump($exception->getTraceAsString());
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
protected static function parse(ResponseInterface $response): array
|
|
{
|
|
|
|
$http_code = $response->getStatusCode();
|
|
$body = $response->getBody();
|
|
// dump($body->getContents());
|
|
// when deal with stream object, need to move to pointer to the ram head
|
|
$body->rewind();
|
|
$content = $body->getContents();
|
|
$payload = \json_decode($content, 1);
|
|
if ($http_code == 200) {
|
|
return $payload;
|
|
}
|
|
}
|
|
|
|
}
|