【问题标题】:ServiceStack.Redis: PooledRedisClientManager creating way too many connectionsServiceStack.Redis:PooledRedisClientManager 创建的连接太多
【发布时间】:2016-02-19 06:18:35
【问题描述】:

我想我在这里做错了什么。在我开始之前,先介绍一下上下文。

我们公司使用名为 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 的连接在使用后都会立即关闭,但结果证明是错误的。

【问题讨论】:

  • 4年后,你是怎么解决的?我现在遇到同样的问题...
  • 你可以尝试使用 StackExchange.Redis 代替 @Ted
  • ServiceStack.Redis 没有任何反对意见,但 StackExchange 是免费和开源的。实际上我遇到了更糟糕的问题,但我认为这是由于我们当时奇怪的开发环境和技术堆栈。从那时起,asp.net core 发生了很大的变化。
  • 无论什么工作 =) 我的情况是,如果我没记错的话,根据一些基准,StackExchanges lib 比 ServiceStack 慢得多。此外,StackExchange 缺少一些对我来说非常重要的功能。由于我正在使用 ServiceStacks 的其他库/API,因此我很自然地会继续使用它。 SS Redis 库现在对我来说运行良好,即使我时不时地有一些功能请求。 Mythz 非常快速地回答问题并添加功能(他同意)=)
  • 出于好奇,这是我为 SE.Redis (github.com/StackExchange/StackExchange.Redis/issues/437) 打开的一个 github 问题。忽略那些令人毛骨悚然的写作:)你解决了你原来的问题吗?已经快一年了......与你所说的相反,我总是发现 SE.Redis 更快,可能是由于异步 I/O。我们用的免费版SS没有这个功能,现在肯定不一样了。

标签: c# iis redis servicestack


【解决方案1】:

访问客户端的典型使用模式是使用 using 语句,即:

using (var redis = redisManager.GetClient())
{
    //...
}

调用Dispose() 将客户端释放回池中。

连接池统计

您可以通过打印出GetStats()返回的Dictionary来查看连接池内部统计的快照:

redisManager.GetStats().PrintDump();

Redis Stats

您还可以使用全局查看所有 Redis 客户端活动的总体统计信息:

RedisStats.ToDictionary().PrintDump();

我还考虑减少您的连接池大小,因为 5000 的连接池接近没有连接池。我的目标是大约 2-3 倍的活动连接。

【讨论】:

  • 我编辑了我的问题。完成后我们总是调用 .Close 方法,该方法调用 ServiceStack RedisClient 对象的 .Dispose() 方法。所以就像我们使用using 语句一样。同样,这个问题似乎只发生在 IIS 上运行的 ASP.NET 应用程序中,当在具有多个线程的控制台应用程序上模拟时,它不会发生。关于连接池统计信息,我会尝试的。关于游泳池的大小,我无法反驳:D
  • 在我处理完 RedisClient 后,我​​应该能够继续使用它吗?我做了一个快速测试,我能够在客户端被处理后设置一个密钥。应该可以吗?
  • 好的,解决了。原来我的代码太聪明了。我仍然不明白我做错了什么,但我把它弄糊涂了,现在它似乎表现得很好。
  • 你能分享你的经验吗?我对 IIS 也有同样的问题,即使我停止 mqServer 并使用 using() 东西也很困难
猜你喜欢
  • 2014-07-25
  • 2015-06-13
  • 2013-03-31
  • 2019-11-20
  • 2011-07-31
  • 1970-01-01
  • 2019-11-07
  • 1970-01-01
  • 2011-08-13
相关资源
最近更新 更多