具有redis实现的Asp.Net Core为高流量提供502错误

时间:2017-06-22 13:36:25

标签: asp.net-core stackexchange.redis

在我们的应用程序中,我们的用户流量很大,每秒大约需要2000个请求。 我们在Asp.Net核心中创建了应用程序并使用了dapper。我们使用redis缓存管理器来分配缓存目的。 当我们托管这个网站并每秒检查几个(20或30)请求时,它工作正常。但是,当我们每秒达到超过50个请求时,网站正在给予 502 - Web服务器在充当网关或代理服务器时收到无效响应。
我们将redis缓存更改为内存缓存,然后它开始适用于每秒2000个请求。 我们使用的是redis 3.2.100版 因此,在这里使用redis我们无法运行此站点以获取更多请求,并且在流量过大时会收到502错误。

为Redis缓存编写的代码

using Common;
using Common.DependencyInjection;
using Newtonsoft.Json;
using StackExchange.Redis;
using System;
using System.Text;

namespace Service.Caching
{
[TransientDependency(ServiceType = typeof(ICacheManager))]
public class RedisCacheManager : ICacheManager
{
    private readonly string _redisHost;
    private readonly int _redisPort;
    private readonly string _redisPassword;
    private readonly ConfigurationOptions _configurationOptions;

    public RedisCacheManager()
    {
        _redisHost = ConfigItems.RedisHost;
        _redisPassword = ConfigItems.RedisPassword;
        _redisPort = ConfigItems.RedisPort;

        _configurationOptions = new ConfigurationOptions();
        configurationOptions.EndPoints.Add(_redisHost, redisPort);
        _configurationOptions.Ssl = false;
        //_configurationOptions.Password = _redisPassword;
        _configurationOptions.AbortOnConnectFail = false;
        _configurationOptions.SyncTimeout = int.MaxValue;
        _configurationOptions.DefaultVersion = new Version(2, 8, 8);
        _configurationOptions.WriteBuffer = 10000000;
        _configurationOptions.KeepAlive = 180;
    }

    /// <summary>
    /// Gets or sets the value associated with the specified key.
    /// </summary>
    /// <typeparam name="T">Type</typeparam>
    /// <param name="key">The key of the value to get.</param>
    /// <returns>
    /// The value associated with the specified key.
    /// </returns>
    public T Get<T>(string key)
    {
        using (var connection = ConnectionMultiplexer.Connect(_configurationOptions))
        {
            var db = connection.GetDatabase(-1);
            //return (T)(object)db.StringGet(key);

            return (T)ConvertToObject<T>(db.StringGet(key));
        }
    }

    /// <summary>
    /// Adds the specified key and object to the cache.
    /// </summary>
    /// <param name="key">key</param>
    /// <param name="data">Data</param>
    /// <param name="cacheTime">Cache time</param>
    public void Set(string key, object data, int cacheTime)
    {
        if (data == null)
            return;

        DateTime expireDate;
        if (cacheTime == 99)
            expireDate = DateTime.Now + TimeSpan.FromSeconds(30);
        else
            expireDate = DateTime.Now + TimeSpan.FromMinutes(cacheTime);

       var value = (RedisValue)ConvertToByteArray(data);

        using (var connection = ConnectionMultiplexer.Connect(_configurationOptions))
        {
            var db = connection.GetDatabase(-1);
            db.StringSet(key, value, new TimeSpan(expireDate.Ticks));
        }
    }

    /// <summary>
    /// Gets a value indicating whether the value associated with the specified key is cached
    /// </summary>
    /// <param name="key">key</param>
    /// <returns>
    /// Result
    /// </returns>
    public bool IsSet(string key)
    {            
        using (var connection = ConnectionMultiplexer.Connect(_configurationOptions))
        {
            var db = connection.GetDatabase(-1);
            return db.KeyExists(key);
        }
    }

    /// <summary>
    /// Removes the value with the specified key from the cache
    /// </summary>
    /// <param name="key">/key</param>
    public void Remove(string key)
    {
        using (var connection = ConnectionMultiplexer.Connect(_configurationOptions))
        {
            var db = connection.GetDatabase(-1);
            db.KeyDelete(key);
        }
    }

    /// <summary>
    /// Removes items by pattern
    /// </summary>
    /// <param name="pattern">pattern</param>
    public void RemoveByPattern(string pattern)
    {            
        using (var connection = ConnectionMultiplexer.Connect(_configurationOptions))
        {
            var server = connection.GetServer(_redisHost, _redisPort);
            var keysToRemove = server.Keys(pattern: "*" + pattern + "*");//-1, pattern);
            foreach (var key in keysToRemove)
                Remove(key);
        }
    }

    /// <summary>
    /// Clear all cache data
    /// </summary>
    public void Clear()
    {
        using (var connection = ConnectionMultiplexer.Connect(_configurationOptions))
        {
            var server = connection.GetServer(_redisHost, _redisPort);
            //var keysToRemove = server.Keys(-1);
            var keysToRemove = server.Keys();
            foreach (var key in keysToRemove)
                Remove(key);
        }
    }

    /// <summary>
    /// Converts to byte array.
    /// </summary>
    /// <param name="data">The data.</param>
    /// <returns>System.Byte[].</returns>
    private byte[] ConvertToByteArray(object data)
    {
        if (data == null)
            return null;

        string serialize = JsonConvert.SerializeObject(data);
        return Encoding.UTF8.GetBytes(serialize);
    }

    /// <summary>
    /// Converts to object.
    /// </summary>
    /// <param name="data">The data.</param>
    /// <returns>System.Object.</returns>
    private T ConvertToObject<T>(byte[] data)
    {
        try
        {
            return JsonConvert.DeserializeObject<T>(Encoding.UTF8.GetString(data));
        }
        catch (Exception ex)
        {
            return (T)Activator.CreateInstance(typeof(T));
        }
    }
}

}

1 个答案:

答案 0 :(得分:1)

在Redis的每次操作上创建新的ConnectionMultiplexer时,从Redis缓存中获取/设置/删除键/值的调用可能会花费更长时间。

使用StackExchange.Redis时,

https://gist.github.com/JonCole/925630df72be1351b21440625ff2671f#file-redis-bestpractices-stackexchange-redis-md有一些最佳做法。

https://docs.microsoft.com/en-us/azure/redis-cache/cache-dotnet-how-to-use-azure-redis-cache#connect-to-the-cache显示推荐的使用模式。

另外,请查看StackExchange.Redis文档(https://stackexchange.github.io/StackExchange.Redis/Basics),它说&#34;因为ConnectionMultiplexer做了很多,它被设计为在调用者之间共享和重用。您不应该为每个操作创建ConnectionMultiplexer。它完全是线程安全的,可以用于此用途。在所有后续示例中,假设您已将一个ConnectionMultiplexer实例存储起来以供重复使用&#34;。