如何改写StackExchange.Redis.ConnectionMultiplexer.Connect添加错误日志?
问题描述
我写了一个Cache类用于Redis缓存操作,代码如下:
internal class Cache : ICache { private ConnectionMultiplexer mux; private ILogger logger; public Cache(ILogger logger) { this.logger = logger; } public async Task SetKeyValue(string key, string value) { . . . Connect(); . . await this.mux.GetDatabase().StringSetAsync(key, value, expiry); } private void Connect() { this.mux = StackExchange.Redis.ConnectionMultiplexer.Connect(connectionString); this.mux.ConnectionFailed += (sender, args) => { this.logger.LogError(args.Exception, $"Connection Failed: {args.Exception.ToString()}"); }; this.mux.InternalError += (sender, args) => { this.logger.LogError(args.Exception, $"Internal Error: {args.Exception.ToString()}"); }; } }
我的核心需求是:
- 捕获
StackExchange.Redis.ConnectionMultiplexer.Connect的连接失败异常并记录 - 捕获
await this.mux.GetDatabase().StringSetAsync(key, value, expiry)的设置失败异常并记录
目前的Connect实现如上,想知道有没有更优方案。我考虑过重写/重载ConnectionMultiplexer.Connect方法,或者利用它的分部类特性,但不确定具体操作方式。当前ConnectionMultiplexer的Connect方法定义如下:
public sealed partial class ConnectionMultiplexer : IInternalConnectionMultiplexer // implies : IConnectionMultiplexer and : IDisposable { . . . public static ConnectionMultiplexer Connect(string configuration, TextWriter log = null) { return Connect(ConfigurationOptions.Parse(configuration), log); } . . . }
优化方案
一、修复当前连接逻辑的核心问题
避免重复创建连接实例
StackExchange.Redis的ConnectionMultiplexer是线程安全的,官方推荐复用单个实例而非每次操作都创建。你当前每次调用SetKeyValue都执行Connect(),会导致资源浪费,应该在构造函数或首次使用时初始化一次。捕获首次连接的直接异常
ConnectionMultiplexer.Connect本身会抛出连接失败异常(比如配置错误、网络不可达),你当前订阅的ConnectionFailed事件只能捕获后续运行中的连接异常,无法覆盖首次连接失败场景,需要用try-catch包裹初始化代码:
private void InitializeConnection() { try { this.mux = StackExchange.Redis.ConnectionMultiplexer.Connect(connectionString); this.mux.ConnectionFailed += (sender, args) => { this.logger.LogError(args.Exception, "Redis连接中断"); }; this.mux.InternalError += (sender, args) => { this.logger.LogError(args.Exception, "Redis内部错误"); }; this.mux.ConnectionRestored += (sender, args) => { this.logger.LogInformation("Redis连接已恢复"); }; } catch (Exception ex) { this.logger.LogError(ex, "Redis首次连接失败"); throw; // 按需决定是否抛出异常让上层处理 } }
二、处理StringSetAsync的操作异常
直接在SetKeyValue方法中用try-catch包裹异步调用,精准捕获操作失败的异常:
public async Task SetKeyValue(string key, string value, TimeSpan? expiry = null) { // 确保连接已初始化(构造函数中调用InitializeConnection) if (this.mux == null) { InitializeConnection(); } try { await this.mux.GetDatabase().StringSetAsync(key, value, expiry); } catch (RedisException ex) { this.logger.LogError(ex, $"Redis设置键值失败,Key: {key}"); throw; } catch (Exception ex) { this.logger.LogError(ex, $"Redis设置键值时发生未知错误,Key: {key}"); throw; } }
三、关于重载/重写Connect和分部类的说明
- 无法重写
Connect方法:ConnectionMultiplexer是sealed密封类,不允许继承,因此无法重写它的方法。 - 分部类特性无法利用:虽然它是分部类,但分部类的扩展只能在同一个程序集内进行,你的代码在独立程序集里,无法通过分部类添加自定义逻辑。
- 替代方案:封装连接工具类
如果想统一管理Redis连接的日志和初始化逻辑,可以封装静态工具类,避免重复代码:
public static class RedisConnectionFactory { public static ConnectionMultiplexer Create(string connectionString, ILogger logger) { try { var multiplexer = ConnectionMultiplexer.Connect(connectionString); multiplexer.ConnectionFailed += (s, e) => logger.LogError(e.Exception, "Redis连接失败"); multiplexer.InternalError += (s, e) => logger.LogError(e.Exception, "Redis内部错误"); multiplexer.ConnectionRestored += (s, e) => logger.LogInformation("Redis连接恢复"); return multiplexer; } catch (Exception ex) { logger.LogError(ex, "Redis连接初始化失败"); throw; } } }
在Cache类构造函数中调用:
public Cache(ILogger logger, string connectionString) { this.logger = logger; this.mux = RedisConnectionFactory.Create(connectionString, logger); }
四、额外优化建议
- 实现
IDisposable接口:Cache类应实现IDisposable,在Dispose方法中释放ConnectionMultiplexer资源。 - 使用
ConfigurationOptions配置:相比字符串配置,ConfigurationOptions更灵活,可以设置重试策略、连接超时等参数。 - 添加连接状态监控:订阅
ConnectionStatusChanged事件,实时记录连接状态变化。
内容的提问来源于stack exchange,提问作者JCB
相关产品推荐
相关产品推荐

