geggleto / php-circuit-breaker
PHP 电路断路器组件
Requires
- php: >=5.6.0
Requires (Dev)
- phpunit/phpunit: ^5.2
README
什么是php-circuit-breaker
这是一个组件,帮助您优雅地处理外部服务的故障和超时(通常是远程、第三方服务)。
这是一个提供非常易于使用的电路断路器组件的库。它不需要外部依赖,并提供了APC和Memcached的默认存储实现,但可以通过多种方式扩展。
框架支持
这个库不需要任何特定的PHP框架,您只需要PHP 5.6或更高版本。
动机与好处
- 允许应用程序在无需人工干预的情况下检测故障并适应其行为。
- 通过在模块中添加故障安全功能来提高服务的健壮性。
安装
"require": {
"geggleto/php-circuit-breaker": "^0.3"
},
之后,您应该更新composer依赖项,然后就可以开始使用了。
用例 - 非关键功能
- 您的应用程序有一个非关键功能,如:用户跟踪、统计、推荐等
- 此可选功能使用远程服务,这会导致您的应用程序出现故障。
- 当“非关键功能”失败时,您希望保持应用程序和核心流程可用。
您的应用程序代码可能如下所示
$factory = new Ejsmont\CircuitBreaker\Factory(); $circuitBreaker = $factory->getSingleApcInstance(30, 300); $userProfile = null; if( $circuitBreaker->isAvailable("UserProfileService") ){ try{ $userProfile = $userProfileService->loadProfileOrWhatever(); $circuitBreaker->reportSuccess("UserProfileService"); }catch( UserProfileServiceConnectionException $e ){ // network failed - report it as failure $circuitBreaker->reportFailure("UserProfileService"); }catch( Exception $e ){ // something went wrong but it is not service's fault, dont report as failure } } if( $userProfile === null ){ // for example, show 'System maintenance, you cant login now.' message // but still let people buy as logged out customers. }
用例 - 支付网关
- Web应用程序依赖于第三方服务(例如支付网关)。
- Web应用程序需要跟踪第三方服务是否不可用。
- 应用程序不能变慢/不可用,它必须告诉用户功能有限或隐藏它们。
- 应用程序在使用结账页面渲染之前使用电路断路器,如果特定的支付网关不可用,则将从用户那里隐藏支付选项。
如您所见,这是一个非常强大的概念,可以在运行时选择性禁用功能,但仍然允许核心业务流程不间断。
与支付服务通信的后端可能如下所示
$factory = new Ejsmont\CircuitBreaker\Factory(); $circuitBreaker = $factory->getSingleApcInstance(30, 300); try{ // try to process the payment // then tell circuit breaker that it went well $circuitBreaker->reportSuccess("PaymentOptionOne"); }catch( SomePaymentConnectionException $e ){ // If you get network error report it as failure $circuitBreaker->reportFailure("PaymentOptionOne"); }catch( Exception $e ){ // in case of your own error handle it however it makes sense but // dont tell circuit breaker it was 3rd party service failure }
由于您正在记录失败和成功的操作,您现在可以在前端以及隐藏失败的支付选项。
渲染可用支付选项的前端可能如下所示
$factory = new Ejsmont\CircuitBreaker\Factory(); $circuitBreaker = $factory->getSingleApcInstance(30, 300); if ($circuitBreaker->isAvailable("PaymentOptionOne")) { // display the option }
功能
- 通过单个电路断路器实例跟踪多个服务。
- 提供可插拔的后端适配器,默认提供APC和Memcached。
- 可自定义的服务阈值。您可以定义需要多少次失败才认为服务已关闭。
- 可自定义的重试超时。您不希望永久禁用服务。在提供的时间超时后,电路断路器将允许单个进程尝试
- 此分支包括在服务修改为失败状态时执行代码块的能力
触发断路器
当提供每个服务的TrippedHandler时,可以在断路器触发时执行代码。TrippedHandler负责记录您的服务故障。断路器将响应两种状态:不可用和重试。第一次断路器触发时,将发送不可用消息“服务不再可用”,而在每次重试尝试之后,将发送“正在重试服务”消息。为这些消息提供了Get/Set方法。
以下是一个EmailHandler
的示例
use Ejsmont\CircuitBreaker\TrippedHandlerInterface; class EmailHandler implements TrippedHandlerInterface { protected $targetEmail = ''; protected $headers = ''; public function __construct($targetEmail) { $this->targetEmail = $targetEmail; $this->headers = 'From: xxx@xxx.ca' . "\r\n" . 'Reply-To: xxx@xxx.ca' . "\r\n" . 'X-Mailer: PHP/' . phpversion(); } public function __invoke($serviceName, $count, $message) { mail($this->targetEmail, "Service Outage: " . $serviceName, $message, $this->headers); } }
以下是如何注册它。这为“数据库”服务添加了一个处理程序
$circuitBreaker = $factory->getSingleApcInstance(5, 30); $circuitBreaker->registerHandler("Database", new \Handler\EmailHandler("your_email@your_domain.com"));
性能影响
电路断路器的开销可以忽略不计。
APC实现大约需要0.0002秒来执行isAvailable()操作,然后报告Success()或reportFailure()。
Memcache适配器在与本地memcached进程通信时在大约0.0005秒范围内。
唯一可能影响性能的因素是网络连接时间。如果您选择使用远程memcached服务器或实现自己的自定义StorageAdapter。
详细信息
在文档更新之前,您可以在我的博客上了解更多关于熔断器及其实现的概念:http://artur.ejsmont.org/blog/circuit-breaker
一些实现细节已更改,但核心逻辑保持不变。
(更新) 您可以阅读我的博客,了解我是如何使用这个包的,http://bolt.tamingtheelephpant.com/page/circuit-breakers-failing-gracefully
单元测试
phpunit -c tests/phpunit.xml --bootstrap tests/bootstrap.php tests
作者
- Artur Esjmont (https://github.com/ejsmont-artur) 通过 http://artur.ejsmont.org
- Glenn Eggleton (https://github.com/geggleto)