问题描述
我们在.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万次点击
解决方法
这里可能有两件事:
- 我不知道
EVAL
在做什么; 可能是正在执行的Lua造成了阻塞;唯一可以确定的方法是查看SLOWLOG
,但我不知道是否在Azure Redis上公开了此信息 - 可能是您的有效负载正在饱和可用带宽-我不知道您要传输什么
- 可能只是网络/套接字的停顿/中断;它们会发生,尤其是在云环境下-并且(相对)较高的延迟使这特别痛苦
- 我们希望启用新的可选合并(而不是多路复用)模型;从理论上讲,这(概念验证非常有效)可以避免大量积压,这意味着即使套接字发生故障:仅影响一个调用,而不是导致一系列失败;限制因素是我们的时间(而且,这需要与redis提供者的许可问题进行权衡;例如,并发连接是否有上限)
- 这可能只是库代码中的错误;如果是这样,我们在这里看不到它,但是我们没有使用与您相同的设置;我们会尽力而为,但是很难诊断出我们看不到的问题,这些问题仅是由于其他人的成本高昂的设置而引起,而我们却无法轻易复制。再加上最终:这不是我们的日常工作:(
我不认为这里有一个简单的“添加此行,一切都会变得很好”的答案。这些是非平凡的大规模远程方案,需要进行大量调查。简而言之:Azure员工不花我们的时间。
,因此,我们在此上发现了问题。 问题出在我们类的注册之内,即上面原始代码所示的AddTransient。 将其更改为AddScoped时,性能要快得多。甚至想知道它是否可以是单例。 奇怪的是,addtransient应该增加“连接的客户端”,这实际上是可行的,但是对可以处理的请求数量也有较大的影响。由于我们在处理过程中从未达到最大连接数限制。
.AddScoped<IRedisCache,RedisCache>()
.AddScoped<ISharedCacheStore,SharedCacheStore>();
使用这段代码而不是AddTransient,我们在4-5分钟内完成了22万次操作,而没有出现问题,而对于旧代码,由于超时异常,我们甚至没有达到4万次操作