http keep-alive na era moderna

92

Portanto, de acordo com o autor do haproxy, quem sabe uma coisa ou duas sobre http:

O Keep-alive foi inventado para reduzir o uso da CPU em servidores quando as CPUs eram 100 vezes mais lentas. Mas o que não foi dito é que as conexões persistentes consomem muita memória, embora não possam ser utilizadas por ninguém, exceto pelo cliente que as abriu. Hoje, em 2009, as CPUs são muito baratas e a memória ainda é limitada a alguns gigabytes pela arquitetura ou pelo preço. Se um site precisa ser mantido ativo, há um problema real. Sites altamente carregados geralmente desabilitam o keep-alive para oferecer suporte ao número máximo de clientes simultâneos. A desvantagem real de não ter keep-alive é uma latência ligeiramente aumentada para buscar objetos. Os navegadores dobram o número de conexões simultâneas em sites não-keepalive para compensar isso.

(de http://haproxy.1wt.eu/ )

Isso está de acordo com a experiência de outras pessoas? ou seja, sem keep-alive - o resultado quase não se nota agora? (provavelmente é importante notar que com websockets etc - uma conexão é mantida "aberta" independentemente do status de keep-alive de qualquer maneira - para aplicativos muito responsivos). O efeito é maior para pessoas que estão remotas do servidor - ou se houver muitos artefatos para carregar do mesmo host ao carregar uma página? (Eu acho que coisas como CSS, imagens e JS estão cada vez mais vindo de CDNs compatíveis com cache).

Pensamentos?

(não tenho certeza se isso é uma coisa do serverfault.com, mas não cruzarei a postagem até que alguém me diga para movê-la para lá).

Michael Neale
fonte
1
É importante notar que em outra parte da documentação para haproxy que keep-alive é mencionado em outros termos mais favoráveis. Eu adoraria ouvir sobre a experiência das pessoas, especialmente para hospedagem em massa.
Michael Neale
"Obter um servidor de aplicação / web melhor projetado"? :-) Projetos mais recentes (como Jetty) com manuseio de conexão de continuação (-like) essencialmente mitigam os problemas de memória / thread. Além disso, "poucos GB" soa como um termo do servidor de 2008/2009 ;-)
3
Parece bobagem para mim. O RTT extra envolvido na criação de um novo soquete é um limite físico rígido que é frequentemente longo o suficiente para ser detectado por um ser humano e não pode ser reduzido dentro das leis conhecidas da física. Por outro lado, a RAM é barata, ficando cada vez mais barata, e não há razão para um soquete ocioso usar mais do que alguns kB dele.
Will Dean
2
mas o que é interessante é que isso não é apenas teoria - este é o autor de haproxy. Tudo o mais que ouço são teorias e suposições.
Michael Neale

Respostas:

141

Olá, já que sou o autor desta citação, vou responder :-)

Existem dois grandes problemas em sites grandes: conexões simultâneas e latência. As conexões simultâneas são causadas por clientes lentos, que demoram muito para baixar o conteúdo, e por estados de conexão ociosa. Esses estados de conexão ociosa são causados ​​pela reutilização da conexão para buscar vários objetos, conhecido como keep-alive, que é ainda mais aumentado pela latência. Quando o cliente está muito próximo do servidor, ele pode fazer um uso intensivo da conexão e garantir que quase nunca fique ocioso. Porém, quando a sequência termina, ninguém se preocupa em fechar rapidamente o canal e a conexão permanece aberta e sem uso por um longo tempo. Essa é a razão pela qual muitas pessoas sugerem o uso de um tempo limite de manutenção de atividade muito baixo. Em alguns servidores como o Apache, o menor tempo limite que você pode definir é um segundo, e muitas vezes é muito para sustentar altas cargas: se você tiver 20.000 clientes à sua frente e eles buscarem em média um objeto a cada segundo, você terá essas 20.000 conexões estabelecidas permanentemente. 20.000 conexões simultâneas em um servidor de propósito geral como o Apache é enorme, exigirá entre 32 e 64 GB de RAM dependendo de quais módulos são carregados, e você provavelmente não pode esperar ir muito mais longe, mesmo adicionando RAM. Na prática, para 20.000 clientes, você pode até ver 40.000 a 60.000 conexões simultâneas no servidor porque os navegadores tentarão configurar 2 a 3 conexões se tiverem muitos objetos para buscar. e você provavelmente não pode esperar ir muito mais alto, mesmo adicionando RAM. Na prática, para 20.000 clientes, você pode até ver 40.000 a 60.000 conexões simultâneas no servidor porque os navegadores tentarão configurar 2 a 3 conexões se tiverem muitos objetos para buscar. e você provavelmente não pode esperar ir muito mais alto, mesmo adicionando RAM. Na prática, para 20.000 clientes, você pode até ver 40.000 a 60.000 conexões simultâneas no servidor porque os navegadores tentarão configurar 2 a 3 conexões se tiverem muitos objetos para buscar.

