Esclarecimento sobre o tamanho da janela TCP do Linux e atrasos

6

Tenho atrasos ao enviar dados por meio de um canal TCP que não consigo entender. O link é um link de 1Gb com uma latência de fim a fim de aproximadamente 40ms. Na minha configuração atual, a latência (o tempo de uma mensagem para ir do espaço de usuário do remetente para o espaço do usuário do receptor) pode atingir 100 ms.

O soquete do remetente é configurado com a opção TCP_NODELAY. O buffer do remetente (SO_SNDBUF) está configurado para ter 8 MB. O buffer de recebimento (SO_RCVBUF) também é configurado para 8MB. O escalonamento da janela TCP está ativado.

update-1 : Estou usando o middleware zeromq 3.1.1 para transportar dados. A configuração de soquete, incluindo o sinalizador TCP_NODELAY, é executada pelo middleware. Algumas opções são acessíveis como rx e tx emitem tamanhos de buffer, mas não TCP_NODELAY. Tanto quanto eu entendi, o TCP_NODELAY é ativado para garantir que os dados sejam enviados como possível. Enquanto isso, o soquete real é enviado e a decisão de enviar uma mensagem é realizada em dois segmentos separados. Um lote adequado é feito se várias mensagens estiverem disponíveis no momento em que a primeira mensagem do lote deve ser enviada.

Eu executei uma captura com o tcpdump do qual os quadros abaixo foram extraídos. Após o handshake TCP inicial, o remetente (172.17.152.124) começa a enviar dados. O tamanho inicial da janela é de 5840 bytes para o receptor & 5792 bytes para o remetente.

