使用StackExchange.Redis突发RedisTimeoutException

Mik*_*sen 5 redis stackexchange.redis

我正在尝试使用StackExchange Redis库跟踪间歇性的"突发"超时.以下是我们的设置:我们的API是用C#编写的,可以在Windows 2008和IIS上运行.我们有4台API服务器在生产中,我们有4台Redis机器(运行Linux最新LTS),每台机器有2个Redis实例(一个主机在端口7000上,一个从机在端口7001上).我几乎看过Redis服务器的各个方面,看起来很棒.日志,CPU和网络中的错误都不是很好,服务器方面的一切看起来都很棒.我可以tail -f在发生这种情况时使用Redis记录并且看不到任何异常(例如重写AOF文件或任何其他内容).我认为问题不在于Redis.

这是我目前所知道的:

  • 我们每小时会看到几次超时异常.通常在一分钟内有40-50次超时,有时高达80-90.然后,他们会离开几分钟.在过去的24小时内,这些事件大约有5,000个,它们是从单个API客户端突然发生的.
  • 这些超时针对Redis 节点,而不针对节点.但是,它们发生在各种Redis命令中,例如GET和SET.
  • 当发生这些超时的突发时,呼叫来自单个API服务器,但发生与各种Redis节点的通话.例如,API3可能会有一堆超时试图调用Cache1,Cache2和Cache3.这有力证据表明该问题与API服务器有关,而与Redis服务器无关.
  • Redis主节点有108个连接的客户端.我记录当前连接,这个数字保持稳定.连接中没有大的峰值,并且看起来没有任何不良代码创建太多连接或不共享ConnectionMultiplexer实例(我有一个并且它是静态的)
  • Redis从属节点有58个连接的客户端,这看起来也完全稳定.
  • 我们正在使用StackExchange.Redis版本1.2.6
  • Redis正在使用AOF模式,磁盘上的大小约为195MB

这是一个示例超时异常.大多数看起来与此基本相同:

Type = StackExchange.Redis.RedisTimeoutException,Message = Timeout执行GET limeade:allActivities,inst:1,mgr:ExecuteSelect,err:never,queue:0,qu:0,qs:0,qc:0,wr:0,wq :0,in:0,ar:0,clientName:LIMEADEAPI4,serverEndpoint:10.xx.xx.11:7000,keyHashSlot:1295,IOCP :( Busy = 0,Free = 1000,Min = 2,Max = 1000) ,WORKER :(忙碌= 9,自由= 32758,最小值= 2,最大值= 32767)(请查看本文,了解可能导致超时的一些常见客户端问题:http: //stackexchange.github.io/ StackExchange.Redis /超时),堆栈跟踪= 在StackExchange.Redis.ConnectionMultiplexer.ExecuteSyncImpl [T](消息消息,ResultProcessor 1 processor, ServerEndPoint server) at StackExchange.Redis.ConnectionMultiplexer.ExecuteSyncImpl[T](Message message, ResultProcessor1个处理器,ServerEndPoint服务器)在StackExchange.Redis.RedisBase.ExecuteSync [T](消息消息,ResultProcessor 1 processor, ServerEndPoint server) at StackExchange.Redis.RedisDatabase.StringGet(RedisKey key, CommandFlags flags) at Limeade.Caching.Providers.RedisCacheProvider1.Get [T](K cacheKey,CacheItemVersion和cacheItemVersion)在......

我已经做了一些关于追踪这些超时异常的研究,但令人惊讶的是所有数字都是零.队列中没有任何东西,没有等待处理的东西,我有大量的线程免费,没有做任何事情.一切看起来都很棒.

任何人都有任何想法如何解决这个问题?问题是这些缓存超时突发导致我们的数据库受到更多打击,在某些情况下这是一件坏事.我很乐意添加任何人都会发现有用的信息.

更新:连接代码

连接到Redis的代码是支持各种缓存环境和配置的相当复杂的系统的一部分,但我可以将其归结为基础知识.首先,有一个CacheFactory类:

public class CacheFactory : ICacheFactory
{
    private static readonly ILogger log = LoggerManager.GetLogger(typeof(CacheFactory));
    private static readonly ICacheProvider<CacheKey> cache;

    static CacheFactory()
    {
        ICacheFactory<CacheKey> configuredFactory = CacheFactorySection.Current?.CreateConfiguredFactory<CacheKey>();
        if (configuredFactory == null)
        {
           // Some error handling, not important
        }

        cache = configuredFactory.GetDefaultCache();
    }

    // ...
}
Run Code Online (Sandbox Code Playgroud)

ICacheProvider是实现与特定缓存系统通信的方式,可以对其进行配置.在这种情况下,configuredFactory是RedisCacheFactory这样的:

public class RedisCacheFactory<T> : ICacheFactory<T> where T : CacheKey, ICacheKeyRepository
{
    private RedisCacheProvider<T> provider;
    private readonly RedisConfiguration configuration;

    public RedisCacheFactory(RedisConfiguration config)
    {
        this.configuration = config;
    }

    public ICacheProvider<T> GetDefaultCache()
    {
        return provider ?? (provider = new RedisCacheProvider<T>(configuration));
    }
}
Run Code Online (Sandbox Code Playgroud)

GetDefaultCache方法在静态构造函数中调用一次,并返回一个RedisCacheProvider.这个类实际连接到Redis:

public class RedisCacheProvider<K> : ICacheProvider<K> where K : CacheKey, ICacheKeyRepository
{
    private readonly ConnectionMultiplexer redisConnection;
    private readonly IDatabase db;
    private readonly RedisCacheSerializer serializer;
    private static readonly ILog log = Logging.RedisCacheProviderLog<K>();
    private readonly CacheMonitor<K> cacheMonitor;
    private readonly TimeSpan defaultTTL;
    private int connectionErrors;

    public RedisCacheProvider(RedisConfiguration options)
    {
        redisConnection = ConnectionMultiplexer.Connect(options.EnvironmentOverride ?? options.Connection);
        db = redisConnection.GetDatabase();
        serializer = new RedisCacheSerializer(options.SerializationBinding);
        cacheMonitor = new CacheMonitor<K>();
        defaultTTL = options.DefaultTTL;

        IEnumerable<string> hosts = options.Connection.EndPoints.Select(e => (e as DnsEndPoint)?.Host);
        log.InfoFormat("Created Redis ConnectionMultiplexer connection.  Hosts=({0})", String.Join(",", hosts));
    }

    // ...
 }
Run Code Online (Sandbox Code Playgroud)

构造函数基于配置的Redis端点(在某些配置文件中)创建ConnectionMultiplexer.我每次创建连接时都会记录.我们没有看到任何过多的这些日志语句,并且与Redis的连接保持稳定.

Tom*_*len 0

global.asax, 中尝试添加:

protected void Application_Start(object sender, EventArgs e)
{
    ThreadPool.SetMinThreads(200, 200);
}
Run Code Online (Sandbox Code Playgroud)

对于我们来说,这将每天的错误从约 50-100 个减少到零。我相信对于要设置的数字没有一般规则,因为它取决于系统(我们可以使用 200),因此可能需要您进行一些实验。

我还相信这提高了网站的性能。