Se você fechar a conexão após cada objeto, o número de conexões simultâneas diminuirá drasticamente. Na verdade, ele cairá por um fator correspondente ao tempo médio para baixar um objeto pelo tempo entre os objetos. Se você precisar de 50 ms para baixar um objeto (uma foto em miniatura, um botão, etc ...), e baixar em média 1 objeto por segundo como acima, então você terá apenas 0,05 conexão por cliente, que é de apenas 1000 conexões simultâneas para 20.000 clientes.

Agora, o tempo para estabelecer novas conexões vai contar. Os clientes muito remotos experimentarão uma latência desagradável. No passado, os navegadores costumavam usar grandes quantidades de conexões simultâneas quando o keep-alive estava desativado. Lembro-me de figuras de 4 no MSIE e 8 no Netscape. Isso realmente teria dividido a latência média por objeto por isso. Agora que o keep-alive está presente em todos os lugares, não estamos mais vendo esses números altos, porque fazer isso aumenta ainda mais a carga nos servidores remotos e os navegadores cuidam da proteção da infraestrutura da Internet.

Isso significa que, com os navegadores de hoje, é mais difícil fazer com que os serviços não-keep-alive sejam tão responsivos quanto os keep-alive. Além disso, alguns navegadores (por exemplo: Opera) usam heurística para tentar usar pipelinining. O pipelining é uma forma eficiente de usar o keep-alive, porque quase elimina a latência enviando várias solicitações sem esperar por uma resposta. Eu tentei em uma página com 100 fotos pequenas, e o primeiro acesso é cerca de duas vezes mais rápido do que sem keep-alive, mas o próximo acesso é cerca de 8 vezes mais rápido, porque as respostas são tão pequenas que apenas a latência conta (apenas Respostas "304").

Eu diria que idealmente deveríamos ter alguns ajustáveis ​​nos navegadores para fazê-los manter as conexões vivas entre os objetos buscados e descartá-los imediatamente quando a página estiver completa. Mas não estamos vendo isso, infelizmente.

Por esse motivo, alguns sites que precisam instalar servidores de uso geral, como o Apache, na parte frontal e que precisam oferecer suporte a grandes quantidades de clientes, geralmente precisam desativar o keep-alive. E para forçar os navegadores a aumentar o número de conexões, eles usam vários nomes de domínio para que os downloads possam ser paralelizados. É particularmente problemático em sites que fazem uso intensivo de SSL porque a configuração da conexão é ainda maior, pois há uma viagem de ida e volta adicional.

O que é mais comumente observado hoje em dia é que tais sites preferem instalar front-ends leves, como haproxy ou nginx, que não têm problemas em lidar com dezenas a centenas de milhares de conexões simultâneas, eles permitem o keep-alive no lado do cliente e desabilitam-no no Lado Apache. Nesse lado, o custo de estabelecer uma conexão é quase nulo em termos de CPU e nada perceptível em termos de tempo. Dessa forma, isso fornece o melhor dos dois mundos: baixa latência devido ao keep-alive com tempos limites muito baixos no lado do cliente e baixo número de conexões no lado do servidor. Todo mundo está feliz :-)

Alguns produtos comerciais melhoram ainda mais isso, reutilizando conexões entre o balanceador de carga frontal e o servidor e multiplexando todas as conexões de cliente sobre eles. Quando os servidores estão próximos ao LB, o ganho não é muito maior do que a solução anterior, mas muitas vezes exigirá adaptações no aplicativo para garantir que não haja risco de cruzamento de sessão entre usuários devido ao compartilhamento inesperado de uma conexão entre vários usuários . Em teoria, isso nunca deveria acontecer. A realidade é muito diferente :-)

Willy Tarreau
fonte
1
Obrigado pela resposta completa e abrangente! Fiquei um pouco confuso com vários comentários na página sobre keep-alive - mas tudo isso faz sentido.
Michael Neale
Curiosamente - observei o Chrome no Linux reutilizar uma conexão mantida ativa por alguns segundos - ou seja, o tempo que levou para abrir outra guia - essa outra guia era para um nome de host diferente, mas resolvida via curinga DNS para o mesmo servidor (em massa hospedagem virtual) - e, portanto, reutilizou a mesma conexão! (isso me causou alguma surpresa, não do tipo bom - obviamente, se manter vivo for apenas do lado do cliente, tudo bem).
Michael Neale
Tudo o que ouvi foi "use qualquer coisa diferente de apache e não é grande coisa". O que extrapolei foi "desative o mod_php e o passageiro e então até o apache pode ter uma chance de lutar".
coolaj86
@ CoolAJ86: o objetivo é absolutamente não criticar o Apache, e eu o uso pessoalmente. O ponto é que quanto mais genérico o servidor, menos opções você tem para escalar. Alguns módulos requerem o modelo pré-bifurcação, então você não pode escalar para um grande número de conexões. Mas, como explicado, não é grande coisa, pois você pode combiná-lo com outro componente gratuito como o haproxy. Por que alguém substituiria tudo neste caso? Melhor instalar o haproxy do que passar pelo aborrecimento de reimplementar seu aplicativo usando outro servidor!
Willy Tarreau
22

Nos anos desde que isto foi escrito (e postado aqui no stackoverflow), agora temos servidores como o nginx, que estão crescendo em popularidade.

