c# - ServiceStack.Redis : PooledRedisClientManager creating way too many connections

标签 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 类,它是一个单例。通过一些测试,我们确保池仅创建一次。我们在单例 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/

相关文章:

c# - 我是否需要序列化抽象基类以使派生类可序列化

asp.net-mvc - IIS 站点上的高 CPU

redis - 如何在 .Net 应用程序中查看所有存储 session 的 REDIS 键和值?

redis - 为什么redis pubsub工作独立于数据库?

python - 如何使用redis for python在Redis中搜索值

c# - 在 WPF 中获取数据网格中的多个选定行?

c# - .Net Core 3.0 Nginx 不提供静态文件

c# - 如何使用 C# 编写 LINQ 查询以从另一个对象获取对象?

c# - 如何将我的 asp.net 项目发布到本地 iis?

iis - 发布到 IIS。省略在服务器上安装 .NET Core Windows Server Hosting bundle