本文目录导读:

在PHP项目中实现熔断状态的自动探测恢复,通常涉及以下几个核心机制和实现思路:
基于时间窗口的自动恢复(最常见)
实现思路
熔断器在进入“打开”状态后,会启动一个定时器,当定时器超时后,自动进入“半开”状态,允许少量请求通过以探测服务是否恢复。
class CircuitBreaker {
private $state = 'closed'; // closed, open, half-open
private $failureCount = 0;
private $successCount = 0;
private $lastFailureTime;
private $openTimeout = 30; // 熔断打开后等待30秒再尝试恢复
private $halfOpenMaxRequests = 5; // 半开状态下允许的探测请求数
public function isAvailable() {
if ($this->state === 'open') {
// 检查是否已过熔断时间窗口
if (time() - $this->lastFailureTime >= $this->openTimeout) {
$this->state = 'half-open';
$this->resetCounters();
return true;
}
return false;
}
if ($this->state === 'half-open') {
// 限制半开状态下的探测请求数量
if ($this->successCount + $this->failureCount >= $this->halfOpenMaxRequests) {
return true;
}
return false;
}
return true;
}
public function recordSuccess() {
if ($this->state === 'half-open') {
$this->successCount++;
// 连续成功达到阈值,恢复为关闭状态
if ($this->successCount >= $this->halfOpenMaxRequests) {
$this->state = 'closed';
$this->resetCounters();
}
}
}
public function recordFailure() {
if ($this->state === 'half-open') {
$this->failureCount++;
// 半开状态下任意失败,立即回到打开状态
if ($this->failureCount >= 1) {
$this->state = 'open';
$this->lastFailureTime = time();
$this->resetCounters();
}
}
}
}
使用Redis实现分布式熔断恢复
class RedisCircuitBreaker {
private $redis;
private $serviceName;
private $openTimeout = 30;
private $halfOpenMaxRequests = 5;
public function __construct($redis, $serviceName) {
$this->redis = $redis;
$this->serviceName = $serviceName;
}
public function isAvailable() {
$state = $this->redis->get("circuit_breaker:{$this->serviceName}:state");
if ($state === 'open') {
$lastFailure = $this->redis->get("circuit_breaker:{$this->serviceName}:last_failure");
if (time() - $lastFailure >= $this->openTimeout) {
// 自动切换到半开状态
$this->redis->set("circuit_breaker:{$this->serviceName}:state", 'half-open');
$this->redis->set("circuit_breaker:{$this->serviceName}:attempts", 0);
return true;
}
return false;
}
if ($state === 'half-open') {
$attempts = $this->redis->incr("circuit_breaker:{$this->serviceName}:attempts");
if ($attempts <= $this->halfOpenMaxRequests) {
return true;
}
return false;
}
return true;
}
}
集成健康检查的健康探测
class HealthCheckCircuitBreaker {
private $healthCheckUrl;
private $healthCheckInterval = 60; // 每60秒检查一次
private $lastHealthCheckTime = 0;
public function checkServiceHealth() {
if (time() - $this->lastHealthCheckTime < $this->healthCheckInterval) {
return true; // 缓存有效,避免频繁检查
}
try {
$ch = curl_init($this->healthCheckUrl);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 5);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($httpCode === 200) {
$this->lastHealthCheckTime = time();
return true;
}
return false;
} catch (Exception $e) {
return false;
}
}
}
渐进式恢复策略
class GradualRecoveryCircuitBreaker {
private $recoverySteps = [
1 => ['timeout' => 30, 'maxRequests' => 1],
2 => ['timeout' => 60, 'maxRequests' => 5],
3 => ['timeout' => 120, 'maxRequests' => 20],
];
private $currentStep = 1;
public function getRecoveryConfig() {
$step = $this->currentStep;
if ($step > count($this->recoverySteps)) {
$step = count($this->recoverySteps);
}
return $this->recoverySteps[$step];
}
public function recordRecoverySuccess() {
// 每次成功恢复,提升恢复步骤
$this->currentStep = min($this->currentStep + 1, 3);
}
public function recordRecoveryFailure() {
// 恢复失败,降级到最低步骤
$this->currentStep = 1;
}
}
完整实现示例
class AutoRecoveryServiceCircuitBreaker {
private $state = 'closed';
private $failureCount = 0;
private $failureThreshold = 5; // 连续失败5次熔断
private $openTimeout = 30; // 熔断30秒后尝试恢复
private $halfOpenMaxRequests = 3;
private $lastFailureTime = 0;
private $requestCount = 0;
// 核心探测逻辑
public function callService($callback) {
if (!$this->isAvailable()) {
throw new \Exception('Circuit breaker is open');
}
try {
$result = $callback();
$this->recordSuccess();
return $result;
} catch (\Exception $e) {
$this->recordFailure();
throw $e;
}
}
// 自动探测恢复
public function probeRecovery() {
if ($this->state !== 'open') {
return true; // 状态不需要探测
}
// 检查时间窗口是否已过
if (time() - $this->lastFailureTime < $this->openTimeout) {
return false;
}
// 尝试进行健康检查
if ($this->healthCheck()) {
$this->state = 'half-open';
$this->requestCount = 0;
return true;
}
// 恢复失败,重置计时器
$this->lastFailureTime = time();
return false;
}
private function healthCheck() {
try {
// 对目标服务发送一个简单的健康检查请求
$ch = curl_init('http://target-service/health');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_TIMEOUT, 3);
$response = curl_exec($ch);
$httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
return $httpCode === 200;
} catch (\Exception $e) {
return false;
}
}
public function isAvailable() {
// 自动触发探测
if ($this->state === 'open') {
$this->probeRecovery();
}
if ($this->state === 'half-open') {
// 限制半开状态下的请求
$this->requestCount++;
return $this->requestCount <= $this->halfOpenMaxRequests;
}
return $this->state === 'closed';
}
public function recordSuccess() {
if ($this->state === 'half-open') {
$this->requestCount++;
if ($this->requestCount >= $this->halfOpenMaxRequests) {
$this->state = 'closed';
$this->resetCounters();
}
}
}
public function recordFailure() {
$this->failureCount++;
$this->lastFailureTime = time();
if ($this->state === 'closed' && $this->failureCount >= $this->failureThreshold) {
$this->state = 'open';
}
if ($this->state === 'half-open') {
$this->state = 'open';
}
}
private function resetCounters() {
$this->failureCount = 0;
$this->requestCount = 0;
}
}
最佳实践建议
- 使用Redis/Memcached:在分布式环境中,熔断状态需要共享
- 设置合理的超时时间:
openTimeout建议设置为服务平均恢复时间的1.5-2倍 - 监控和告警:记录熔断切换事件,设置告警
- 渐进式恢复:避免突然大量请求压垮正在恢复的服务
- 健康检查端点:确保目标服务有专门的健康检查端点
这种设计能够在服务恢复时自动探测并逐渐恢复流量,避免人工干预,提高系统的自愈能力。