Otimização do TCP para melhorar o desempenho da rede


Nesta página, descrevemos os métodos para calcular as configurações corretas para diminuir a latência das conexões TCP no Google Cloud e em cenários híbridos. Esta página também ajuda a entender maneiras de melhorar a latência de conexão entre processos no Google Cloud.

A arquitetura de microsserviços moderna defende que os desenvolvedores construam pequenos serviços com uma única responsabilidade. Os serviços precisam se comunicar usando TCP ou UDP, de acordo com as expectativas de confiabilidade do sistema. Portanto, é essencial que os sistemas baseados em microsserviços se comuniquem com confiabilidade e baixa latência.

O Google Cloud oferece confiabilidade e baixa latência por meio de uma rede global, o que significa que nossos usuários também podem se tornar globais. Ter uma rede global significa que você cria uma rede de nuvem privada virtual (VPC, na sigla em inglês) que abrange regiões e zonas. Os aplicativos podem se conectar entre regiões e zonas sem sair da rede do Google Cloud.

Os aplicativos que tenham sido criados para um ambiente tradicional de data center podem apresentar desempenho lento quando são transferidos para um ambiente de nuvem híbrida, ou seja, quando alguns dos componentes do aplicativo são executados em um data center corporativo e outros são executados na nuvem. Um desempenho lento pode ser resultado de vários fatores. O foco deste artigo são latências de ida e volta e como a latência afeta o desempenho do TCP em aplicativos que movimentam uma quantidade considerável de dados em qualquer parte da rede.

O problema: latência e comportamento do TCP

O TCP usa um mecanismo de gestão de janelas para evitar que um remetente rápido ultrapasse um receptor lento. O receptor anuncia quantos dados o remetente precisa enviar antes de aguardar uma atualização de janela por parte do receptor. Dessa forma, se um aplicativo receptor não conseguir receber dados na conexão, haverá um limite na quantidade de dados que podem ficar enfileirados aguardando o aplicativo.

A janela TCP permite o uso eficiente de memória nos sistemas de envio e recebimento. Como o aplicativo receptor consome dados, as atualizações da janela são enviadas para o remetente. O menor tempo de atualização de uma janela é uma ida e volta. Isso leva à seguinte fórmula para um dos limites no desempenho da transferência em massa de uma conexão TCP:

Capacidade <= tamanho da janela / latência do tempo de ida e volta (RTT, na sigla em inglês)

No design original do TCP, essa janela tem um tamanho máximo de 65535 bytes (64 KiB - 1). Essa era a quantidade máxima de dados que o remetente poderia enviar antes que o remetente recebesse uma atualização de janela para permitir o envio de mais dados.

Alterações no TCP desde sua introdução

Desde que o TCP foi introduzido, alguns recursos importantes foram alterados:

  • As velocidades de rede típicas aumentaram em quatro ordens de grandeza.
  • A memória típica em um sistema aumentou em quatro ordens de grandeza.

O resultado da primeira alteração é que os tamanhos originais de janela TCP levaram a um uso ineficiente dos recursos da rede. Um remetente enviava o equivalente a uma janela de dados na melhor velocidade possível para as condições da rede e ficava ocioso por um período considerável de tempo enquanto aguardava a atualização da janela TCP. O resultado da segunda alteração é que os remetentes e os receptores podem usar mais memória na rede para resolver a limitação exposta pela primeira alteração.

O diagrama a seguir ilustra esse intercâmbio.

O remetente envia apenas 64 K de dados e tem que esperar muito tempo até que uma atualização de janela seja retornada

O remetente não pode utilizar totalmente a rede porque está aguardando a atualização da janela TCP para enviar mais dados.

Como enviar mais dados por vez

A solução é enviar mais dados por vez. À medida que a largura de banda da rede aumenta, mais dados cabem no canal (rede) e, à medida que ele fica mais longo, mais tempo leva para que o recebimento dos dados seja reconhecido. Essa relação é conhecida como produto de atraso de largura de banda (BDP, na sigla em inglês) e é calculada com a multiplicação da largura de banda pelo tempo de ida e volta (RTT, na sigla em inglês). O resultado é um valor que especifica o número ideal de bits a serem enviados para preencher o canal. Esta é a fórmula:

