Degradação de desempenho do twemproxy (quebra-nozes) com o cliente .net ServiceStack.Redis

StackOverflow https://stackoverflow.com//questions/20014030

Pergunta

Configure o redis e o quebra-nozes no CentOS 6.4.e tentando se conectar usando o cliente ServiceStack.Redis.Foi encontrado um grande problema de desempenho.

Para teste, resta apenas 1 instância do 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

No teste de unidade a seguir, estou tentando enviar strings de 100k para redis por meio do quebra-nozes.

[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);
            });
        }

    }
}

Nas primeiras execuções após a reinicialização do quebra-nozes, AddRangeToList funciona com 1-2 segundos.Mas com execuções subsequentes, o desempenho do AddRangeToList cai significativamente de alguns minutos até mais de 20 minutos (se nenhum tempo limite estiver configurado).Não consigo reproduzir o mesmo ao usar o redis diretamente.Ainda não experimentei nenhum outro cliente.Alguma idéia do porquê?

Isto é o que vejo no console após a execução do teste de unidade:

Test Name:  TestMethod1
Test Outcome:   Passed  
Remove: 0.0331171
AddRangeToList: 806.8219166
50000
GetRangeFromList: 1.741737
Foi útil?

Solução

Se o quebra-nozes estiver fazendo proxy de várias dezenas de milhares de conexões ou enviando solicitações multi-get com vários milhares de chaves, você deve usar o tamanho mbuf de 512

O link a seguir fala sobre como interpretar o tamanho do mbuf?- https://github.com/twitter/twemproxy/issues/141

Cada conexão de cliente consome pelo menos um mbuf.Para atender uma solicitação, precisamos de duas conexões (uma do cliente para o proxy e outra do proxy para o servidor).Então precisaríamos de dois mbufs.

Uma solicitação fragmentável como 'get foo bar ', que aliás é fragmentada para 'get foo ' e 'get bar ' consumiria dois mbuf para solicitação e dois mbuf para resposta.Portanto, uma solicitação fragmentável com N fragmentos precisa de N * 2 mbufs

O bom do mbuf é que a memória vem de um pool de reutilização.Depois que um mbuf é alocado, ele nunca é liberado, mas apenas colocado de volta no pool de reutilização.O ruim é que uma vez alocado o mbuf ele nunca é liberado, já que um mbuf liberado sempre volta para o pool de reutilização - https://github.com/twitter/twemproxy/blob/master/src/nc_mbuf.c#L23-L24 (isso pode ser corrigido colocando um parâmetro de limite no pool de reutilização)

Portanto, se o quebra-nozes estiver lidando com, digamos, 1K conexões de cliente e 100 conexões de servidor, ele consumirá (max(1000, 100) * 2 * mbuf-size) memória para mbuf.Se presumirmos que os clientes estão enviando solicitações sem pipeline, com o tamanho mbuf padrão de 16K, isso consumiria no total 32M.

Além disso, se em média cada solicitação tiver 10 fragmentos, o consumo de memória seria de 320M.Em vez de lidar com conexões de cliente de 1K, digamos que você estivesse lidando com 10K, o consumo de memória seria de 3,2G.Agora, em vez de usar um tamanho mbuf padrão de 16K, você usou 512 bytes, então o consumo de memória para o mesmo cenário cairia para 1000 * 2 * 512 * 10 = 10M

Esta é a razão pela qual para um 'grande número' de conexões você deseja escolher um valor pequeno para o tamanho do mbuf, como 512

Outras dicas

Parece que o problema está relacionado ao alto uso de memória ao transferir essa quantidade de dados.

Por padrão, o quebra-nozes aloca tamanho de buffer de 16k para cada chave.No meu caso vai ser 16k*100.000 = 1,5GB.Vi um pico de cerca de 2 Gb ao observar o processo de quebra-nozes.Minha VM do Cent OS estava sobrecarregada e não havia memória suficiente para lidar com esse pico.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top