我想我在这里做错了什么。在我开始之前,先介绍一下。
我们公司使用名为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个左右的用户。这是我们到目前为止所发现的:
&RedisClient.Check()
到&RedisClient.Close()
的调用中使用了单个连接。&RedisClient.Check()
被调用时,通常会创建另一个连接(显然它不会重复使用以前关闭的客户端),我们最终会有成千上万个(假设一个Close Wait
状态的TCP连接(有点大)的池限制,不会被重用。new RedisClient()
创建一个新连接,人们可能认为它不是最聪明的方式处理它,但是......它会暂时执行,然后所有成千上万的{{1}}状态的连接开始关闭,然后池再次开始工作。我的问题是:为什么不重用TCP连接?它在控制台应用程序模拟中运行良好,但是当我们使用IIS将它用于我们的Genexus应用程序时,它只是不断创建这些连接。
我是不是一直把这个游泳池弄错了,或者我做错了什么?
注意:现在我提供所有这些信息,但如果你需要更多,没问题。我只是不知道还能提供什么。
编辑:已解决。我的代码试图太聪明了。我愚蠢了,现在它正常工作,但我仍然不明白我做错了什么。此外,我认为绝对所有与Redis的连接在被使用后立即被关闭都是错误的。
答案 0 :(得分:3)
访问客户端的典型使用模式是使用using语句,即:
using (var redis = redisManager.GetClient())
{
//...
}
调用Dispose()
是将客户端释放回池中的原因。
您可以通过打印GetStats()
返回的词典来查看连接池内部统计信息的快照:
redisManager.GetStats().PrintDump();
您还可以使用全局查看所有Redis客户端活动的总体统计信息:
RedisStats.ToDictionary().PrintDump();
我还考虑减少连接池大小,因为 5000 的连接池接近于没有连接池。我的目标是活跃连接的2-3倍。