Aumentando o número máximo de conexões TCP / IP no Linux

214

Estou programando um servidor e parece que meu número de conexões está sendo limitado, pois minha largura de banda não está saturada, mesmo quando defini o número de conexões como "ilimitado".

Como posso aumentar ou eliminar um número máximo de conexões que minha caixa Ubuntu Linux pode abrir por vez? O sistema operacional limita isso ou é o roteador ou o ISP? Ou é outra coisa?

red0ct
fonte
2
@ Monkey Software: Eu respondi isso de qualquer maneira, porque espero que isso possa ser útil para alguém que realmente está escrevendo um servidor no futuro.
derobert
1
@derobert: Eu vi esse +1. Na verdade, eu tive o mesmo pensamento após o meu comentário anterior, mas pensei em deixar o comentário em pé.
Lawrence Dol

Respostas:

395

O número máximo de conexões é afetado por certos limites nos lados do cliente e do servidor, embora um pouco diferente.

No lado do cliente: aumente o intervalo de portas epérmicas e diminua otcp_fin_timeout

Para descobrir os valores padrão:

sysctl net.ipv4.ip_local_port_range
sysctl net.ipv4.tcp_fin_timeout

O intervalo de portas epérmicas define o número máximo de soquetes de saída que um host pode criar a partir de um endereço IP específico. O fin_timeoutdefine o tempo mínimo em que esses soquetes permanecerão no TIME_WAITestado (inutilizáveis ​​após serem usados ​​uma vez). Os padrões usuais do sistema são:

  • net.ipv4.ip_local_port_range = 32768 61000
  • net.ipv4.tcp_fin_timeout = 60

Isso basicamente significa que seu sistema não pode garantir consistentemente mais do que (61000 - 32768) / 60 = 470soquetes por segundo. Se você não estiver satisfeito com isso, poderá começar aumentando o port_range. Definir o intervalo para 15000 61000é bastante comum nos dias de hoje. Você pode aumentar ainda mais a disponibilidade diminuindo o fin_timeout. Suponha que você faça as duas coisas, verá mais de 1500 conexões de saída por segundo, mais rapidamente.

Para alterar os valores :

sysctl net.ipv4.ip_local_port_range="15000 61000"
sysctl net.ipv4.tcp_fin_timeout=30

O acima não deve ser interpretado como os fatores que afetam a capacidade do sistema para fazer conexões de saída por segundo. Mas esses fatores afetam a capacidade do sistema de lidar com conexões simultâneas de maneira sustentável por longos períodos de "atividade".

Os valores padrão do Sysctl em uma caixa típica do Linux para tcp_tw_recycle& tcp_tw_reuseseriam

net.ipv4.tcp_tw_recycle=0
net.ipv4.tcp_tw_reuse=0

Eles não permitem a conexão de um soquete "usado" (em estado de espera) e forçam os soquetes a durar o time_waitciclo completo . Eu recomendo definir:

sysctl net.ipv4.tcp_tw_recycle=1
sysctl net.ipv4.tcp_tw_reuse=1 

Isso permite um rápido ciclo de soquetes no time_waitestado e reutilizá-los. Porém, antes de fazer essa alteração, verifique se isso não entra em conflito com os protocolos que você usaria para o aplicativo que precisa desses soquetes. Leia a postagem "Como lidar com o TCP TIME-WAIT" de Vincent Bernat para entender as implicações. A net.ipv4.tcp_tw_recycle opção é bastante problemática para servidores voltados para o público, pois não processa conexões de dois computadores diferentes atrás do mesmo dispositivo NAT , o que é um problema difícil de detectar e está esperando para te morder. Observe que net.ipv4.tcp_tw_recyclefoi removido do Linux 4.12.

No lado do servidor: o net.core.somaxconnvalor tem um papel importante. Limita o número máximo de solicitações na fila para um soquete de escuta. Se você tem certeza da capacidade do seu aplicativo de servidor, aumente do padrão 128 para algo como 128 a 1024. Agora você pode aproveitar esse aumento modificando a variável backlog de escuta na chamada de escuta do aplicativo, para um número igual ou superior.

sysctl net.core.somaxconn=1024

txqueuelenO parâmetro de suas placas Ethernet também tem um papel a desempenhar. Os valores padrão são 1000, portanto, aumente-os para 5000 ou até mais, se o seu sistema puder lidar com isso.

