Add callable type support to the fallback property of CircuitBreaker Attribute (#6363)

Co-authored-by: Deeka Wong <8337659+huangdijia@users.noreply.github.com>
This commit is contained in:
张城铭 2023-12-07 11:25:12 +08:00 committed by GitHub
parent af608b8e4a
commit 7f03249ed7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 44 additions and 17 deletions

View File

@ -11,6 +11,7 @@
- [#6342](https://github.com/hyperf/hyperf/pull/6342) Added `Coroutine::fork()` method and `Coroutine::pid()` method.
- [#6360](https://github.com/hyperf/hyperf/pull/6360/files) Added response `content-type` header for swagger server.
- [#6363](https://github.com/hyperf/hyperf/pull/6363) Add callable type support to the fallback property of CircuitBreaker Attribute.
# v3.1.0 - 2023-12-01

View File

@ -36,7 +36,7 @@ class UserService
#[Inject]
private UserServiceClient $client;
#[CircuitBreaker(timeout: 0.05, failCounter: 1, successCounter: 1, fallback: "App\UserService::searchFallback")]
#[CircuitBreaker(options: ['timeout' => 0.05], failCounter: 1, successCounter: 1, fallback: [UserService::class, 'searchFallback'])]
public function search($offset, $limit)
{
return $this->client->users($offset, $limit);

View File

@ -30,7 +30,7 @@ class UserService
#[Inject]
private UserServiceClient $client;
#[CircuitBreaker(options: ['timeout' => 0.05], failCounter: 1, successCounter: 1, fallback: "App\Service\UserService::searchFallback")]
#[CircuitBreaker(options: ['timeout' => 0.05], failCounter: 1, successCounter: 1, fallback: [UserService::class, 'searchFallback'])]
public function search($offset, $limit)
{
return $this->client->users($offset, $limit);

View File

@ -30,7 +30,7 @@ class UserService
#[Inject]
private UserServiceClient $client;
#[CircuitBreaker(options: ['timeout' => 0.05], failCounter: 1, successCounter: 1, fallback: "App\Service\UserService::searchFallback")]
#[CircuitBreaker(options: ['timeout' => 0.05], failCounter: 1, successCounter: 1, fallback: [UserService::class, 'searchFallback'])]
public function search($offset, $limit)
{
return $this->client->users($offset, $limit);

View File

@ -30,7 +30,7 @@ class UserService
#[Inject]
private UserServiceClient $client;
#[CircuitBreaker(options: ['timeout' => 0.05], failCounter: 1, successCounter: 1, fallback: "App\Service\UserService::searchFallback")]
#[CircuitBreaker(options: ['timeout' => 0.05], failCounter: 1, successCounter: 1, fallback: [UserService::class, 'searchFallback'])]
public function search($offset, $limit)
{
return $this->client->users($offset, $limit);

View File

@ -29,7 +29,7 @@ class CircuitBreaker extends AbstractAnnotation
*/
public function __construct(
public string $handler = TimeoutHandler::class,
public ?string $fallback = null,
public string|array $fallback = [],
public float $duration = 10.0,
public int $successCounter = 10,
public int $failCounter = 10,

View File

@ -0,0 +1,18 @@
<?php
declare(strict_types=1);
/**
* This file is part of Hyperf.
*
* @link https://www.hyperf.io
* @document https://hyperf.wiki
* @contact group@hyperf.io
* @license https://github.com/hyperf/hyperf/blob/master/LICENSE
*/
namespace Hyperf\CircuitBreaker\Exception;
use RuntimeException;
class BadFallbackException extends RuntimeException
{
}

View File

@ -15,6 +15,7 @@ use Hyperf\CircuitBreaker\Annotation\CircuitBreaker as Annotation;
use Hyperf\CircuitBreaker\CircuitBreaker;
use Hyperf\CircuitBreaker\CircuitBreakerFactory;
use Hyperf\CircuitBreaker\CircuitBreakerInterface;
use Hyperf\CircuitBreaker\Exception\BadFallbackException;
use Hyperf\CircuitBreaker\Exception\CircuitBreakerException;
use Hyperf\CircuitBreaker\FallbackInterface;
use Hyperf\Contract\StdoutLoggerInterface;
@ -169,27 +170,34 @@ abstract class AbstractHandler implements HandlerInterface
protected function fallback(ProceedingJoinPoint $proceedingJoinPoint, CircuitBreakerInterface $breaker, Annotation $annotation)
{
[$className, $methodName] = $this->prepareHandler($annotation->fallback);
[$class, $method] = $this->prepareHandler($annotation->fallback, $proceedingJoinPoint);
$class = $this->container->get($className);
if ($class instanceof FallbackInterface) {
return $class->fallback($proceedingJoinPoint);
$instance = $this->container->get($class);
if ($instance instanceof FallbackInterface) {
return $instance->fallback($proceedingJoinPoint);
}
$argument = $proceedingJoinPoint->getArguments();
$arguments = $proceedingJoinPoint->getArguments();
return $class->{$methodName}(...$argument);
return $instance->{$method}(...$arguments);
}
abstract protected function process(ProceedingJoinPoint $proceedingJoinPoint, CircuitBreakerInterface $breaker, Annotation $annotation);
protected function prepareHandler(string $fallback): array
protected function prepareHandler(string|array $fallback, ProceedingJoinPoint $proceedingJoinPoint): array
{
$result = explode('::', $fallback);
if (is_string($fallback)) {
$fallback = explode('::', $fallback);
if (! isset($fallback[1]) && is_callable([$proceedingJoinPoint->className, $fallback[0]])) {
return [$proceedingJoinPoint->className, $fallback[0]];
}
$fallback[1] ??= 'fallback';
}
return [
$result[0],
$result[1] ?? 'fallback',
];
if (is_array($fallback) && is_callable($fallback)) {
return $fallback;
}
throw new BadFallbackException('The fallback is invalid.');
}
}