StackExchange.Redis-无法解释的超时异常问题

问题描述

我们在.NET Core 3.1与Azure Redis缓存的集成中遇到问题。 抛出的异常是

执行以下命令时发生未处理的异常 请求。“,” @ l“:”错误“,” @ x“:” StackExchange.Redis.RedisTimeoutException: 等待响应超时(出站= 1403KiB,入站= 5657KiB,15000ms 过去,超时为15000毫秒),命令= EVAL,下一个:EVAL,实例:0,qu: 0,qs:709,aw:True,rs:ReadAsync,ws:Writing,in:0, serverEndpoint:redis-scr-mns-dev.redis.cache.windows.net:6380,mc: 1/1/0,mgr:10个(共10个)可用,客户端名称:xxxxxxxxxxxx,IOCP: (忙= 0,免费= 1000,最小= 4,最大= 1000),工作人员: (忙碌= 7,免费= 32760,最小值= 4,最大值= 32767),v:2.1.5.3.4321 查看本文,了解可能导致某些常见的客户端问题 超时时间: https://stackexchange.github.io/StackExchange.Redis/Timeouts

是的,我已经阅读了这篇文章,并且我们正在使用StackExchange.Redis NuGet软件包,它是可用的最新版本。我们已经采取的步骤是

  • 使用多个值(ThreadPool.SetMinThreads(short.MaxValue,short.MaxValue);)设置最小线程池计数
  • 增加,将Redis timeout 值从默认的5秒增加到15秒(走高一点并不能解决问题,我想老实说,因为您会进一步阅读:))

您要求的设置是什么?

  • .NET Core 3.1 REST API 在最新的IIS上运行,并在具有16GB RAM的4核Windows服务器上设置了3个工作线程。关于CPU或内存的监控)
  • 已连接到 Azure Redis缓存。当前运行的 Basic C5 具有较高的网络带宽和23GB的内存(以前是较低的版本,因此我们尝试扩展此容量)
  • 最后将请求推送到Azure服务总线(那里没有问题)

批处理进程正在运行并处理10000个API调用(几个API),其中上述一个对Redis Cache崩溃(超时异常)。其他api正常运行且未超时,但当前正在连接到其他Redis缓存(只是为了隔离该api的行为) 所有api和/或批处理程序都使用具有缓存实现的自定义NuGet程序包,因此我们确信在这1个api中,所有共享代码都不会成为实现问题。

我们如何使用缓存?好吧,通过依赖关系注入,我们注入了ISharedCacheStore,这是我们自己的接口,我们将其置于IDistributedCache之上,以确保只有异步调用可用,以及RedisCache(这是使用Redis的实现)(ISharedCacheStore供以后使用)缓存机制) 我们使用 Microsoft.Extensions.Caching.StackExchangeRedis版本3.1.5 ,并且启动时的注册为

 services.Configure<CacheConfiguration>(options => configuration?.GetSection("CacheConfiguration").Bind(options))
            .AddStackExchangeRedisCache(s =>
                {
                    s.Configuration = connectionString;
                })
            .AddTransient<IRedisCache,RedisCache>()
            .AddTransient<ISharedCacheStore,SharedCacheStore>();

说实话,我们没有想法。我们没有看到Azure中的Redis缓存实例有问题,因为当我们超时时,这个实例甚至还没有达到顶部。在较低的定价计划中,服务器负载达到80%左右,而在较高的定价计划中,服务器负载甚至没有达到当前计划的10%。

根据数据分析,我们在运行中每分钟有4000次高速缓存命中,导致大约10%的服务器负载。

更新 :值得一提的是,批处理和API现在运行在本地环境中,而不是云中。计划在未来几个月内迁移到云。 这也适用于其他api连接到Redis缓存且未发出问题的

比较

  • 另一个Azure Redis缓存每分钟可命中45K,而不会出现任何问题(来自内部部署)
  • 这甚至达到了超时标记点,甚至达到每分钟1万次点击

解决方法

这里可能有两件事:

  1. 我不知道EVAL在做什么; 可能是正在执行的Lua造成了阻塞;唯一可以确定的方法是查看SLOWLOG,但我不知道是否在Azure Redis上公开了此信息
  2. 可能是您的有效负载正在饱和可用带宽-我不知道您要传输什么
  3. 可能只是网络/套接字的停顿/中断;它们会发生,尤其是在云环境下-并且(相对)较高的延迟使这特别痛苦
  4. 我们希望启用新的可选合并(而不是多路复用)模型;从理论上讲,这(概念验证非常有效)可以避免大量积压,这意味着即使套接字发生故障:仅影响一个调用,而不是导致一系列失败;限制因素是我们的时间(而且,这需要与redis提供者的许可问题进行权衡;例如,并发连接是否有上限)
  5. 这可能只是库代码中的错误;如果是这样,我们在这里看不到它,但是我们没有使用与您相同的设置;我们会尽力而为,但是很难诊断出我们看不到的问题,这些问题仅是由于其他人的成本高昂的设置而引起,而我们却无法轻易复制。再加上最终:这不是我们的日常工作:(

我不认为这里有一个简单的“添加此行,一切都会变得很好”的答案。这些是非平凡的大规模远程方案,需要进行大量调查。简而言之:Azure员工不花我们的时间。

,

因此,我们在此上发现了问题。 问题出在我们类的注册之内,即上面原始代码所示的AddTransient。 将其更改为AddScoped时,性能要快得多。甚至想知道它是否可以是单例。 奇怪的是,addtransient应该增加“连接的客户端”,这实际上是可行的,但是对可以处理的请求数量也有较大的影响。由于我们在处理过程中从未达到最大连接数限制。

.AddScoped<IRedisCache,RedisCache>()
        .AddScoped<ISharedCacheStore,SharedCacheStore>();

使用这段代码而不是AddTransient,我们在4-5分钟内完成了22万次操作,而没有出现问题,而对于旧代码,由于超时异常,我们甚至没有达到4万次操作

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...