ifconfig eth0 txqueuelen 5000
echo "/sbin/ifconfig eth0 txqueuelen 5000" >> /etc/rc.local

Da mesma forma, amplie os valores para net.core.netdev_max_backloge net.ipv4.tcp_max_syn_backlog. Seus valores padrão são 1000 e 1024, respectivamente.

sysctl net.core.netdev_max_backlog=2000
sysctl net.ipv4.tcp_max_syn_backlog=2048

Agora, lembre-se de iniciar os aplicativos do lado do cliente e do servidor aumentando os limites de FD no shell.

Além da acima mencionada, uma técnica mais popular usada pelos programadores é reduzir o número de chamadas de gravação TCP . Minha preferência é usar um buffer no qual envio os dados que desejo enviar ao cliente e, em pontos apropriados, escrevo os dados no buffer no soquete real. Essa técnica permite que eu use pacotes de dados grandes, reduza a fragmentação, reduz a utilização da CPU tanto na área do usuário quanto no nível do kernel.

mdk
fonte
4
Resposta brilhante! Meu problema era um pouco diferente, ou seja, eu estava tentando mover as informações da sessão de um armazenamento de sessão no nível do aplicativo para redis via PHP. Por alguma razão, não pude adicionar mais de 28230 sessões sem adicionar muito sono de uma só vez, sem erros observados no php ou nos logs do redis. Nós quebramos nossa cabeça por um dia inteiro, até que pensei que talvez o problema não estivesse no php / redis, mas na camada do tcp / ip que conecta os dois e chegamos a essa resposta. Conseguiu corrigir o problema em pouco tempo depois disso :) Muito obrigado!
S1d
27
Não esqueça que estamos sempre falando sobre a porta IP +. Você pode ter soquetes "ilimitados" abertos na porta XY de muitos IPs diferentes. O limite de 470 se aplica a soquetes abertos simultâneos apenas ao mesmo IP. Outro IP pode ter suas próprias conexões 470 nas mesmas portas.
Marki555
6
@ Marki555: Seu comentário é MUITO CORRETO. Os aplicativos desenvolvidos para gerar e sustentar um grande número de conexões de saída devem ter um "conhecimento" dos IPs disponíveis para a criação de conexões de saída e, em seguida, vincular-se adequadamente a esses endereços IP usando algum tipo de "algoritmo round-robin" e manter um "placar".
Mdk
8
Esta resposta tem erros. Primeiro, net.ipv4.tcp_fin_timeout é apenas para o estado FIN_WAIT_2 ( cs.uwaterloo.ca/~brecht/servers/ip-sysctl.txt ). Em segundo lugar, como disse @Eric, "470 soquetes a qualquer momento" não está correto.
Sharvanath
3
@ MDK: Eu não estou claro com esta parte do cálculo (61000 - 32768) / 60 = 470 sockets per second. Você pode elaborar isso?
Tom Taylor
64

Existem algumas variáveis ​​para definir o número máximo de conexões. Provavelmente, você está ficando sem números de arquivos primeiro. Verifique ulimit -n. Depois disso, há configurações em / proc, mas essas são padronizadas para dezenas de milhares.

Mais importante, parece que você está fazendo algo errado. Uma única conexão TCP deve poder usar toda a largura de banda entre duas partes; se não for:

  • Verifique se a configuração da janela TCP é grande o suficiente. Os padrões do Linux são bons para tudo, exceto links inet realmente rápidos (centenas de mbps) ou links via satélite rápidos. Qual é o seu produto de atraso na largura de banda *?
  • Verifique a perda de pacotes usando ping com pacotes grandes ( ping -s 1472...)
  • Verifique a limitação da taxa. No Linux, isso é configurado comtc
  • Confirme se a largura de banda que você acha que existe realmente existe usando, por exemplo, iperf
  • Confirme se o seu protocolo está correto. Lembre-se de latência.
  • Se for um gigabit + LAN, você pode usar pacotes jumbo? Você está?

