pixelfederation/circuit-breaker-bundle

PHP世界中类似于Java的Hystrix的类似包。

2.0.1 2024-05-07 07:58 UTC

This package is auto-updated.

Last update: 2024-09-20 12:26:54 UTC


README

此包尝试复制著名的Java Hystrix库,并尝试使断路器模式的开发变得简单。

开发者只需将任何服务标记为断路器服务,使用特殊注解,此包将自动处理所有底层连接。

当前的底层实现使用Ganesha

安装

$ composer require pixelfederation/circuit-breaker-bundle

配置

只需启用此包。目前没有配置选项。

// in config/bundles.php add this line:
PixelFederation\CircuitBreakerBundle\Bridge\Symfony\PixelFederationCircuitBreakerBundle::class

使用方法

为了在某个服务上激活断路器,该服务必须实现PixelFederation\CircuitBreakerBundle\CircuitBrokenService接口。

该类不能被标记为final,因为底层会从它派生一个代理类。

要配置断路器,您可以使用类级别配置或方法级别配置。类级别配置适用于所有断路器方法。它配置为一个类级别的注解@CircuitBreakerService。要标记公共方法为断路器,必须使用@CircuitBreaker注解或属性

use PixelFederation\CircuitBreakerBundle\Annotation\CircuitBreakerService;
use PixelFederation\CircuitBreakerBundle\Annotation\CircuitBreaker;

/**
 * @CircuitBreakerService(
 *    defaultFallback="makeDefaultFallbackRequest", 
 *    ignoreExceptions={BadMethodCallException::class}
 * )
 */
class Service {
    /**
     * @CircuitBreaker() 
     */
    public function iShouldBeCircuitBroken(): int
    {
        return 0;
    }
}

use PixelFederation\CircuitBreakerBundle\Annotation\CircuitBreakerService;
use PixelFederation\CircuitBreakerBundle\Annotation\CircuitBreaker;

#[CircuitBreakerService(defaultFallback: 'makeDefaultFallbackRequest', ignoreExceptions: [BadMethodCallException::class])]
class Service {
    #[CircuitBreaker()]
    public function iShouldBeCircuitBroken(): int
    {
        return 0;
    }
}

@CircuitBreakerService的配置选项包括

  • defaultFallback:类中的一个公共方法,在未为断路器方法配置回退时,在异常发生时应调用。
  • ignoreExceptions:不触发标记包装服务为失败的异常列表

方法级别注解@CircuitBreaker可以覆盖类级别配置,并具有自己的配置选项,包括

  • fallbackMethod:类中的一个公共方法,在异常发生时应调用
  • ignoreExceptions:不触发标记包装服务为失败的异常列表
use PixelFederation\CircuitBreakerBundle\Annotation\CircuitBreaker;

class Service {
    /**
     * @CircuitBreaker(fallbackMethod="makeSpecialFallbackRequest") 
     */
    public function iShouldBeCircuitBroken(): int
    {
        return 0;
    }
}

use PixelFederation\CircuitBreakerBundle\Annotation\CircuitBreaker;

class Service {
    #[CircuitBreaker(fallbackMethod: 'makeSpecialFallbackRequest')]
    public function iShouldBeCircuitBroken(): int
    {
        return 0;
    }
}

**回退方法也必须是公共的**。

**重要**:回退方法必须与可回退方法的签名相同,因为回退方法将以相同的参数调用。

**重要**:将不支持Doctrine注解,因此建议使用包属性。

**关于复杂场景的注意事项**:在更复杂的场景中,也可以为回退方法定义回退方法。这种场景的一个例子可能是在默认/可回退方法中有一个API调用。它的回退方法可能对不同的API有不同的调用,这意味着这种方法也可以使用回退。在这种情况下,它可以配置一个尝试从某些缓存中加载数据的回退方法。这种方法也可能使用返回某些默认值的回退方法。

完整示例

use PixelFederation\CircuitBreakerBundle\Annotation\CircuitBreaker;
use PixelFederation\CircuitBreakerBundle\Annotation\CircuitBreakerService;
use PixelFederation\CircuitBreakerBundle\CircuitBrokenService;
use BadMethodCallException;
use InvalidArgumentException;
use RuntimeException;

/**
 * The class level annotation activates circuit breaking configuration on methods
 * marked with the @CircuitBreaker annotation.
 * In this case, also the default fallback for each circuit broken method is configured
 * (the 'makeDefaultFallbackRequest' method)
 * 
 * The ignoreExceptions option sets exceptions, on which occurrence the service won't be marked
 * as failing, e.g. some app/system level exceptions, which don't need to have to do anything 
 * with http requests under the hood.
 * 
 * @CircuitBreakerService(
 *    defaultFallback="makeDefaultFallbackRequest", 
 *    ignoreExceptions={BadMethodCallException::class}
 * )
 */
class TestService implements CircuitBrokenService
{
    private SomeHttpClient $client;

    public function __construct(SomeHttpClient $client)
    {
        $this->client = $client;
    }

    /**
     * This method is marked to be circuit-broken. It uses the class level configured fallback
     * and ignores the class level configured exceptions. 
     * 
     * @CircuitBreaker()
     */
    public function makeRequest(): int
    {
        return $this->client->makeRequest(); // it is important to set http timeouts here
    }

    /**
     * This method is marked to be circuit-broken. It uses a different fallback, not the one
     * configured on class level.
     * 
     * @CircuitBreaker(fallbackMethod="makeSpecialFallbackRequest")
     */
    public function makeRequestWithCustomCircuitBreaker(string $param): int
    {
        return $this->client->makeAnotherRequest($param); // it is important to set http timeouts here
    }

    public function makeDefaultFallbackRequest(): void
    {
        return 1; // ideally there is no call to any external dependency in the fallback method
    }
    
    // notice that this fallback method has the same method signature as the method makeRequestWithCustomCircuitBreaker
    public function makeSpecialFallbackRequest(string $param): void
    {
        return 0; // ideally there is no call to any external dependency in the fallback method
    }
}

祝您玩得开心 ;)