C# Redis:PooledRedisClientManager创建的连接太多
我想我做错了什么。在我开始之前,先讲一点背景知识 我们公司使用一种叫做GeneXus的工具:这是一种代码生成器工具,已经使用了很多年了。它生成C#代码,这样我们就可以构建自己的程序集,并使用该工具工作。我们的应用程序处理很多SOAP调用,并且它还很好地利用了Redis。事实上,Redis是整个代码基础设施的主要组成部分 为了使它与Genexus一起工作,我们必须围绕ServiceStack.Redis库创建一个包装器类,以便它可以在Genexus代码中使用。这就是我们在GeneXus中使用它的方式:C# Redis:PooledRedisClientManager创建的连接太多,c#,iis,redis,servicestack,C#,Iis,Redis,servicestack,我想我做错了什么。在我开始之前,先讲一点背景知识 我们公司使用一种叫做GeneXus的工具:这是一种代码生成器工具,已经使用了很多年了。它生成C#代码,这样我们就可以构建自己的程序集,并使用该工具工作。我们的应用程序处理很多SOAP调用,并且它还很好地利用了Redis。事实上,Redis是整个代码基础设施的主要组成部分 为了使它与Genexus一起工作,我们必须围绕ServiceStack.Redis库创建一个包装器类,以便它可以在Genexus代码中使用。这就是我们在GeneXus中使用它的方
//First we check if Redis is working at all. It just pings the Redis server.
If &RedisClient.Check()
//Here we make several calls to get and set some data. Like that:
If &RedisClient.Exists("Some_Key")
&MyData = &RedisClient.Get("Some_Key")
Else
&MyData = FetchFromSQLServerDatabase()
&RedisClient.Set("Some_Key", &MyData)
EndIf
//We are done with Redis, close it.
&RedisClient.Close()
EndIf
这是一个简单的例子,但我们的包装器一直是这样使用的:检查它是否在线,做几件事,然后关闭客户端
调用.Close()
调用引擎盖下的.Dispose()
方法
这就是我们在包装器中管理客户端创建的方式
首先,我们有一个RedisProvider类,它是一个单例。通过一些测试,我们确保只创建一次池。我们在singleton RedisProvider中创建如下池实例:
Pool = new PooledRedisClientManager(
poolSize: poolSize,
poolTimeOutSeconds: timeout,
readWriteHosts: hosts);
这个RedisProvider类也有这样一个方法:
public RedisClient GetClient() => (RedisClient)Pool.GetClient();
到目前为止我们的发现:
我们使用ApacheJMeter对我们的SOAP Web服务进行了一些测试,模拟了大约50个用户。这就是我们目前发现的:
- 此问题仅发生在IIS ASP.NET应用程序中。在具有高并发性的控制台应用程序上测试它无法重现问题
- 池本身只创建一次。整个应用程序共享这个实例
- 在上面的GeneXus示例中,已经完全证明在调用中使用单个连接,从
到&RedisClient.Check()
&RedisClient.Close()
- 但是当调用另一个
时,通常会创建另一个连接(显然它不会重用以前关闭的客户端),我们最终会有成千上万(假设池限制为5000)TCP连接(这有点大)处于&RedisClient.Check()
状态,它们不会被重复使用关闭等待
- 当它达到池限制时,我们有一些处理逻辑(我没有放在这里),在池超时后使用
创建一个新连接,这可能不是处理这个问题的最聪明的方法,但是。。。它会这样做一段时间,然后处于new RedisClient()
状态的数千个连接开始关闭,然后池再次开始工作Close Wait
编辑:已解决。我的代码太聪明了。我把它简化了,现在它工作正常了,尽管我仍然不明白我做错了什么。此外,我认为所有与Redis的连接都在使用后立即关闭的假设也被证明是错误的。访问客户端的典型使用模式是使用using语句,即:
using (var redis = redisManager.GetClient())
{
//...
}
调用Dispose()
将客户端释放回池中
连接池统计信息
通过打印由GetStats()
返回的字典,可以查看连接池内部统计信息的快照:
您还可以使用全局设置查看所有Redis客户端活动的总体统计信息:
RedisStats.ToDictionary().PrintDump();
<>我也会考虑减少连接池的大小,因为<强> 5000 < /强>的连接池接近于没有连接池。我的目标是让你的活跃关系增加2-3倍。我编辑了我的问题。完成后,我们总是调用.Close方法,该方法调用ServiceStack RedisClient对象的.Dispose()方法。因此,这就像我们使用
使用
语句一样。同样,这个问题似乎只发生在IIS上运行的ASP.NET应用程序中,当在具有多个线程的控制台应用程序上进行模拟时,它不会发生。关于连接池的统计信息,我会试试看。关于池的大小,我无可争辩:D我应该在处理RedisClient之后继续使用它吗?我做了一个快速测试,在处理完客户端后,我能够设置一个密钥。这可能吗?好的,解决了。原来我的代码太聪明了。我仍然不明白我做错了什么,但我把它放低了,它现在似乎运行正常。你能分享你的经验吗?我对IIS也有同样的问题,即使4年后我停止mqServer并使用using()工具,你是如何解决的?我现在也遇到了同样的问题…您可以尝试对ServiceStack.Redis使用StackExchange.Redis而不是@TedNothing,但是StackExchange one是免费的,开源的。事实上,我在这方面遇到了更糟糕的问题,但我认为这是由于当时我们怪异的开发环境和技术堆栈造成的。从那时起,使用asp.net内核,事情有了很大的发展。不管怎样都可以=)我是这样想的,如果我没记错的话,根据一些基准测试,StackExchange lib比ServiceStack慢得多。另外,StackExchange对我来说缺少一些非常重要的功能。因为我正在使用ServiceStacks和其他libs/api,所以我自然会尽可能地继续使用它。SS Redis库现在对我来说工作得很好,尽管我偶尔会有一些特性请求。Mythz非常快速地回答问题并添加功能(他同意)=),只是出于好奇
RedisStats.ToDictionary().PrintDump();