我想我在这里做错了什么。在开始之前,先介绍一下背景。
我们公司使用一种名为 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 类,它是一个单例。通过一些测试,我们确保池仅创建一次。我们在单例 RedisProvider 中创建一个像这样的池实例:
Pool = new PooledRedisClientManager(
poolSize: poolSize,
poolTimeOutSeconds: timeout,
readWriteHosts: hosts);
这个RedisProvider类也有一个这样的方法:
public RedisClient GetClient() => (RedisClient)Pool.GetClient();
到目前为止我们发现了什么:
我们使用 Apache JMeter 针对 SOAP Web 服务进行了一些测试,模拟了 50 个左右的用户。这是我们迄今为止发现的:
- 该问题仅发生在 IIS ASP.NET 应用程序内部。在具有高并发性的控制台应用程序上进行测试无法重现该问题。
- 池本身仅创建一次。整个应用程序共享这个单一实例。
- 在上面的 GeneXus 示例中,完全证明了从
&RedisClient.Check()
到&RedisClient.Close()
的调用之间使用了单个连接。 - 但是当另一个
&RedisClient.Check()
被调用时,通常会创建另一个连接(显然它不会重用之前关闭的客户端),并且我们最终会拥有成千上万的连接(假设池限制为 5000) )处于Close Wait
状态的 TCP 连接(有点巨大),这些连接不会被重用。 - 当它达到池限制时,我们有一些处理逻辑(我没有放在这里)在池超时后使用
new RedisClient()
创建一个新连接,这可能会认为这不是处理这个问题的最明智的方法,但是好吧...它会这样做一段时间,然后所有处于Close Wait
状态的数千个连接开始关闭,然后池再次开始工作。
我的问题是:为什么不重用 TCP 连接?它在控制台应用程序模拟中运行良好,但是当我们使用 IIS 将其用于我们的 Genexus 应用程序时,它只是不断创建这些连接。
我是否一直把这个池的事情弄错了,或者我做错了什么?
注意:目前我正在提供所有这些信息,但如果您需要更多信息,也没有问题。我只是不知道还要提供什么。
编辑:已解决。我的代码试图变得过于智能。我把它简化了,现在它可以正常工作了,尽管我仍然不明白我做错了什么。另外,我认为所有与 Redis 的连接在使用后立即关闭的假设被证明是错误的。
最佳答案
访问客户端的典型使用模式是使用 using 语句,即:
using (var redis = redisManager.GetClient())
{
//...
}
调用 Dispose()
会将客户端释放回池中。
连接池统计信息
您可以通过打印 GetStats()
返回的字典来查看连接池内部统计信息的快照:
redisManager.GetStats().PrintDump();
Redis Stats
您还可以使用 Global 查看所有 Redis 客户端事件的总体统计信息:
RedisStats.ToDictionary().PrintDump();
我还会考虑减小连接池大小,因为 5000 连接池接近没有连接池。我的目标是让您的活跃连接数达到约 2-3 倍。
关于c# - ServiceStack.Redis : PooledRedisClientManager creating way too many connections,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33764374/