Redis 缓存仅在异步方法中因同步请求而超时,而异步请求的响应速度较慢

本文关键字:请求 异步 响应速度 超时 缓存 异步方法 Redis 同步 | 更新日期: 2023-09-27 18:35:42

首先,我将Azure Redis缓存服务和StackExchange.Redis(1.0.371)客户端与MVC 5和Web Api 2应用程序一起使用。我得到了非常有趣的行为。当我使用异步转换同步调用时,同步请求超时和响应缓慢。我举个例子。这是我的RedisCacheService,

public class RedisCacheService : ICacheService
{
    private readonly IDatabase _cache;
    private static readonly ConnectionMultiplexer ConnectionMultiplexer;
    static RedisCacheService()
    {
        var connection = ConfigurationManager.AppSettings["RedisConnection"];
        ConnectionMultiplexer = ConnectionMultiplexer.Connect(connection);
    }
    public RedisCacheService(ISettings settings)
    {            
        _cache = ConnectionMultiplexer.GetDatabase();
    }
    public bool Exists(string key)
    {
        return _cache.KeyExists(key);
    }
    public Task<bool> ExistsAsync(string key)
    {
        return _cache.KeyExistsAsync(key);
    }

    public void Save(string key, string value, int timeOutInMinutes)
    {
        var ts = TimeSpan.FromMinutes(timeOutInMinutes);
        _cache.StringSet(key, value, ts);
    }
    public Task SaveAsync(string key, string value, int timeOutInMinutes)
    {
        var ts = TimeSpan.FromMinutes(timeOutInMinutes);
        return _cache.StringSetAsync(key, value, ts);
    }
    public string Get(string key)
    {
        return _cache.StringGet(key);
    }
    public async Task<string> GetAsync(string key)
    {
        string result = null;
        var val = await _cache.StringGetAsync(key);
        if(val.HasValue) 
        {
            result = val;
        }
        return result;
    }
    ......................................................................
}

这是我调用缓存的方法。

public async Task<IList<XX>> GetXXXXX(XXXX)
{
    var key = string.Format("{0}/XX{1}_{2}", XXXX, XX, XX);
    var xxx = _cacheService.Get(key);
    if (xxx != null)
    {
        return JsonConvert.DeserializeObject<IList<XX>>(xxx);
    }
    var x = await _repository.GetXXXXX(XXXXX);
    var contents = JsonConvert.SerializeObject(x);
    _cacheService.Save(key, JsonConvert.SerializeObject(x));
    return x;
}

上面的方法总是给我,

System.TimeoutException
Timeout performing GET XXX, inst: 0, mgr: Inactive, queue: 3, qu=2, qs=1, qc=0, wr=1/1, in=0/0

System.TimeoutException
Timeout performing SETEX XXX, inst: 0, mgr: Inactive, queue: 2, qu=1, qs=1, qc=0, wr=1/1, in=0/0

让我们将其更改为异步,

public async Task<IList<XX>> GetXXXXX(XXXX)
{
    var key = string.Format("{0}/XX{1}_{2}", XXXX, XX, XX);
    var xxx = await _cacheService.GetAsync(key);
    if (xxx != null)
    {
        return JsonConvert.DeserializeObject<IList<XX>>(xxx);
    }
    var x = await _repository.GetXXXXX(XXXXX);
    var contents = JsonConvert.SerializeObject(x);
    await  _cacheService.SaveAsync(key, JsonConvert.SerializeObject(x));
    return x;
}

上述方法有效,但至少需要 5-10 秒。我的意思是如果没有可用的缓存,则为 10 秒,如果缓存可用,则为 5 秒。

现在让我们让我的方法完全同步,

public async Task<IList<XX>> GetXXXXX(XXXX)
{
    var key = string.Format("{0}/XX{1}_{2}", XXXX, XX, XX);
    var xxx = _cacheService.Get(key);
    if (xxx != null)
    {
        return Task.FromResult(JsonConvert.DeserializeObject<IList<XX>>(xxx));
    }
    //var x = await _repository.GetXXXXX(XXXXX);
    var x = (IList<XX>)new List<XX>();
    var contents = JsonConvert.SerializeObject(x);
    _cacheService.Save(key, JsonConvert.SerializeObject(x));
    return Task.FromResult(x);
}

请注意注释以调用存储库方法。上述方法立即起作用,意味着我在不到 1 秒的时间内得到结果。显然,Azure或StackExcahge.Redis客户端有问题。

更新:我的最后一个方法(异步)也像魅力一样工作(快速且没有错误),

public async Task<IList<XX>> GetXXXXX(XXXX)
{
    var key = string.Format("{0}/XX{1}_{2}", XXXX, XX, XX);
    var xxx = await _cacheService.GetAsync(key);
    if (xxx != null)
    {
        return JsonConvert.DeserializeObject<IList<XX>>(xxx);
    }
    //var x = await _repository.GetXXXXX(XXXXX);
    var x = (IList<XX>)new List<XX>();
    var contents = JsonConvert.SerializeObject(x);
    await _cacheService.SaveAsync(key, JsonConvert.SerializeObject(x));
    return x;
}

请注意,我仍然对存储库代码进行了注释。

Redis 缓存仅在异步方法中因同步请求而超时,而异步请求的响应速度较慢

看起来Azure中的这些超时可能是一个悬而未决的问题。是否针对本地(非 Azure)服务器尝试过此代码?你得到相同的结果吗?

我已经通过在 redis 缓存连接字符串中设置 syncTimeout 属性解决了 Redis 缓存中的超时问题 issuse

例如 var conn = ConnectionMultiplexer.Connect("contoso5.redis.cache.windows.net,ssl=true,password=password,syncTimeout=5000");

连接字符串属性 https://stackexchange.github.io/StackExchange.Redis/Configuration 参考