.NET Core 6中StackExchange.Redis连接池缺失add_ServerMaintenanceEvent实现
异常根源
这个异常是版本兼容性问题:你使用的StackExchange.Redis.MultiplexerPool(1.0.2版本)与当前依赖的StackExchange.Redis主库版本不匹配。新版本的StackExchange.Redis在IConnectionMultiplexer接口中新增了ServerMaintenanceEvent事件,但MultiplexerPool库的内部实现类InternalDisposableConnectionMultiplexer没有实现该事件的add_ServerMaintenanceEvent/remove_ServerMaintenanceEvent方法,导致类型加载失败。
解决步骤
1. 版本对齐修复
- 查看
StackExchange.Redis.MultiplexerPool的兼容说明,将StackExchange.Redis主库版本降级到与MultiplexerPool 1.0.2匹配的版本(通常是StackExchange.Redis 2.x系列,比如2.6.70及以下)。 - 或者直接升级
StackExchange.Redis.MultiplexerPool到最新版,确保其与当前StackExchange.Redis版本兼容。
2. 代码实现优化(附带问题修正)
除了版本问题,你的类库实现还有几个需要调整的点:
(1)配置硬编码问题
LibraryRedisCacheService构造函数中硬编码了连接字符串,应该从配置注入,保持类库灵活性:
// 修改构造函数,接收配置参数 public LibraryRedisCacheService(IConfiguration configuration) { var connectionString = configuration.GetValue<string>("Redis:ConnectionString"); var configureOptions = ConfigurationOptions.Parse(connectionString); configureOptions.AllowAdmin = true; configureOptions.AsyncTimeout = 10000; _connectionMultiplexerPool = ConnectionMultiplexerPoolFactory.Create( poolSize: 10, configurationOptions: configureOptions, connectionSelectionStrategy: ConnectionSelectionStrategy.RoundRobin); _connectionsErrorCount = new int[_connectionMultiplexerPool.PoolSize]; }
(2)错误计数索引逻辑错误
原代码中_connectionsErrorCount[connection.ConnectionIndex++]会导致错误计数对应到下一个连接的索引,修正为:
catch (RedisConnectionException) { var currentIndex = connection.ConnectionIndex; _connectionsErrorCount[currentIndex]++; if (_connectionsErrorCount[currentIndex] < 3) { throw; } await connection.ReconnectAsync(); return await op(connection.Connection.GetDatabase()); }
(3)递归重试的栈溢出风险
SetCacheAsync和GetCacheAsync使用递归重试,高重试次数可能导致栈溢出,改为循环实现:
public async Task SetCacheAsync<T>(string key, T value, TimeSpan? expiry = null) { int currentRetry = 0; while (currentRetry <= 3) { try { await QueryRedisAsync(async db => await db.StringSetAsync(key, JsonConvert.SerializeObject(value), expiry ?? TimeSpan.FromHours(12))); return; } catch (Exception) { if (currentRetry >= 3) throw; await Task.Delay(3000); currentRetry++; } } }
(4)DI注册优化
原注册方式直接实例化Library1,没有利用DI容器的依赖注入能力,改为让Library1通过构造函数注入缓存服务:
// 先注册缓存服务 services.AddSingleton<LibraryRedisCacheService>(); // 注册类库,让容器自动注入依赖 services.AddSingleton<ILibrary1, Library1>(sp => new Library1(sp.GetRequiredService<IConfiguration>(), "KeyvaultReference", sp.GetRequiredService<LibraryRedisCacheService>())); // 修改Library1的构造函数 public class Library1 : ILibrary1 { private readonly LibraryRedisCacheService _redisClient; public Library1(IConfiguration configuration, string azureOptionsKey, LibraryRedisCacheService redisClient) { configuration.Bind(azureOptionsKey, azureOptions); _redisClient = redisClient; } }
3. 替代方案(如果MultiplexerPool不再维护)
如果StackExchange.Redis.MultiplexerPool已经停止维护,建议直接使用StackExchange.Redis原生的ConnectionMultiplexer:
ConnectionMultiplexer本身是线程安全的,单实例即可支撑高并发场景,大部分情况下不需要额外的连接池。- 若确实需要多连接实例,可以自己封装一个简单的连接池,基于
List<IConnectionMultiplexer>实现轮询或其他选择策略。
内容的提问来源于stack exchange,提问作者user3501613