O nginx, por exemplo, pode manter abertas 10.000 conexões keep-alive em um único processo com apenas 2,5 MB (megabytes) de RAM. Na verdade, é fácil manter abertos vários milhares de conexões com muito pouca RAM, e os únicos limites que você atingirá serão outros limites, como o número de identificadores de arquivos abertos ou conexões TCP.

Keep-alive era um problema não por causa de qualquer problema com a especificação do keep-alive em si, mas por causa do modelo de escalonamento baseado em processo do Apache e de keep-alives hackeados em um servidor cuja arquitetura não foi projetada para acomodá-lo.

Especialmente problemático é o Apache Prefork + mod_php + keep-alives. Este é um modelo em que cada conexão continuará ocupando toda a RAM que um processo PHP ocupa, mesmo se estiver completamente ocioso e apenas permanecer aberto como um keep-alive. Isso não é escalável. Mas os servidores não precisam ser projetados dessa forma - não há nenhuma razão particular para que um servidor precise manter cada conexão keep-alive em um processo separado (especialmente quando cada um desses processos tem um interpretador PHP completo). PHP-FPM e um modelo de processamento de servidor baseado em eventos como o do nginx resolvem o problema de forma elegante.

Atualização 2015:

SPDY e HTTP / 2 substituem a funcionalidade keep-alive do HTTP por algo ainda melhor: a capacidade não só de manter uma conexão ativa e fazer várias solicitações e respostas sobre ela, mas também de serem multiplexados, para que as respostas possam ser enviadas em qualquer ordem , e em paralelo, ao invés de apenas na ordem em que foram solicitadas. Isso evita que as respostas lentas bloqueiem as mais rápidas e remove a tentação dos navegadores de manter várias conexões paralelas abertas com um único servidor. Essas tecnologias destacam ainda mais as inadequações da abordagem mod_php e os benefícios de algo como um servidor da web baseado em eventos (ou, pelo menos, multiencadeado) acoplado separadamente a algo como PHP-FPM.

Thomasrutter
fonte
2

meu entendimento era que tinha pouco a ver com CPU, mas sim com a latência na abertura de sockets repetidos para o outro lado do mundo. mesmo se você tiver largura de banda infinita, a latência de conexão tornará todo o processo lento. amplificado se sua página tiver dezenas de objetos. mesmo uma conexão persistente tem uma latência de solicitação / resposta, mas é reduzida quando você tem 2 soquetes, pois, em média, um deve estar transmitindo dados enquanto o outro pode estar bloqueando. Além disso, um roteador nunca presumirá que um soquete está conectado antes de permitir que você escreva nele. Ele precisa do handshake de ida e volta completo. novamente, não tenho a pretensão de ser um especialista, mas é assim que sempre vi. o que seria realmente legal é um protocolo totalmente ASYNC (não, não um protocolo totalmente doente).

catchpolenet
fonte
sim - essa seria minha suposição. talvez seja uma troca - há um ponto em que a latência (devido à distância) significa que é um problema real
Michael Neale
ok, então a tipografia moderna faria você se conectar a um proxy que está próximo (talvez). mas você estende a questão para saber se os proxies devem usar conexões persistentes?
catchpolenet
@Michael Neale também, por causa de coisas como inicialização lenta do TCP, a penalidade de latência real é muito pior do que você esperaria.
MartinodF
talvez a compensação seja um período de tempo limite muito mais curto. se você tem solicitações de backup, por que desligar o soquete e começar de novo? mesmo 1 segundo permitiria que uma página carregasse com persistência total e, em seguida, desligasse os soquetes imediatamente após.
catchpolenet
2

Keep-alives muito longos podem ser úteis se você estiver usando um CDN "origin pull", como o CloudFront ou CloudFlare. Na verdade, isso pode ser mais rápido do que nenhum CDN, mesmo se você estiver servindo conteúdo totalmente dinâmico.

Se você mantiver ativos por muito tempo, de forma que cada PoP basicamente tenha uma conexão permanente com seu servidor, na primeira vez que os usuários visitarem seu site, eles poderão fazer um handshake TCP rápido com seu PoP local em vez de um handshake lento com você. (A própria luz leva cerca de 100 ms para percorrer meio mundo via fibra, e estabelecer uma conexão TCP requer que três pacotes sejam passados ​​de um lado para outro. SSL requer três viagens de ida e volta .)

mjs
fonte
1
Fiquei tentado a marcar com +1, mas seu segundo parágrafo contém esta observação incorreta de que a luz leva apenas 10 ms para viajar meio caminho ao redor do mundo. 10 ms da velocidade da luz no vácuo é 3000 km e 10 ms da velocidade da luz em uma fibra não é muito mais do que 2000 km; a meio caminho ao redor do mundo (ao longo da superfície) são 20.000 km. Portanto, seriam 100 ms --- se apenas sua fibra fosse diretamente de Londres para Sydney, em vez de provavelmente circunavegar a África pelo mar ou fazer a longa rota pelo Havaí ...
pirâmides
@pyramids Você está certo, ou eu digitei isso ou simplesmente fui um erro. Atualizará.
mjs