Por que os servidores da Web assíncronos apareceram?

Olá a todos. Em contato com Vladislav Rodin. Atualmente, sou o chefe do curso de arquiteto de carga alta da OTUS e também ensino cursos de arquitetura de software.

Além de ensinar, como você deve ter notado, também estou escrevendo material com direitos autorais para o blog OTUS no Haber e quero coincidir com o artigo de hoje para iniciar o curso Linux Administrator , para o qual o conjunto está aberto agora.





Introdução


Por que o aplicativo Web fica mais lento e não retém a carga? Os desenvolvedores, que foram os primeiros a encontrar essa pergunta e conduziram pesquisas em alguns sistemas, chegaram à conclusão decepcionante de que otimizar uma lógica de negócios por si só não seria suficiente. A resposta para essa pergunta está em um nível inferior - no nível do sistema operacional. Para que seu aplicativo retenha a carga, é necessário revisar seu conceito de arquitetura de forma que funcione efetivamente nesse nível. Isso levou ao surgimento de servidores Web assíncronos.

Infelizmente, não consegui encontrar um único material que me permita restaurar todos os relacionamentos causais na evolução dos servidores da web de uma só vez. Então surgiu a idéia de escrever este artigo, que, espero, se tornará esse material.

Recursos do sistema operacional Linux


Antes de falar sobre os modelos de servidores web, permito-me relembrar alguns recursos dos processos e threads no Linux. Precisamos disso ao analisar as vantagens e desvantagens dos modelos acima.

Mudança de contexto


Provavelmente, qualquer usuário que saiba que apenas um programa pode ser executado em um núcleo de processador de uma só vez perguntará: "Por que 20 programas podem ser lançados no meu processador de quatro núcleos de uma vez?".

De fato, isso se deve ao fato de que a multitarefa preventiva ocorre . O sistema operacional aloca um certo quantum de tempo (~ 50 μs) e coloca o programa para executar no kernel durante esse tempo. Após o tempo acabar, a troca de contexto é interrompida e alternada. Ou seja, o sistema operacional simplesmente coloca o próximo programa para executar. Como a troca ocorre com frequência, temos a impressão de que todos os programas funcionam simultaneamente. Preste atenção à alta frequência de comutação, isso será importante para a apresentação subsequente.

A mudança de contexto foi mencionada acima. O que inclui? Ao alternar o contexto, é necessário salvar os registros do processador, limpar seu pipeline de instruções, salvar as regiões de memória alocadas ao processo. Em geral, a operação é bastante cara. Demora ~ 0,5 μs, enquanto a execução de uma linha simples de código é ~ 1 ns. Além disso, com um aumento no número de processos por núcleo do processador, a sobrecarga para a alternância de contexto aumentará.

Modelos de servidor Web


Atualmente, existem os seguintes modelos de servidor da web:

  • trabalhador
  • prefork
  • assíncrono
  • combinado


Vamos discutir cada um deles separadamente.

Trabalhador e prefork


Historicamente, com esses modelos, tudo começou. A essência é muito simples : um cliente chega até nós, selecionamos um manipulador separado para ele, que processa o cliente recebido do começo ao fim. Um manipulador pode ser um processo (pré-fork) ou um encadeamento (trabalhador). Um exemplo desse servidor web é o conhecido Apache.

Farei uma reserva imediatamente: a criação de um novo manipulador para cada cliente é cara. Primeiro, com um número constante de núcleos, um aumento no número de processadores leva a um aumento na latência (devido às alternâncias de contexto). Em segundo lugar, a quantidade necessária de memória cresce linearmente com o aumento de clientes, porque, mesmo se você usar threads de compartilhamento de memória, cada thread terá sua própria pilha. Assim, o número de clientes processados ​​simultaneamente é limitado.o tamanho do pool, que, por sua vez, depende do número de núcleos do processador. O problema é resolvido usando métodos de escala vertical.

Outra desvantagem fundamental desses servidores é o uso não ideal de recursos. Os processos (ou threads) ficam ociosos a maior parte do tempo . Imagine a seguinte situação: durante o processamento do cliente, é necessário pegar alguns dados do disco rígido, fazer uma solicitação no banco de dados ou gravar algo na rede. Como a leitura de um disco rígido no Linux é uma operação de bloqueio , o processo (ou o encadeamento) ficará parado aguardando uma resposta, mas ainda participará da alocação do tempo do processador.

Worker vs prefork


Worker e prefork têm algumas diferenças fundamentais. Os fluxos são um pouco mais econômicos na memória porque eles são compartilhados. Pelo mesmo motivo, uma troca de contexto entre eles é mais fácil do que entre processos. No entanto, no caso de um trabalhador, o código se torna multiencadeado, porque os encadeamentos devem ser sincronizados. Como resultado, obtemos todos os "encantos" do código multiencadeado: torna-se mais difícil escrever, ler, testar e depurar.

Modelo assíncrono


Portanto, worker e prefork não permitem o processamento de um grande número de clientes ao mesmo tempo devido ao tamanho limitado do pool e também não usam recursos de maneira ideal devido à alternância de contexto e ao bloqueio de chamadas do sistema. Como você pode ver, o problema é multithreading e um agendador pesado do SO. Isso leva à seguinte idéia: vamos processar os clientes em apenas um segmento, mas vamos carregá-lo em 100%.

Esses servidores são baseados em um loop de eventos e em um modelo de reator ( máquina de eventos ). O código do cliente, iniciando uma operação de E / S, registra um retorno de chamadaem uma fila de prioridade (prioridade é tempo de prontidão). O loop de eventos pesquisa os descritores que aguardam E / S e atualiza a prioridade (se disponível). Além disso, o loop de eventos retira eventos da fila de prioridade, fazendo com que os retornos de chamada retornem o controle ao loop de eventos no final.

Esse modelo permite que você lide com um grande número de clientes, evitando sobrecarga para alternar o contexto . Este modelo não é perfeito e tem várias desvantagens. Em primeiro lugar, não é consumido mais de um núcleo do processador , porque existe apenas um processo. Isso é tratado usando um modelo combinado, que será discutido abaixo. Em segundo lugar, os clientes estão conectados por esse processo.. O código precisa ser escrito com cuidado. Vazamentos de memória, erros levam ao fato de que todos os clientes caem de uma só vez. Além disso, esse processo não deve ser bloqueado por nada, o retorno de chamada não deve consistir na solução de algumas tarefas difíceis, porque todos os clientes serão bloqueados. Em terceiro lugar, o código assíncrono é muito mais complicado . É necessário registrar um retorno de chamada adicional para que os dados não cheguem, para resolver o problema de como ramificar corretamente etc.

Modelo combinado


Este modelo é usado em servidores reais. Este modelo possui um conjunto de processos, cada um com um conjunto de encadeamentos, cada um dos quais, por sua vez, usa um modelo de processamento baseado na entrada-saída assíncrona. O Nginx apresenta um modelo combinado.

Conclusão


Assim, voltando ao básico do sistema operacional, examinamos as diferenças conceituais entre os modelos de servidor da web usados ​​no Apache e no Nginx. Cada um deles tem suas próprias vantagens e desvantagens, portanto sua combinação é frequentemente usada na produção.

A idéia de processamento assíncrono evoluiu ainda mais: no nível das plataformas de linguagem, surgiram os conceitos de fios / fibras / goroutines verdes, que permitem "esconder debaixo do capô" a assincronia de entrada e saída, deixando o desenvolvedor feliz com o belo código síncrono. No entanto, esse conceito merece um artigo separado.



Saiba mais sobre o curso.



All Articles