使用StackExchange.Redis

时间:2018-08-20 18:13:07

标签: 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 master 节点上,而不发生在从属节点上。但是,它们是通过各种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 =超时   执行GET limeade:allActivities,实例:1,mgr:ExecuteSelect,   err:永不,队列: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),工作人员:   (繁忙= 9,免费= 32758,最小值= 2,最大值= 32767)(请看一下   关于可能导致超时的一些常见客户端问题的文章:   http://stackexchange.github.io/StackExchange.Redis/Timeouts),StackTrace=   在   StackExchange.Redis.ConnectionMultiplexer.ExecuteSyncImpl [T](消息   消息,ResultProcessor 1 processor, ServerEndPoint server) at StackExchange.Redis.ConnectionMultiplexer.ExecuteSyncImpl[T](Message message, ResultProcessor 1个处理器,ServerEndPoint服务器)位于   StackExchange.Redis.RedisBase.ExecuteSync [T](消息,   ResultProcessor 1 processor, ServerEndPoint server) at StackExchange.Redis.RedisDatabase.StringGet(RedisKey key, CommandFlags flags) at Limeade.Caching.Providers.RedisCacheProvider 1.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();
    }

    // ...
}

ICacheProvider是实现与特定缓存系统进行对话的方法,可以对其进行配置。在这种情况下,configureFactory是一个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));
    }
}

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));
    }

    // ...
 }

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

1 个答案:

答案 0 :(得分:0)

global.asax中,尝试添加:

protected void Application_Start(object sender, EventArgs e)
{
    ThreadPool.SetMinThreads(200, 200);
}

对于我们来说,这将错误从每天〜50-100减少到了零。我认为设置数字取决于系统是没有一般性规定的(200个对我们有用),因此可能需要您做些尝试。

我也相信这可以改善网站的性能。