PHP异常处理与错误恢复策略
业务系统的健壮性取决于异常处理和错误恢复能力。今天说说PHP中高级的异常处理技巧和系统容错策略。
PHP7以后,大部分错误都可以用try-catch捕获。但有些致命错误还是捕获不了的。
```php
// 可以被捕获的错误
try {
// 类型错误
$result = 1 / 0;
} catch (DivisionByZeroError $e) {
echo "除零错误: {$e->getMessage()}\n";
}
try {
// 调用不存在的方法
$obj = new stdClass();
$obj->nonexistentMethod();
} catch (Error $e) {
echo "错误: {$e->getMessage()}\n";
}
// 不能捕获的致命错误
// try {
// // 内存耗尽 - 不能捕获
// $data = str_repeat("x", PHP_INT_MAX);
// } catch (Error $e) {
// echo "这行不会执行\n";
// }
?>
```
Graceful降级策略。当外部服务不可用时,返回缓存或默认值,而不是直接报错。
```php
class GracefulDegradation
{
private array $fallbacks = [];
public function withFallback(string $service, callable $primary, callable $fallback): mixed
{
try {
return $primary();
} catch (Exception $e) {
$this->logFailure($service, $e);
return $fallback($e);
}
}
private function logFailure(string $service, Exception $e): void
{
$log = sprintf(
"[%s] 服务降级: %s - %s\n",
date('Y-m-d H:i:s'),
$service,
$e->getMessage()
);
file_put_contents('/tmp/degradation.log', $log, FILE_APPEND);
}
}
class UserService
{
public function getUser(int $id): array
{
// 模拟远程调用
if (rand(0, 2) === 0) {
throw new RuntimeException("远程服务不可用");
}
return ['id' => $id, 'name' => '张三'];
}
public function getFromCache(int $id): array
{
return ['id' => $id, 'name' => '缓存用户', 'cached' => true];
}
}
$service = new UserService();
$degradation = new GracefulDegradation();
for ($i = 0; $i < 5; $i++) {
$user = $degradation->withFallback(
'user-service',
fn() => $service->getUser(1),
fn($e) => $service->getFromCache(1)
);
echo "用户: {$user['name']}" . (isset($user['cached']) ? ' (缓存)' : '') . "\n";
}
?>
```
重试模式。对于临时性故障,自动重试可以提高成功率。
```php
class RetryHandler
{
private int $maxAttempts;
private array $delays;
public function __construct(int $maxAttempts = 3, array $delays = [100, 500, 2000])
{
$this->maxAttempts = $maxAttempts;
$this->delays = $delays;
}
public function execute(callable $operation, callable $shouldRetry = null): mixed
{
$attempt = 0;
$lastException = null;
while ($attempt < $this->maxAttempts) {
try {
return $operation();
} catch (Exception $e) {
$lastException = $e;
$attempt++;
if ($attempt >= $this->maxAttempts) {
break;
}
if ($shouldRetry !== null && !$shouldRetry($e)) {
break;
}
$delay = $this->delays[$attempt - 1] ?? end($this->delays);
echo "尝试 #{$attempt} 失败,{$delay}ms后重试: {$e->getMessage()}\n";
usleep($delay * 1000);
}
}
throw new RuntimeException("操作失败,已重试{$this->maxAttempts}次", 0, $lastException);
}
public function executeWithExponentialBackoff(callable $operation, int $maxAttempts = 5): mixed
{
$attempt = 0;
while ($attempt < $maxAttempts) {
try {
return $operation();
} catch (Exception $e) {
$attempt++;
if ($attempt >= $maxAttempts) {
throw $e;
}
$delay = pow(2, $attempt) * 100 + rand(0, 100);
usleep($delay * 1000);
}
}
return null;
}
}
$retry = new RetryHandler();
try {
$result = $retry->execute(function () {
if (rand(0, 3) < 2) {
throw new RuntimeException("临时错误");
}
return "成功!";
});
echo $result . "\n";
} catch (RuntimeException $e) {
echo "最终失败: {$e->getMessage()}\n";
}
?>
```
断路器模式。连续失败一定次数后暂时关闭服务,避免无效重试。
```php
class CircuitBreaker
{
private string $name;
private int $failureThreshold;
private int $resetTimeout;
private int $failureCount = 0;
private int $lastFailureTime = 0;
private string $state = 'closed';
public function __construct(string $name, int $failureThreshold = 5, int $resetTimeout = 30)
{
$this->name = $name;
$this->failureThreshold = $failureThreshold;
$this->resetTimeout = $resetTimeout;
}
public function call(callable $operation, callable $fallback = null): mixed
{
if ($this->isOpen()) {
if ($this->shouldAttemptReset()) {
$this->state = 'half-open';
} else {
echo "断路器已打开: {$this->name}\n";
return $fallback ? $fallback() : null;
}
}
try {
$result = $operation();
$this->onSuccess();
return $result;
} catch (Exception $e) {
$this->onFailure();
echo "调用失败 ({$this->failureCount}/{$this->failureThreshold}): {$e->getMessage()}\n";
return $fallback ? $fallback() : null;
}
}
private function isOpen(): bool
{
return $this->state === 'open';
}
private function shouldAttemptReset(): bool
{
return time() - $this->lastFailureTime >= $this->resetTimeout;
}
private function onSuccess(): void
{
$this->state = 'closed';
$this->failureCount = 0;
}
private function onFailure(): void
{
$this->failureCount++;
$this->lastFailureTime = time();
if ($this->failureCount >= $this->failureThreshold) {
$this->state = 'open';
echo "断路器打开: {$this->name} ({$this->resetTimeout}秒后尝试重置)\n";
}
}
}
$breaker = new CircuitBreaker('redis-cache', 3, 10);
for ($i = 0; $i < 10; $i++) {
$result = $breaker->call(
function () {
if (rand(0, 2) === 0) {
return "成功获取数据";
}
throw new RuntimeException("连接超时");
},
function () {
return "使用本地缓存";
}
);
echo "结果: $result\n";
sleep(1);
}
?>
```
PHP的异常处理机制从PHP7开始已经很完善了。用好try-catch、降级、重试和断路器模式,可以构建稳定的系统。但异常处理也有成本,不要用异常来控制正常流程,只在真正的异常情况使用。
PHP异常处理与错误恢复策略