BDP (bits) = largura de banda (bits/segundo) * RTT (segundos)

O BDP calculado é usado como tamanho de janela TCP para otimização.

Por exemplo, imagine uma rede de 10 Gbps com um RTT de 30 milissegundos. Para o tamanho da janela, use o valor do tamanho de janela TCP original (65535 bytes). Esse valor não chega nem perto de aproveitar a capacidade da largura de banda. O desempenho máximo de TCP possível neste link é:

(65535 bytes * 8 bits/byte) = largura de banda * 0,030 segundo
largura de banda = (65535 bytes * 8 bits/byte)/0,030 segundo
largura de banda = 524280 bits/0,030 segundo
largura de banda = 17476000 bits/segundo

Explicando de outra maneira, estes valores resultam em uma capacidade de mais de 17 Mbits por segundo, que é uma pequena fração da amplitude de 10 Gbps da rede.

A solução: dimensionamento do tamanho da janela TCP

Para resolver as limitações de desempenho impostas pelo design original do tamanho da janela TCP, foram introduzidas extensões no protocolo TCP que permitem que o tamanho da janela seja dimensionado para valores muito maiores. O dimensionamento da janela é compatível com janelas de até 1.073.725.440 bytes, ou quase 1 GiB. Esse recurso é descrito na RFC 7323 como opção de escala da janela TCP.

As extensões de escala de janela expandem a definição da janela TCP para usar 30 bits e, em seguida, usam um fator de escalonamento implícito para transportar esse valor de 30 bits no campo de janela de 16 bits do cabeçalho TCP. Para ver se o recurso está ativado em sistemas baseados em Linux, use este comando:

sudo sysctl net.ipv4.tcp_window_scaling

Todas as máquinas virtuais Linux do Google Cloud têm este recurso ativado por padrão. O valor de retorno de 1 indica que a opção está ativada. Se o recurso estiver desativado, ative-o usando o seguinte comando:

sudo sysctl -w net.ipv4.tcp_window_scaling=1

Taxa de transferência com tamanho de janela maior

Use o exemplo anterior para mostrar a vantagem de ter o escalonamento de janelas. Como antes, suponha uma rede de 10 Gbps com latência de 30 milissegundos e calcule um novo tamanho de janela usando esta fórmula:

(Velocidade do link * latência) / 8 bits = tamanho da janela

Se você inserir os números do exemplo, terá:

(10 Gbps * 30 ms/1000 seg)/8bits/byte = tamanho da janela
(10.000 Mbps * 0,030 segundo)/8 bits/byte = 37,5 MB

Aumentar o tamanho da janela TCP para 37 MB pode incrementar o limite teórico de desempenho da transferência em massa do TCP para um valor que se aproxime da capacidade da rede. Naturalmente, outros fatores podem limitar o desempenho (como sobrecarga do sistema, tamanho médio do pacote e quantidade de fluxos adicionais que compartilham o link), mas o tamanho da janela atenua substancialmente os limites impostos pelo restrito tamanho de janela anterior.

Como configurar os ajustáveis do Linux para alterar o tamanho da janela TCP

No Linux, o tamanho da janela TCP é afetado pelos seguintes ajustáveis sysctl(8):

net.core.rmem_max
net.core.wmem_max
net.ipv4.tcp_rmem
net.ipv4.tcp_wmem

Os dois primeiros ajustáveis afetam o tamanho máximo da janela TCP para aplicativos que tentam controlar o tamanho da janela TCP diretamente, limitando a solicitação dos aplicativos a nada mais que esses valores. Os próximos dois ajustáveis afetam o tamanho da janela TCP para aplicativos que permitem que o ajuste automático do Linux faça o trabalho.

