2024-02-11 12:35:11
在 Laravel 6 中,为确保 Redis 连接的稳定性,需采用健壮的异常处理机制。以下是基于 Throwable 的完整解决方案,涵盖连接检测、异常捕获、日志记录及降级策略:
1. 核心检测逻辑:连接 + Ping 验证直接使用 Redis::connection() 结合 ping() 方法验证连接状态,并通过 getLastError() 获取底层错误详情:
try { $redis = Redis::connection(); if (!$redis->ping()) { Log::error('Redis connection failed: ' . $redis->getLastError()); // 执行降级策略(如切换本地缓存) return $this->handleRedisFailure(); } // 正常操作示例:获取键值 $value = $redis->get('key');} catch (Throwable $e) { Log::error('Redis connection error: ' . $e->getMessage()); return $this->handleRedisFailure();}ping() 比直接操作数据更轻量,适合快速验证连接。
getLastError() 提供 Redis 驱动层的原始错误(如网络超时、认证失败)。
PHP 的 Throwable 是所有错误和异常的基类,覆盖范围比 Exception 更广(包括 Error 类型):
catch (Throwable $e) { // 捕获所有可能的错误,包括语法错误、内存耗尽等}当 Redis 驱动内部抛出非 Exception 类型的错误(如 PHP 7+ 的 TypeError)。
确保代码不会因未捕获的错误导致进程终止。
使用 Laravel 的日志系统记录异常上下文,便于后续分析:
Log::error('Redis operation failed', [ 'exception' => get_class($e), 'message' => $e->getMessage(), 'file' => $e->getFile(), 'line' => $e->getLine(), 'context' => [ 'redis_command' => 'get', // 记录失败的命令 'key' => 'example_key', // 关联的键名 ]]);结构化日志可被 ELK(Elasticsearch+Logstash+Kibana)等工具解析。
包含堆栈轨迹和自定义上下文,加速问题定位。
当 Redis 不可用时,提供备用方案以维持核心功能:
protected function handleRedisFailure(){ // 示例1:返回缓存的旧数据(需提前设计缓存失效机制) $cachedValue = Cache::store('file')->get('key'); // 切换到文件缓存 // 示例2:直接返回默认值 $defaultValue = 'default_response'; // 示例3:显示用户友好页面 return response()->view('errors.redis_down', [ 'retry_after' => 30 // 建议重试时间 ], 503);}幂等性:确保降级操作不会引发副作用(如重复下单)。
可观测性:通过日志或监控系统标记降级事件。
自动恢复:结合队列或定时任务检测 Redis 恢复状态。
重试逻辑:对瞬时错误(如网络抖动)自动重试:
use IlluminateSupportInteractsWithTime;$maxRetries = 3;$retryDelay = 100; // 毫秒for ($i = 0; $i < $maxRetries; $i++) { try { $redis = Redis::connection(); if ($redis->ping()) { return $redis->get('key'); // 成功则返回 } } catch (Throwable $e) { if ($i === $maxRetries - 1) throw $e; // 最后一次重试失败则抛出 usleep($retryDelay * 1000); // 指数退避可优化此处 }}断路器:使用第三方包(如 php-di/php-di 或 guzzlehttp/guzzle 的断路器中间件)在连续失败后暂时拒绝请求,避免雪崩效应。
在 config/database.php 中优化 Redis 配置:
'redis' => [ 'client' => 'predis', // 或 phpredis 'options' => [ 'connection_timeout' => 5, // 连接超时(秒) 'read_write_timeout' => 10, // 读写超时 'persistent_connections' => false, // 非持久化连接更易恢复 ], 'default' => [ 'host' => env('REDIS_HOST', '127.0.0.1'), 'password' => env('REDIS_PASSWORD', null), 'database' => 0, ],],connection_timeout:缩短超时时间以快速失败。
persistent_connections:禁用持久化连接(PHP-FPM 场景下可能引发连接泄漏)。
此方案可显著减少 Redis 服务中断对业务的影响,同时为运维提供足够的排查依据。