Temos infraestrutura distribuída em alguns dos principais locais do mundo: Cingapura, Londres e Los Angeles. O RTT entre dois locais está acima de > 150 ms.
Recentemente, atualizamos todos os servidores para usar links de 1 Gbps (de 100 Mbps). Fizemos alguns testes baseados em TCP entre servidores em diferentes locais e observamos alguns resultados surpreendentes. Estes resultados são completamente repetíveis.
- Los Angeles (100Mbps) para Londres (100Mbps): ~ 96Mbps de taxa de transferência
- Los Angeles (100 Mbps) para Londres (1 Gbps): ~ taxa de transferência de 96 Mbps
- Los Angeles (1Gbps) para Londres (100Mbps): taxa de transferência 10-40Mbps (volátil)
- Los Angeles (1Gbps) para Londres (1Gbps): taxa de transferência 10-40Mbps (volátil)
- Los Angeles (1Gbps) para Los Angeles (1Gbps): > taxa de transferência de 900 Mbps
Parece que sempre que o remetente está funcionando a 1 Gbps, nossa taxa de transferência sofre muito significativamente em links longos.
A abordagem de teste anterior é extremamente simples - estou usando apenas cURL para fazer o download de um binário de 1 GB do servidor de destino (assim, no caso acima, o cliente cURL é executado no servidor de Londres e é baixado do LA, o remetente). Isso está usando uma única conexão TCP, é claro.
Repetindo os mesmos testes em UDP usando iperf, o problema desaparece!
- Los Angeles (100Mbps) para Londres (100Mbps): ~ 96Mbps de taxa de transferência
- Los Angeles (100 Mbps) para Londres (1 Gbps): ~ taxa de transferência de 96 Mbps
- Los Angeles (1Gbps) para Londres (100Mbps): ~ 96Mbps de taxa de transferência
- Los Angeles (1Gbps) para Londres (1Gbps): > taxa de transferência de 250 Mbps
Isso aponta diretamente para algum problema de configuração TCP ou NIC / porta aos meus olhos.
Ambos os servidores estão executando o CentOS 6.x, com o TCP cúbico. Ambos têm 8MB de envio TCP máximo & receber janelas e ter registros de data e hora TCP e reconhecimentos seletivos ativados. A mesma configuração TCP é usada em todos os casos de teste. A configuração completa do TCP está abaixo:
net.core.somaxconn = 128
net.core.xfrm_aevent_etime = 10
net.core.xfrm_aevent_rseqth = 2
net.core.xfrm_larval_drop = 1
net.core.xfrm_acq_expires = 30
net.core.wmem_max = 8388608
net.core.rmem_max = 8388608
net.core.wmem_default = 131072
net.core.rmem_default = 131072
net.core.dev_weight = 64
net.core.netdev_max_backlog = 1000
net.core.message_cost = 5
net.core.message_burst = 10
net.core.optmem_max = 20480
net.core.rps_sock_flow_entries = 0
net.core.netdev_budget = 300
net.core.warnings = 1
net.ipv4.tcp_timestamps = 1
net.ipv4.tcp_window_scaling = 1
net.ipv4.tcp_sack = 1
net.ipv4.tcp_retrans_collapse = 1
net.ipv4.tcp_syn_retries = 5
net.ipv4.tcp_synack_retries = 5
net.ipv4.tcp_max_orphans = 262144
net.ipv4.tcp_max_tw_buckets = 262144
net.ipv4.tcp_keepalive_time = 7200
net.ipv4.tcp_keepalive_probes = 9
net.ipv4.tcp_keepalive_intvl = 75
net.ipv4.tcp_retries1 = 3
net.ipv4.tcp_retries2 = 15
net.ipv4.tcp_fin_timeout = 60
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_tw_recycle = 0
net.ipv4.tcp_abort_on_overflow = 0
net.ipv4.tcp_stdurg = 0
net.ipv4.tcp_rfc1337 = 0
net.ipv4.tcp_max_syn_backlog = 2048
net.ipv4.tcp_orphan_retries = 0
net.ipv4.tcp_fack = 1
net.ipv4.tcp_reordering = 3
net.ipv4.tcp_ecn = 2
net.ipv4.tcp_dsack = 1
net.ipv4.tcp_mem = 1528512 2038016 3057024
net.ipv4.tcp_wmem = 4096 131072 8388608
net.ipv4.tcp_rmem = 4096 131072 8388608
net.ipv4.tcp_app_win = 31
net.ipv4.tcp_adv_win_scale = 2
net.ipv4.tcp_tw_reuse = 0
net.ipv4.tcp_frto = 2
net.ipv4.tcp_frto_response = 0
net.ipv4.tcp_low_latency = 0
net.ipv4.tcp_no_metrics_save = 0
net.ipv4.tcp_moderate_rcvbuf = 1
net.ipv4.tcp_tso_win_divisor = 3
net.ipv4.tcp_congestion_control = cubic
net.ipv4.tcp_abc = 0
net.ipv4.tcp_mtu_probing = 0
net.ipv4.tcp_base_mss = 512
net.ipv4.tcp_workaround_signed_windows = 0
net.ipv4.tcp_dma_copybreak = 4096
net.ipv4.tcp_slow_start_after_idle = 1
net.ipv4.tcp_available_congestion_control = cubic reno
net.ipv4.tcp_allowed_congestion_control = cubic reno
net.ipv4.tcp_max_ssthresh = 0
net.ipv4.tcp_thin_linear_timeouts = 0
net.ipv4.tcp_thin_dupack = 0
Em anexo estão algumas imagens dos gráficos de I / O do wireshark de alguns casos de teste (desculpe, não posso postar imagens diretamente ainda):
Caso de teste 1 (100Mbps - > 100Mbps) - boa transferência suave. Nenhuma perda na captura.
- link
Caso de teste 3 (1Gbps - > 100Mbps) - a transferência de vota, leva muito tempo para chegar a qualquer velocidade - nunca se aproxima de 100Mbps. Ainda não há perdas / retransmissões na captura!
- link
Portanto, em resumo, quando um link longo é usado com uma conexão de 1 Gbps, obtemos uma taxa de transferência TCP muito menor do que quando usamos uma conexão de 100 Mbps.
Eu aprecio muito algumas dicas de especialistas em TCP por aí!
Obrigado!
UPDATE (2013-05-29):
Solucionamos o problema com o caso de teste nº 4 acima (remetente de 1 Gbps, receptor de 1 Gbps, em um RTT grande). Agora podemos atingir ~ 970Mbps dentro de alguns segundos após o início da transferência. O problema aparece como um switch usado com o provedor de hospedagem. Mudar para um diferente resolveu isso.
No entanto, o caso de teste nº 3 continua a ser problemático. Se tivermos um receptor rodando a 100Mbps e o remetente a 1Gbps, veremos uma espera de aproximadamente 2-3 minutos para o receptor atingir 100Mbps (mas agora atinge a taxa total, diferente de antes). Assim que derrubarmos o remetente para 100Mbps ou aumentarmos o receptor para 1Gbps, o problema desaparece e podemos aumentar a velocidade máxima em um segundo ou dois.
O motivo subjacente é que estamos vendo perdas, é claro, logo após o início da transferência. No entanto, isso não concorda com minha compreensão de como o slow-start funciona; a velocidade da interface não deve ter qualquer influência nisso, já que deve ser governada pelos ACKs do receptor.
Sugestões recebidas com gratidão por favor! Se eu pudesse oferecer uma recompensa aqui, eu faria!