C# Azure Redis缓存-ConnectionMultiplexer对象池

C# Azure Redis缓存-ConnectionMultiplexer对象池,c#,caching,azure,stackexchange.redis,azure-redis-cache,C#,Caching,Azure,Stackexchange.redis,Azure Redis Cache,我们在应用程序中使用c1azureredis缓存。最近,我们在GET操作上遇到了很多超时 ,可能的解决方案之一是实现ConnectionMultiplexer对象池 另一种可能的解决方案是使用ConnectionMultiplexer池 对象,并选择“加载最少的” 发送新请求时的ConnectionMultiplexer。这应该可以防止 导致其他请求也超时的单个超时 使用C#实现一个ConnectionMultiplexer对象池会是什么样子 编辑: 如果您使用的是StackExchange.R

我们在应用程序中使用c1azureredis缓存。最近,我们在GET操作上遇到了很多超时

,可能的解决方案之一是实现ConnectionMultiplexer对象池

另一种可能的解决方案是使用ConnectionMultiplexer池 对象,并选择“加载最少的” 发送新请求时的ConnectionMultiplexer。这应该可以防止 导致其他请求也超时的单个超时

使用C#实现一个ConnectionMultiplexer对象池会是什么样子

编辑:


如果您使用的是StackExchange.Redis,根据这一点,您可以在connection multiplexer对象上使用TotalUnderstand属性

下面是我提出的一个实现,它工作正常:

public static int POOL_SIZE = 100;
private static readonly Object lockPookRoundRobin = new Object();
private static Lazy<Context>[] lazyConnection = null;

//Static initializer to be executed once on the first call
private static void InitConnectionPool()
{
    lock (lockPookRoundRobin)
    {
        if (lazyConnection == null) {
             lazyConnection = new Lazy<Context>[POOL_SIZE];
        }


        for (int i = 0; i < POOL_SIZE; i++){
            if (lazyConnection[i] == null)
                lazyConnection[i] = new Lazy<Context>(() => new Context("YOUR_CONNECTION_STRING", new CachingFramework.Redis.Serializers.JsonSerializer()));
        }
    }
}

private static Context GetLeastLoadedConnection()
{
    //choose the least loaded connection from the pool
    /*
    var minValue = lazyConnection.Min((lazyCtx) => lazyCtx.Value.GetConnectionMultiplexer().GetCounters().TotalOutstanding);
    var lazyContext = lazyConnection.Where((lazyCtx) => lazyCtx.Value.GetConnectionMultiplexer().GetCounters().TotalOutstanding == minValue).First();
    */

    // UPDATE following @Luke Foust comment below
    Lazy<Connection> lazyContext;

    var loadedLazys = lazyConnection.Where((lazy) => lazy.IsValueCreated);
    if(loadedLazys.Count()==lazyConnection.Count()){
        var minValue = loadedLazys.Min((lazy) => lazy.Value.TotalOutstanding);
        lazyContext = loadedLazys.Where((lazy) => lazy.Value.TotalOutstanding == minValue).First();
    }else{
        lazyContext = lazyConnection[loadedLazys.Count()];
    }
    return lazyContext.Value;
}

private static Context Connection
{
    get
    {
        lock (lockPookRoundRobin)
        {
            return GetLeastLoadedConnection();
        }
    }
}

