使用.net ServiceStack.Redis客户端的twemproxy(nutcracker)性能下降

时间:2013-11-16 02:48:20

标签: .net redis servicestack twemproxy

在CentOS 6.4上安装redis和nutcracker。并尝试使用ServiceStack.Redis客户端进行连接。发现主要性能问题。

测试只留下1个redis实例

beta:
  listen: 0.0.0.0:22122
  hash: fnv1a_64
  distribution: ketama
  auto_eject_hosts: true
  #timeout: 5000
  #server_retry_timeout: 2000
  #server_failure_limit: 3
  redis: true
  servers:
  #- 127.0.0.1:6379:1
   - 127.0.0.1:6380:1

在下面的单元测试中,我试图通过胡桃夹子向redis发送100k字符串。

[TestClass]
public class RedisProxyTest
{
    public string host = "192.168.56.112";
    //public int port = 6379;
    public int port = 22122;

    [TestMethod]
    public void TestMethod1()
    {
        var key = "l2";
        var count = 100000;
        using (var redisClient = new RedisClient(host, port))
        {
            var list = new List<string>();
            for (int i = 0; i < count; i++)
            {
                list.Add(Guid.NewGuid().ToString());
            }

            Utils.TimeLog("Remove", () => redisClient.Remove(key));

            Utils.TimeLog("AddRangeToList", () => redisClient.AddRangeToList(key, list));
        }

        using (var redisClient = new RedisClient(host, port))
        {
            redisClient.GetListCount(key);

            Utils.TimeLog("GetRangeFromList", () =>
            {
                var ret = redisClient.GetRangeFromList(key, count / 2, count - 1);
                Console.WriteLine(ret.Count);
            });
        }

    }
}

在胡桃夹子重启后的前几次运行中,AddRangeToList的工作时间为1-2秒。但是随后的运行,AddRangeToList性能会在几分钟内显着下降甚至超过20分钟(如果没有配置超时)。直接使用redis时我无法重现。我还没有尝试任何其他客户端。有什么想法吗?

这是我在单元测试运行后在控制台中看到的:

Test Name:  TestMethod1
Test Outcome:   Passed  
Remove: 0.0331171
AddRangeToList: 806.8219166
50000
GetRangeFromList: 1.741737

2 个答案:

答案 0 :(得分:2)

看起来这个问题与传输大量数据时的高内存使用率有关。

默认情况下,nutcracker为每个密钥分配16k缓冲区大小。在我的情况下,它将 16k * 100000 = 1.5Gb 。看着胡桃夹子的过程,我看到了2Gb左右的高峰。我的Cent OS VM过载,没有足够的内存来处理这个尖峰。

答案 1 :(得分:1)

如果胡桃夹子接近数万个连接或发送带有数千个密钥的多次获取请求,则应使用512的mbuf大小

以下链接讨论如何解释mbuf大小? - https://github.com/twitter/twemproxy/issues/141

  

每个客户端连接至少消耗一个mbuf。要为请求提供服务,我们需要两个连接(一个从客户端到代理,另一个从代理到服务器)。所以我们需要两个mbuf。

     

像'get foo bar \ r \ n'这样的可分段请求,其中btw被分段为'get foo \ r \ n'和'get bar \ r \ n'将消耗两个mbuf用于请求,两个mbuf用于响应。因此,具有N个片段的可分段请求需要N * 2个mbufs

     

关于mbuf的好处是内存来自重用池。一旦分配了mbuf,它就永远不会被释放,而只是放回到重用池中。糟糕的是,一旦分配了mbuf,它就永远不会被释放,因为释放的mbuf总是返回到重用池 - https://github.com/twitter/twemproxy/blob/master/src/nc_mbuf.c#L23-L24(这可以通过在重用池上放置一个阈值参数来修复)

     

因此,如果胡桃夹子正在处理说1K客户端连接和100个服务器连接,它将消耗mbuf(max(1000,100)* 2 * mbuf-size)内存。如果我们假设客户端发送非流水线请求,那么默认的mbuf-size为16K,这将总共消耗32M。

     

此外,如果平均每个请求有10个片段,那么内存消耗将为320M。而不是处理1K客户端连接,假设您正在处理10K,那么内存消耗将是3.2G。现在不使用16K的默认mbuf大小,而是使用512字节,然后同一场景的内存消耗将降至1000 * 2 * 512 * 10 = 10M

     

这就是为什么“大量”连接要为mbuf-size选择一个小值,如512