O valor ideal de tamanho de janela depende de circunstâncias específicas, mas um ponto de partida é o BDP (produto de atraso de largura de banda) maior para os caminhos que o sistema provavelmente utilizará para enviar dados. Nesse caso, siga estas etapas para definir os ajustáveis:

  1. Confira se você tem privilégios raiz.
  2. Consiga as configurações atuais do buffer. Salve-as caso você queira reverter essas alterações.

    sudo sysctl -a | grep mem
    
  3. Defina uma variável de ambiente como o novo tamanho de janela TCP a ser utilizado:

    MaxExpectedPathBDP=8388608
    
  4. Defina o tamanho máximo do buffer de recebimento do sistema operacional para todos os tipos de conexões:

    sudo sysctl -w net.core.rmem_max=$MaxExpectedPathBDP
    
  5. Defina o tamanho máximo do buffer de envio do sistema operacional para todos os tipos de conexões:

    sudo sysctl -w net.core.wmem_max=$MaxExpectedPathBDP
    
  6. Defina as configurações do buffer de memória de recebimento TCP (tcp_rmem):

    sudo sysctl -w net.ipv4.tcp_rmem="4096 87380 $MaxExpectedPathBDP"
    

    A configuração tcp_rmem usa três valores:

    • O tamanho mínimo do buffer de recebimento que pode ser alocado para um soquete TCP. Neste exemplo, o valor é 4096 bytes.
    • O tamanho padrão do buffer de recebimento, que também modifica o valor /proc/sys/net/core/rmem_default usado por outros protocolos. No exemplo, o valor é 87380 bytes.
    • O tamanho máximo do buffer de recebimento que pode ser alocado para um soquete TCP. No exemplo, é o valor definido por você anteriormente (8388608 bytes).
  7. Defina as configurações do buffer de memória de envio TCP (tcp_wmem):

    sudo sysctl -w net.ipv4.tcp_wmem="4096 16384 $MaxExpectedPathBDP"
    

    A configuração tcp_wmem usa três valores:

    • O espaço mínimo do buffer de envio TCP disponível para um único soquete TCP.
    • O espaço padrão do buffer permitido para um único soquete TCP.
    • O espaço máximo do buffer de envio do TCP.
  8. Defina os ajustáveis para que as conexões subsequentes usem os valores especificados:

    sudo sysctl -w net.ipv4.route.flush=1
    

Para manter estas configurações durante as reinicializações, anexe os comandos definidos anteriormente ao arquivo /etc/sysctl.conf:

sudo bash -c 'cat << EOF >> /etc/sysctl.conf
net.core.rmem_max=8388608
net.core.wmem_max=8388608
net.ipv4.tcp_rmem=4096 87380 8388608
net.ipv4.tcp_wmem=4096 16384 8388608
net.ipv4.route.flush=1
EOF'

Como testar o RTT com um tamanho de janela atualizado

Quando o tamanho da janela TCP é grande o suficiente para utilizar o BDP, a imagem muda, como mostrado neste diagrama:

O remetente envia uma grande quantidade de dados por vez e não tem que esperar quase nada por uma atualização de janela

O tamanho da janela TCP sempre pode ser adaptado com base nos recursos disponíveis para o processo envolvido e o algoritmo do TCP em uso. Como mostrado no diagrama, o dimensionamento de janelas permite que uma conexão vá além do tamanho da janela de 65 KiB definido na especificação original do TCP.

Faça o teste. Primeiro, certifique-se de ter feito alterações no tamanho da janela TCP em um computador local e um remoto ao definir os ajustáveis nas duas máquinas. Em seguida, execute os seguintes comandos:

dd if=/dev/urandom of=sample.txt bs=1M count=1024 iflag=fullblock
scp sample.txt your_username@remotehost.com:/some/remote/directory

O primeiro comando cria um arquivo sample.txt de 1 GB com dados aleatórios. O segundo comando copia este arquivo da sua máquina local para uma máquina remota.

Observe a saída do comando scp no console, que exibe a largura de banda em Kbps. Há uma diferença considerável nos resultados do tamanho da janela TCP antes e depois das alterações.

A seguir