【问题标题】:StackExchange.Redis timeout on only 1 serverStackExchange.Redis 仅在 1 台服务器上超时
【发布时间】:2016-11-15 23:39:53
【问题描述】:

当一个新机器启动(或者可能是应用程序池被回收)时,我们会看到每个 redis 请求的超时错误。有趣的是,大概是1/30左右。也就是说,30 个盒子可以正常启动并工作(实际调用是 Redis Lock 调用),每 1 个盒子在这种故障状态下启动。下面的示例显示队列中的 9k 个项目。根据 MS azure 建议(尽管我们不在 Azure 上),ConnectionMultiplexer 正在延迟初始化,调用如下:

var db = m_dbFactory.GetDatabase();
bool gotLock = db.LockTake(key, value, m_redisLockConfig.RedisLockMaxAgeTimeSpan);

我们正在使用 Ninject 来获取注入的 dbFactory 的单例:

kernel.Bind<IRedisDatabaseFactory>().To<RedisDatabaseFactory>().InSingletonScope();

我们不得不重新部署代码(回收应用程序池)来解决问题,或者杀死负载均衡器后面的 1 个坏盒子。有没有人遇到过这个问题?我看到队列中有 9k 项尚未写入出站网络,遵循 azure 故障排除链接:https://azure.microsoft.com/en-us/blog/investigating-timeout-exceptions-in-stackexchange-redis-for-azure-redis-cache/

但是,如果连接没有打开,我特意从我的 redis 数据库工厂抛出一个错误(我在我们的日志中没有看到)。下面是整个类来查看连接多路复用器的初始化:

public class RedisDatabaseFactory : IRedisDatabaseFactory
{
    private readonly Lazy<IConnectionMultiplexer> m_lazyConnectionMultiplexer;

    public RedisDatabaseFactory(IRedisConfig redisConfig)
    {
        var endPoint = new DnsEndPoint(redisConfig.Host, redisConfig.Port);

        var configOptions = new ConfigurationOptions
        {
            EndPoints = { endPoint },
            Password = redisConfig.Password,
            ConnectTimeout = 5000,
            AbortOnConnectFail = false
        };

        m_lazyConnectionMultiplexer = new Lazy<IConnectionMultiplexer>(() => 
            ConnectionMultiplexer.Connect(configOptions));
    }

    private IConnectionMultiplexer Connection
    {
        get { return m_lazyConnectionMultiplexer.Value; }
    }

    /// <summary>
    /// Gets a connected redis database
    /// </summary>
    /// <exception cref="Exception"></exception>
    /// <returns>Connected redis database</returns>
    public IDatabase GetDatabase()
    {
        if (!Connection.IsConnected)
        {
            throw new Exception("Redis connection failure");
        }
        return Connection.GetDatabase();
    }
}

这是堆栈跟踪:

System.TimeoutException: Timeout performing SET mykey, inst: 0, mgr: ExecuteSelect, err: never, queue: 9058, qu: 9058, qs: 0, qc: 0, wr: 0, wq: 0, in: 0, ar: 0, IOCP: (Busy=0,Free=1000,Min=1,Max=1000), WORKER: (Busy=1,Free=32766,Min=1,Max=32767), clientName: myclient at StackExchange.Redis.ConnectionMultiplexer.ExecuteSyncImpl[T](Message message, ResultProcessor1 processor, ServerEndPoint server) at StackExchange.Redis.RedisBase.ExecuteSync[T](Message message, ResultProcessor1 processor, ServerEndPoint server) at StackExchange.Redis.RedisDatabase.StringSet(RedisKey key, RedisValue value, Nullable1 expiry, When when, CommandFlags flags) at StackExchange.Redis.RedisDatabase.LockTake(RedisKey key, RedisValue value, TimeSpan expiry, CommandFlags flags)

我更改了密钥名称、客户端名称并删除了反引号。

【问题讨论】:

    标签: c# redis stackexchange.redis


    【解决方案1】:

    这确实太晚了,但我们最终确实做出了解决问题的更改。我们升级到最新的 StackExchange.Redis,以防问题由 Marc Gravell 和团队解决,但我们还进行了以下更改:

    m_lazyConnectionMultiplexer = new Lazy<IConnectionMultiplexer>(() => ConnectionMultiplexer.Connect(configOptions),LazyThreadSafetyMode.PublicationOnly;
    

    因此,如果连接多路复用器初始化为错误状态,之后另一个将被初始化。在进行了这 2 项更改之后,我们再也没有看到这个问题。我相信问题实际上并不在于应用程序池回收,而在于我们定期从 Amazon Machine Image 拆除和构建盒子的过程中。当它们被备份时,有时 1 处于糟糕的状态。我希望我已经确定了修复方法,但这对我们有用。

    【讨论】:

    【解决方案2】:

    从您的超时错误消息中,我突然想到了两件事。

    1. 您的“qu: 9058”号码意味着 9058 个请求已在本地排队,但尚未在线发送。这可能意味着您的系统连接到 Redis 的时间过长。
    2. 您可能应该按照此处所述更改 ThreadPool 配置:https://gist.github.com/JonCole/e65411214030f0d823cb。 IOCP 和 WORKER 线程都有 1 分钟的线程,这可能会在流量爆发期间导致问题,这对于许多应用在启动期间很常见。

    如果这不能为您解决问题,那么您可能需要监控客户端 CPU 使用率。如果您的客户端 CPU 达到 100% 左右,那么您的系统将没有足够的 CPU 来跟上您尝试提供的所有工作。将您的客户端机器升级到更快的东西。在您的情况下,ThreadPool 中的默认 Min Threads 为 1,这通常表明您只有 1 个 CPU Core,这可能还不够。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-03-03
      • 2016-11-20
      • 2019-01-10
      • 1970-01-01
      • 2015-08-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多