Meu problema é que o remetente envia dois quadros (# 6 e # 7) e pára, aguardando que uma confirmação retorne do receptor. Tanto quanto eu posso ver, o tamanho da janela do receptor não é alcançado ea transferência não deve parar (384 bytes pendentes com um tamanho de janela de recepção inicial de 5840 bytes). Estou começando a pensar que não entendi corretamente o que é o TCP. Alguém pode ajudar a esclarecer?

update-2 : Minha carga de dados consiste em um número mágico seguido por um registro de data e hora. Eu isolei os pacotes atrasados, comparando os timestamps das cargas úteis com os timestamps colocados pelo tcpdump. A carga útil do quadro # 9 está muito próxima da das imagens nº 6 e nº 7 e claramente menor que a data e hora da confirmação recebida no quadro nº 8.

update-1 : O fato de o frame nº 9 não ser enviado imediatamente pode ser explicado pela lentidão do início do canal TCP. Na verdade, o problema também aparece quando a conexão está em execução por vários minutos, então a inicialização lenta não parece ser a explicação geral.

  1. 20:53:26.017415 IP 172.17.60.9.39943 > 172.17.152.124.56001: Flags [S], seq 2473022771, win 5840, options [mss 1460,sackOK,TS val 4219180820 ecr 0,nop,wscale 8], length 0

  2. 20:53:26.017423 IP 172.17.152.124.56001 > 172.17.60.9.39943: Flags [S.], seq 2948065596, ack 2473022772, win 5792, options [mss 1460,sackOK,TS val 186598852 ecr 219180820,nop,wscale 9], length 0

  3. 20:53:26.091940 IP 172.17.60.9.39943 > 172.17.152.124.56001: Flags [.], ack 1, win 23, options [nop,nop,TS val 4219180894 ecr 186598852], length 0

  4. 20:53:26.091958 IP 172.17.60.9.39943 > 172.17.152.124.56001: Flags [P.], seq 1:15, ack 1, w in 23, options [nop,nop,TS val 4219180895 ecr 186598852], length 14

  5. 20:53:26.091964 IP 172.17.152.124.56001 > 172.17.60.9.39943: Flags [.], ack 15, win 12, options [nop,nop,TS val 186598927 ecr 4219180895], length 0

  6. 20:53:26.128298 IP 172.17.152.124.56001 > 172.17.60.9.39943: Flags [P.], seq 1:257, ack 15, win 12, options [nop,nop,TS val 186598963 ecr 4219180895], length 256

  7. 20:53:26.128519 IP 172.17.152.124.56001 > 172.17.60.9.39943: Flags [P.], seq 257:385, ack 15, win 12, options [nop,nop,TS val 186598963 ecr 4219180895], length 128

  8. 20:53:26.202465 IP 172.17.60.9.39943 > 172.17.152.124.56001: Flags [.], ack 257, win 27, options [nop,nop,TS val 4219181005 ecr 186598963], length 0

  9. 20:53:26.202475 IP 172.17.152.124.56001 > 172.17.60.9.39943: Flags [.], seq 385:1833, ack 15, win 12, options [nop,nop,TS val 186599037 ecr 4219181005], length 1448

  10. 20:53:26.202480 IP 172.17.152.124.56001 > 172.17.60.9.39943: Flags [P.], seq 1833:2305, ack 15, win 12, options [nop,nop,TS val 186599037 ecr 4219181005], length 472

Se isso for importante, as duas extremidades são as caixas Linux RHEL5, com os kernels 2.6.18 e as placas de rede usando os drivers e1000e.

update-3 Conteúdo do /etc/sysctl.conf

[jlafaye@localhost ~]$ cat /etc/sysctl.conf | grep -v "^#" | grep -v "^$" 
net.ipv4.ip_forward = 0
net.ipv4.conf.default.rp_filter = 1
net.ipv4.conf.default.accept_source_route = 0
kernel.sysrq = 0
kernel.core_uses_pid = 1
net.ipv4.tcp_syncookies = 1
kernel.msgmnb = 65536
kernel.msgmax = 65536
kernel.shmmax = 68719476736
kernel.shmall = 4294967296
net.core.rmem_max = 16777216
net.core.wmem_max = 16777216
net.core.rmem_default = 1048576
net.core.wmem_default = 1048576
net.ipv4.tcp_rmem = 65536 4194304 16777216
net.ipv4.tcp_wmem = 65536 4194304 16777216 
net.core.netdev_max_backlog = 10000 
net.ipv4.tcp_window_scaling = 1
net.ipv4.tcp_mem = 262144 4194304 16777216
kernel.shmmax = 68719476736
    
por jlafaye 07.05.2012 / 23:08

2 respostas

10

Depois de explorar um pouco mais meu tráfego, pude ver que meus dados não passavam de uma sequência de pequenos disparos com pequenos períodos ociosos entre eles.

Com a ferramenta útil ss , consegui recuperar o tamanho atual da janela de congestionamento da minha conexão (consulte o valor cwnd na saída):

[user@localhost ~]$ /usr/sbin/ss -i -t -e | grep -A 1 56001

ESTAB 0 0 192.168.1.1:56001
192.168.2.1:45614 uid:1001 ino:6873875 sk:17cd4200ffff8804 ts sackscalable wscale:8,9 rto:277 rtt:74/1 ato:40 cwnd:36 send 5.6Mbps rcv_space:5792

Eu executei a ferramenta várias vezes e descobri que o tamanho da janela de congestionamento era regularmente redefinido para o valor inicial (10 ms, na minha caixa do Linux). A conexão estava constantemente voltando para a fase de início lento. Durante o período de início lento, rajadas com um número de mensagens que excedem o tamanho da janela foram atrasadas, esperando pelos acks relacionados aos primeiros pacotes do burst.

O fato de o tráfego consistir em uma sequência de explosões provavelmente explica a redefinição do tamanho da janela de congestionamento.

Ao desativar o modo de início lento após o período inativo, consegui me livrar dos atrasos.

[user@host ~]$ cat /proc/sys/net/ipv4/tcp_slow_start_after_idle 0

    
por 09.05.2012 / 22:48
2

Isso não vai ser algo sutil como um cenário em algum lugar. Isso vai ser um problema com o protocolo em camadas no topo do TCP ou um bug de código. Não há nenhuma mágica "ir mais rápido" alternar para TCP, exceto para casos incomuns, como redes com latência muito alta ou perda de pacotes causada por ruído.

A explicação mais óbvia seria se o código chama write ou send com partes muito pequenas. Você precisa acumular pelo menos 2KB por envio, idealmente 16KB. Você diz que agrupa as mensagens, mas não está claro o que isso significa. Você os transmite em uma chamada para write ou send ? Você agrupa-os em uma única unidade de dados de protocolo para o protocolo em camadas no topo do TCP? Fazer as duas coisas ajuda muito com a latência.

Além disso, livre-se do TCP_NODELAY. Pode reduzir o rendimento. É apenas para aplicativos que não foram projetados para trabalhar com TCP ou para aplicativos que não podem prever qual lado precisará transmitir em seguida.

A não ser, é claro, que você esteja na verdade colocando um protocolo em cima do TCP, onde você não sabe qual lado vai transmitir em seguida (como telnet , por exemplo). Então pode fazer sentido definir TCP_NODELAY. Especialização significativa é necessária para fazer esse tipo de protocolo funcionar com baixa latência. Se esta é a sua situação, poste mais detalhes sobre o protocolo que você está mergulhando em cima do TCP, como são seus tamanhos de unidade de dados de protocolo e o que determina qual lado transmite quando.

Se você realmente agrupar as mensagens disponíveis de uma só vez e passá-las em uma única chamada para write ou send , provavelmente o problema é que o outro lado não está enviando uma confirmação de camada de aplicativo para cada lote. Estes melhoram a latência, dando pacotes TCP ACKs para pegar carona. Seu protocolo deve incluí-los para garantir que os lados alternem, o que ajuda a manter a latência baixa.

    
por 07.05.2012 / 23:55