Muitos trabalhadores VS sem bloqueio e sem bloqueio


9

Suponha que exista um servidor HTTP que aceite conexões e, de alguma forma, espere que os cabeçalhos sejam totalmente enviados. Gostaria de saber qual é a maneira mais comum de implementá-lo e quais são os outros prós e contras. Só consigo pensar neles:

Muitos trabalhadores bloqueadores são bons porque:

  • É mais responsivo.
  • Mais fácil de introduzir novas conexões (os funcionários as escolhem por si mesmas, em vez de ficarem de fora esperando até que ele possa ser adicionado a uma lista sincronizada).
  • O uso da CPU se equilibra automaticamente (sem nenhum esforço adicional) à medida que o número de conexões aumenta e diminui.
  • Menos uso da CPU (threads bloqueados são retirados do loop de execução e não requerem lógica para saltar entre clientes).

Um único trabalhador sem bloqueio é bom porque:

  • Usa menos memória.
  • Menos vulnerável a clientes preguiçosos (que se conectam ao servidor e enviam cabeçalhos lentamente ou não enviam).

Como você provavelmente pode ver, na minha opinião, vários threads de trabalho parecem uma solução um pouco melhor em geral. O único problema é que é mais fácil atacar esse servidor.

Edit (more research): Alguns recursos que encontrei na web ( milhares de threads e E / S de bloqueio - a maneira antiga de escrever servidores Java é nova novamente (e muito melhor) por Paul Tyma) sugere que a abordagem de bloqueio geralmente é melhor, mas Ainda não sei como lidar com conexões falsas.

PS Não sugira o uso de algumas bibliotecas ou aplicativos para a tarefa. Estou mais interessado em saber como ele realmente funciona ou pode funcionar, em vez de fazê-lo funcionar.

PSS Dividi a lógica em várias partes e esta trata apenas da aceitação de cabeçalhos HTTP. Não os processa.


Lo, há muitos anos, escrevi um servidor encadeado com bloqueio de E / S, porque era fácil escrever. Um colega escreveu o outro tipo e funcionou admiravelmente. Elas eram duas formas da principal oferta de produtos em uma empresa em que eu trabalhava. Para "clientes preguiçosos" no cenário de bloqueio, você pode ter um tempo limite na recepção de dados.

Respostas:


4

Não há bala de prata

Na prática, depende ...

tl; dr - solução fácil, use nginx ...

Bloqueio:

Por exemplo, o Apache, por padrão, usa um esquema de bloqueio no qual o processo é bifurcado para todas as conexões. Isso significa que toda conexão precisa de seu próprio espaço de memória e a enorme quantidade de sobrecarga de alternância de contexto aumenta mais à medida que o número de conexões aumenta. Mas o benefício é que, quando uma conexão é fechada, o contexto pode ser descartado e toda e qualquer memória pode ser facilmente recuperada.

Uma abordagem multithread seria semelhante, pois a sobrecarga da alternância de contexto aumenta com o número de conexões, mas pode ser mais eficiente em termos de memória em um contexto compartilhado. O problema dessa abordagem é que é difícil gerenciar a memória compartilhada de maneira segura. As abordagens para superar os problemas de sincronização de memória geralmente incluem sua própria sobrecarga; por exemplo, o bloqueio pode congelar o encadeamento principal em cargas intensivas da CPU, e o uso de tipos imutáveis ​​adiciona muitas cópias desnecessárias de dados.

AFAIK, geralmente é preferível o uso de uma abordagem de vários processos em um servidor HTTP de bloqueio, porque é mais seguro / mais simples gerenciar / recuperar a memória de uma maneira segura. A coleta de lixo se torna um problema quando a recuperação de memória é tão simples quanto interromper um processo. Para processos de longa execução (por exemplo, um daemon), essa característica é especialmente importante.

Embora a sobrecarga de alternância de contexto possa parecer insignificante para um pequeno número de trabalhadores, as desvantagens se tornam mais relevantes à medida que a carga aumenta para centenas de milhares de conexões simultâneas. Na melhor das hipóteses, a alternância de contexto escala O (n) para o número de trabalhadores presentes, mas na prática é provavelmente pior.

Onde os servidores que usam bloqueio podem não ser a escolha ideal para cargas pesadas de E / S, eles são ideais para trabalhos intensivos em CPU e a passagem de mensagens é reduzida ao mínimo.

Sem bloqueio:

Não bloquear seria algo como Node.js ou nginx. Eles são especialmente conhecidos por dimensionar para um número muito maior de conexões por nó, sob carga intensiva de IO. Basicamente, uma vez que as pessoas atingem o limite superior do que os servidores baseados em processos / encadeamentos podem lidar, eles começam a explorar opções alternativas. Isso também é conhecido como o problema C10K (ou seja, a capacidade de lidar com 10.000 conexões simultâneas).

Os servidores assíncronos sem bloqueio geralmente compartilham muitas características com uma abordagem de multiencadeamento com bloqueio, pois você deve ter cuidado para evitar cargas intensivas da CPU, porque não deseja sobrecarregar o encadeamento principal. A vantagem é que a sobrecarga incorrida pela alternância de contexto é essencialmente eliminada e com apenas uma passagem de mensagem de contexto se torna um problema.

Embora possa não funcionar para muitos protocolos de rede, a natureza sem estado de HTTPs funciona especialmente bem para arquiteturas sem bloqueio. Ao usar a combinação de um proxy reverso e vários servidores HTTP sem bloqueio, é possível identificar e rotear os nós com carga pesada.

Mesmo em um servidor que possui apenas um nó, é muito comum a instalação incluir um servidor por núcleo de processador para maximizar a taxa de transferência.

Ambos:

O caso de uso 'ideal' seria uma combinação de ambos. Um proxy reverso na frente dedicado a rotear solicitações na parte superior, depois uma mistura de servidores de bloqueio e sem bloqueio. Sem bloqueio para tarefas de E / S, como veiculação de conteúdo estático, conteúdo de cache e conteúdo html. Bloqueio para tarefas pesadas da CPU, como codificação de imagens / vídeo, conteúdo de streaming, processamento de números, gravação de banco de dados, etc.

No seu caso:

Se você está apenas verificando os cabeçalhos, mas não processando as solicitações, o que está descrevendo é essencialmente um proxy reverso. Nesse caso, eu definitivamente adotaria uma abordagem assíncrona.

Eu sugiro verificar a documentação do proxy reverso interno do nginx .

A parte, de lado:

Li o artigo a partir do link que você forneceu e faz sentido que o assíncrono tenha sido uma má escolha para sua implementação específica. O problema pode ser resumido em uma declaração.

Verificou-se que, ao alternar entre clientes, o código para salvar e restaurar valores / estado era difícil

Eles estavam construindo uma plataforma estatal. Nesse caso, uma abordagem assíncrona significaria que você precisaria salvar / carregar constantemente o estado toda vez que o contexto alternar (ou seja, quando um evento for disparado). Além disso, no lado SMTP, eles estão fazendo muito trabalho intensivo da CPU.

Parece que eles tinham uma compreensão muito pobre de assíncrono e, como resultado, fizeram muitas suposições ruins.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.