【问题标题】:Consistent Timeouts in Redis Cache in AzureAzure 中 Redis 缓存中的一致超时
【发布时间】:2014-10-14 04:03:26
【问题描述】:

我在一个 azure 网站中使用 Redis 缓存。缓存托管在 Azure 中。在通过我们的监控为缓存设置值时,我注意到一些超时。所以我运行了一些负载测试,这些测试是在我从本地服务器缓存转移到使用 redis 之前运行的,与以前的测试运行相比,结果非常糟糕,主要是由于 redis 缓存超时引起的。

我正在使用 StackExchange.Redis 库版本 1.0.333 强名称版本。

我小心翼翼地在每次访问缓存时都不要创建新连接。

负载测试实际上并没有给服务器加载那么多,结果之前是 100% 成功,现在由于超时导致大约 50% 的错误率。

用于访问缓存的代码。

 public static class RedisCacheProvider
{
    private static ConnectionMultiplexer connection;
    private static ConnectionMultiplexer Connection
    {
        get
        {
            if (connection == null || !connection.IsConnected)
            {
                connection = ConnectionMultiplexer.Connect(ConfigurationManager.ConnectionStrings["RedisCache"].ToString());
            }
            return connection;
        }
    }

    private static IDatabase Cache
    {
        get
        {
            return Connection.GetDatabase();
        }
    }


    public static T Get<T>(string key)
    {
        return Deserialize<T>(Cache.StringGet(key));
    }

    public static object Get(string key)
    {
        return Deserialize<object>(Cache.StringGet(key));
    }

    public static void Set(string key, object value)
    {
        Cache.StringSet(key, Serialize(value));
    }

    public static void Remove(string key)
    {
        Cache.KeyDelete(key);
    }

    public static void RemoveContains(string contains)
    {
        var endpoints = Connection.GetEndPoints();
        var server = Connection.GetServer(endpoints.First());
        var keys = server.Keys();
        foreach (var key in keys)
        {
            if (key.ToString().Contains(contains))
                Cache.KeyDelete(key);
        }
    }

    public static void RemoveAll()
    {
        var endpoints = Connection.GetEndPoints();
        var server = Connection.GetServer(endpoints.First());
        server.FlushAllDatabases();
    }

    static byte[] Serialize(object o)
    {
        if (o == null)
        {
            return null;
        }

        BinaryFormatter binaryFormatter = new BinaryFormatter();
        using (MemoryStream memoryStream = new MemoryStream())
        {
            binaryFormatter.Serialize(memoryStream, o);
            byte[] objectDataAsStream = memoryStream.ToArray();
            return objectDataAsStream;
        }
    }

    static T Deserialize<T>(byte[] stream)
    {
        if (stream == null)
        {
            return default(T);
        }

        BinaryFormatter binaryFormatter = new BinaryFormatter();
        using (MemoryStream memoryStream = new MemoryStream(stream))
        {
            T result = (T)binaryFormatter.Deserialize(memoryStream);
            return result;
        }
    }

}

【问题讨论】:

    标签: azure redis stackexchange.redis azure-redis-cache


    【解决方案1】:

    我最近遇到了同样的问题。

    几点可以改善你的情况:

    Protobuf-net 代替 BinaryFormatter

    我建议使用protobuf-net,因为它会减少您要存储在缓存中的值的大小。

    public interface ICacheDataSerializer
        {
            byte[] Serialize(object o);
            T Deserialize<T>(byte[] stream);
        }
    
    public class ProtobufNetSerializer : ICacheDataSerializer
        {
            public byte[] Serialize(object o)
            {
                using (var memoryStream = new MemoryStream())
                {
                    Serializer.Serialize(memoryStream, o);
    
                    return memoryStream.ToArray();
                }
            }
    
            public T Deserialize<T>(byte[] stream)
            {
                var memoryStream = new MemoryStream(stream);
    
                return Serializer.Deserialize<T>(memoryStream);
            }
        }
    

    实施重试策略

    实现这个 RedisCacheTransientErrorDetectionStrategy 来处理超时问题。

    using Microsoft.Practices.TransientFaultHandling;
    
    public class RedisCacheTransientErrorDetectionStrategy : ITransientErrorDetectionStrategy
        {
            /// <summary>
            /// Custom Redis Transient Error Detenction Strategy must have been implemented to satisfy Redis exceptions.
            /// </summary>
            /// <param name="ex"></param>
            /// <returns></returns>
            public bool IsTransient(Exception ex)
            {
                if (ex == null) return false;
    
                if (ex is TimeoutException) return true;
    
                if (ex is RedisServerException) return true;
    
                if (ex is RedisException) return true;
    
                if (ex.InnerException != null)
                {
                    return IsTransient(ex.InnerException);
                }
    
                return false;
            }
        }
    

    像这样实例化:

    private readonly RetryPolicy _retryPolicy;
    
    // CODE
    var retryStrategy = new FixedInterval(3, TimeSpan.FromSeconds(2));
                _retryPolicy = new RetryPolicy<RedisCacheTransientErrorDetectionStrategy>(retryStrategy);
    

    这样使用:

    var cachedString = _retryPolicy.ExecuteAction(() => dataCache.StringGet(fullCacheKey));
    

    查看您的代码,以尽量减少缓存调用和您存储在缓存中的值。通过更有效地存储值,我减少了很多错误。

    如果这些都没有帮助。移动到更高的缓存(我们最终使用 C3 而不是 C1)。

    【讨论】:

      【解决方案2】:

      如果 IsConnected 为 false,则不应创建新的 ConnectionMultiplexer。现有的多路复用器将在后台重新连接。通过创建一个新的多路复用器而不处理旧的多路复用器,您正在泄漏连接。我们推荐以下模式:

      private static Lazy<ConnectionMultiplexer> lazyConnection =
          new Lazy<ConnectionMultiplexer>(() => {
              return ConnectionMultiplexer.Connect(
                  "mycache.redis.cache.windows.net,abortConnect=false,ssl=true,password=...");
          });
      
      public static ConnectionMultiplexer Connection {
          get {
              return lazyConnection.Value;
          }
      }
      

      您可以在 Azure 门户中监控与缓存的连接数。如果它看起来异常高,这可能是影响您的表现的原因。

      如需进一步帮助,请通过“azurecache@microsoft.com”与我们联系。

      【讨论】:

      • 谢谢!我认为它确实改进了它,但我仍然在实时环境中遇到一些超时错误。服务器上的负载很少,所以仍然对超时感到有些困惑。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-09-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-06-22
      相关资源
      最近更新 更多