StackExchange.Redis 仅在 1 台服务器上超时

StackExchange.Redis timeout on only 1 server

当一个新盒子启动时(或者大概是回收了应用程序池),我们看到每个 redis 请求都出现超时错误。有趣的是,它可能是 1/30 左右。也就是说,30 个 box 将正常启动并工作(实际调用是 Redis Lock 调用)每 1 个在这种故障状态下启动的 box。下面的示例显示队列中有 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 故障排除 link:https://azure.microsoft.com/en-us/blog/investigating-timeout-exceptions-in-stackexchange-redis-for-azure-redis-cache/

但是,如果连接未打开,我会从我的 redis 数据库工厂(我没有在日志中看到)专门抛出一个错误。下面是整个class看connectionmultiplexer初始化:

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)

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

你的超时错误消息让我想到了两件事。

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

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

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

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

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