public RedisCacheService()
{
    InitConnectionPool();
}
publicstaticintpool\u SIZE=100;
私有静态只读对象lockPookRoundRobin=新对象();
私有静态惰性[]lazyConnection=null;
//在第一次调用时执行一次静态初始值设定项
私有静态void InitConnectionPool()
{
锁(lockPookRoundRobin)
{
if(lazyConnection==null){
lazyConnection=newlazy[POOL_SIZE];
}
对于(int i=0;inew Context(“您的连接字符串”,new cachingframew.Redis.Serializers.JsonSerializer());
}
}
}
私有静态上下文GetLeastLoadedConnection()
{
//从池中选择加载最少的连接
/*
var minValue=lazyConnection.Min((lazyCtx)=>lazyCtx.Value.GetConnectionMultiplexer().GetCounters().totalsumment);
var lazyContext=lazyConnection.Where((lazyCtx)=>lazyCtx.Value.GetConnectionMultiplexer().GetCounters().TotalUnderstanding==minValue).First();
*/
//更新以下@Luke Foust评论
懒散的环境;
var loadedLazys=lazyConnection.Where((lazy)=>lazy.IsValueCreated);
if(loadedLazys.Count()==lazyConnection.Count()){
var minValue=loadedLazys.Min((lazy)=>lazy.Value.totalsumment);
lazyContext=loadedLazys.Where((lazy)=>lazy.Value.TotalUnderstanding==minValue.First();
}否则{
lazyContext=lazyConnection[loadedLazys.Count()];
}
返回lazyContext.Value;
}
私有静态上下文连接
{
得到
{
锁(lockPookRoundRobin)
{
返回GetLeastLoadedConnection();
}
}
}
公共再缓存服务()
{
InitConnectionPool();
}

您也可以通过使用

示例代码:

using StackExchange.Redis;
using StackExchange.Redis.Extensions.Core.Abstractions;
using StackExchange.Redis.Extensions.Core.Configuration;
using System;
using System.Collections.Concurrent;
using System.Linq;

namespace Pool.Redis
{
/// <summary>
/// Provides redis pool
/// </summary>
public class RedisConnectionPool : IRedisCacheConnectionPoolManager
{
    private static ConcurrentBag<Lazy<ConnectionMultiplexer>> connections;
    private readonly RedisConfiguration redisConfiguration;

    public RedisConnectionPool(RedisConfiguration redisConfiguration)
    {
        this.redisConfiguration = redisConfiguration;
        Initialize();
    }

    public IConnectionMultiplexer GetConnection()
    {
        Lazy<ConnectionMultiplexer> response;
        var loadedLazys = connections.Where(lazy => lazy.IsValueCreated);

        if (loadedLazys.Count() == connections.Count)
        {
            response = connections.OrderBy(x => x.Value.GetCounters().TotalOutstanding).First();
        }
        else
        {
            response = connections.First(lazy => !lazy.IsValueCreated);
        }

        return response.Value;
    }

    private void Initialize()
    {
        connections = new ConcurrentBag<Lazy<ConnectionMultiplexer>>();

        for (int i = 0; i < redisConfiguration.PoolSize; i++)
        {
            connections.Add(new Lazy<ConnectionMultiplexer>(() => ConnectionMultiplexer.Connect(redisConfiguration.ConfigurationOptions)));
        }
    }

    public void Dispose()
    {
        var activeConnections = connections.Where(lazy => lazy.IsValueCreated).ToList();
        activeConnections.ForEach(connection => connection.Value.Dispose());
        Initialize();
    }
}

您是否正在执行任何特别长时间运行的操作?在跳转到池之前,我很想了解这是否是延迟、带宽饱和、服务器拥塞等问题。@MarcGravel-我们几乎解决了所有超时问题,只需重新编写一些代码即可获得更好的性能。这并不是真的需要,但我仍然有兴趣看到一段实现连接多路复用器池的代码。@Jakuboholovsky你能分享一些关于如何重新编写以获得更好性能的经验吗?@huangcd-是的,当然,在这里看看我的答案@Jakuboholovsky谢谢!对lazyConnection.Min的调用不会使用整个池进行连接/初始化,从而使Lazy的使用无效吗?你完全正确,我更新了答案以解决此问题并正确使用连接池。您可以在线测试此代码:@Gomino,这里的上下文是什么。我可以从Microsoft.AspNetCore.Hosting.Internal.Hosting应用程序的一个包中看到它。它的构造函数中没有参数。您指的是哪个包获取上下文对象。也在这一行懒散的背景下;从何处获得连接类?@AnilPurswani您现在应该使用
RedisContext
类。实际上,
Context
类来自
CachingFramework.Redis
,但在2018年5月被commit标记为过时,并被commit@Gomino删除,我使用的是StackExchange.Redis,所以我使用了ConnectionMultiplexer,效果很好。同样像在您提供的链接中一样,我在dispose方法中使用了“GetCounters().totalsupernative”,您正在调用
Initialize
,它真的应该存在吗?
            return new RedisConfiguration()
        {
            AbortOnConnectFail = true,
            Hosts = new RedisHost[] {
                                      new RedisHost() 
                                      {
                                          Host = ConfigurationManager.AppSettings["RedisCacheAddress"].ToString(),
                                          Port = 6380
                                      },
                                    },
            ConnectTimeout = Convert.ToInt32(ConfigurationManager.AppSettings["RedisTimeout"].ToString()),
            Database = 0,
            Ssl = true,
            Password = ConfigurationManager.AppSettings["RedisCachePassword"].ToString(),
            ServerEnumerationStrategy = new ServerEnumerationStrategy()
            {
                Mode = ServerEnumerationStrategy.ModeOptions.All,
                TargetRole = ServerEnumerationStrategy.TargetRoleOptions.Any,
                UnreachableServerAction = ServerEnumerationStrategy.UnreachableServerActionOptions.Throw
            },
            PoolSize = 50
        };