Possivelmente eu entendi mal. Talvez você esteja fazendo algo como o Bittorrent, onde você precisa de muitas conexões. Nesse caso, você precisa descobrir quantas conexões você está realmente usando (tente netstatou lsof). Se esse número for substancial, você pode:

  • Tenha muita largura de banda, por exemplo, 100mbps +. Nesse caso, talvez seja necessário atualizar o ulimit -n. Ainda assim, ~ 1000 conexões (padrão no meu sistema) são poucas.
  • Problemas de rede que estão atrapalhando suas conexões (por exemplo, perda de pacotes)
  • Tenha algo mais lento, por exemplo, largura de banda IO, especialmente se você estiver procurando. Você já conferiu iostat -x?

Além disso, se você estiver usando um roteador NAT de nível consumidor (Linksys, Netgear, DLink etc.), tenha cuidado para que você possa exceder suas habilidades com milhares de conexões.

Espero que isso ofereça alguma ajuda. Você está realmente fazendo uma pergunta sobre redes.

derobert
fonte
16

Para melhorar a resposta dada por derobert,

Você pode determinar qual é o limite de conexão do seu sistema operacional catting nf_conntrack_max.

Por exemplo: cat / proc / sys / net / netfilter / nf_conntrack_max

Você pode usar o script a seguir para contar o número de conexões TCP a um determinado intervalo de portas TCP. Por padrão 1-65535.

Isso confirmará se você está ou não atingindo o limite máximo de conexão do sistema operacional.

Aqui está o script.

#!/bin/bash
OS=$(uname)

case "$OS" in
    'SunOS')
            AWK=/usr/bin/nawk
            ;;
    'Linux')
            AWK=/bin/awk
            ;;
    'AIX')
            AWK=/usr/bin/awk
            ;;
esac

netstat -an | $AWK -v start=1 -v end=65535 ' $NF ~ /TIME_WAIT|ESTABLISHED/ && $4 !~ /127\.0\.0\.1/ {
    if ($1 ~ /\./)
            {sip=$1}
    else {sip=$4}

    if ( sip ~ /:/ )
            {d=2}
    else {d=5}

    split( sip, a, /:|\./ )

    if ( a[d] >= start && a[d] <= end ) {
            ++connections;
            }
    }
    END {print connections}'
whitehat237
fonte
3
which awké seu amigo para determinar caminho para awk, SunOS tem um link para ele também :)
Panagiotis Moustafellos
2
@PanagiotisM. whichconta com o programa PATHem que você pode usar, em awkvez de fornecer o caminho completo. (Dito isso, não tenho certeza se a solução no script está mais próxima da perfeição, mas não é disso que se trata).
Michael Krelin - hacker de
5
Eu amo como esse script fica balístico para determinar a awklocalização, mas assume que o shell é sempre /bin/bash (dica profissional: o AIX5 / 6 nem sequer tem bash por padrão).
kubanczyk
A awkdetecção é útil? Pessoalmente, eu simplesmente assumiria ter uma PATHalternativa correta, mas uma alternativa razoável poderia ser/usr/bin/env awk e, /usr/bin/env bashrespectivamente. Pelo que vale a pena, o local está errado no meu sistema Linux. É na /usr/bin/awknão/bin/awk
Wolph
1
Quando executo esse script, recebo 798, então o que isso significa?
10

No nível do aplicativo, aqui está algo que um desenvolvedor pode fazer:

Do lado do servidor:

  1. Verifique se o balanceador de carga (se você tiver) funciona corretamente.

  2. Transforme o tempo limite do TCP lento em resposta 503 Rápida Imediata, se o balanceador de carga funcionar corretamente, ele deverá escolher o recurso de trabalho a ser veiculado e é melhor do que ficar parado com massagens de erro inesperadas.

Por exemplo: se você estiver usando o servidor do nó, você pode usar o toobusy do npm. Implementação algo como:

var toobusy = require('toobusy');
app.use(function(req, res, next) {
  if (toobusy()) res.send(503, "I'm busy right now, sorry.");
  else next();
});

Por que 503? Aqui estão algumas boas idéias para sobrecarga: http://ferd.ca/queues-don-t-fix-overload.html

Também podemos trabalhar no lado do cliente:

  1. Tente agrupar as chamadas em lote, reduza o tráfego e o número total de solicitações, por cliente e servidor.

  2. Tente criar um cache na camada intermediária para lidar com solicitações duplicadas desnecessárias.

Kev
fonte