1

我在一个 azure 网站中使用 Redis Cache。缓存托管在 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;
        }
    }

}
4

2 回答 2

3

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

有几点可以改善您的情况:

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)。

在此处输入图像描述

于 2015-04-21T23:04:37.847 回答
1

如果 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”与我们联系。

于 2014-10-14T05:50:41.720 回答