You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何改写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);
    }
    .
    .
    .
}

优化方案

一、修复当前连接逻辑的核心问题

  1. 避免重复创建连接实例
    StackExchange.Redis的ConnectionMultiplexer是线程安全的,官方推荐复用单个实例而非每次操作都创建。你当前每次调用SetKeyValue都执行Connect(),会导致资源浪费,应该在构造函数或首次使用时初始化一次。

  2. 捕获首次连接的直接异常
    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和分部类的说明

  1. 无法重写Connect方法:ConnectionMultiplexer是sealed密封类,不允许继承,因此无法重写它的方法。
  2. 分部类特性无法利用:虽然它是分部类,但分部类的扩展只能在同一个程序集内进行,你的代码在独立程序集里,无法通过分部类添加自定义逻辑。
  3. 替代方案:封装连接工具类
    如果想统一管理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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 23:35:42