A enorme popularidade do termo "assíncrona" estava na onda de Web 2.0, que varreu a web com JavaScript e Ajax. Mas o assíncrono é raro na maioria das linguagens de programação de alto nível. O PHP reflete melhor esse recurso: ele não apenas bloqueia de forma assíncrona, mas também não fornece vários threads. O PHP é executado de maneira síncrona de bloqueio. Tais vantagens são benéficas para os programadores escreverem a lógica de negócios na sequência, mas em aplicativos de rede complexos, o bloqueio faz com que não seja mais concomitante.
No lado do servidor, a E/S é muito cara e a E/S distribuída é mais cara. Somente quando o back-end pode responder rapidamente aos recursos, a experiência do front-end pode se tornar melhor. O Node.js é a primeira plataforma a usar assíncrono como o principal método de programação e conceito de design. Acompanhado por E/S assíncrona, orientada por eventos e fibra única, eles formam o tom do nó. Este artigo introduzirá como o nó implementa a E/S assíncrona.
1. Conceitos básicos
"Async" e "não bloqueando" soam a mesma coisa e, em termos de resultados reais, ambos alcançam o objetivo do paralelismo. Mas, da perspectiva da E/S do kernel de computador, existem apenas duas maneiras: bloqueando e não bloqueando. Portanto, assíncrono/síncrono e bloqueio/não bloqueio são na verdade duas coisas diferentes.
1.1 Bloqueio de E/S e E/S não bloqueando
Um recurso do bloqueio de E/S é que, depois de ligar, você deve esperar até que todas as operações sejam concluídas no nível do kernel do sistema antes que a chamada seja concluída. Tomando a leitura de um arquivo no disco como exemplo, essa chamada termina depois que o kernel do sistema conclui a pesquisa de disco, lê dados e copia dados na memória.
O bloqueio de E/S faz com que a CPU aguarde a E/S, desperdiçando o tempo de espera e o poder de processamento da CPU não pode ser totalmente utilizado. A característica da E/S não bloqueadora é que ele retornará imediatamente após a chamada e a fatia de tempo da CPU pode ser usada para lidar com outras transações após o retorno. Como a E/S completa não está concluída, o que é imediatamente retornado não é os dados esperados pela camada de negócios, mas apenas o status da chamada atual. Para obter os dados completos, o aplicativo precisa chamar repetidamente a operação de E/S para confirmar se ele foi concluído (ou seja, pesquisa). As técnicas de pesquisa precisam do seguinte:
1. LEAD: Verificar o status de E/S por chamadas repetidas é o método de desempenho mais original e mais baixo
2.Select: melhorias para ler, julgue o status do evento no descritor de arquivo. A desvantagem é que o número máximo de descritores de arquivos é limitado.
3.Poll: melhorias a serem selecionadas, usando listas vinculadas para evitar o limite máximo de números, mas quando há muitos descritores, o desempenho ainda é muito baixo
4.Epoll: Se nenhum evento de E/S for verificado durante a pesquisa, ele dormirá até que o evento ocorra e acorde. Este é o mecanismo de notificação de eventos de E/S mais eficiente em Linux.
A pesquisa atende à necessidade de E/S não bloqueadora para garantir a aquisição completa de dados, mas, para aplicativos, ela ainda pode contar como uma espécie de sincronização, pois ainda precisa esperar que a E/S retorne completamente. Durante a espera, a CPU é usada para atravessar o estado do descritor de arquivo ou para hibernar a espera de eventos ocorrer.
1.2 E/S assíncrona em ideal e realidade
A E/S assíncrona perfeita deve ser o aplicativo que inicia uma chamada sem bloqueio e pode lidar diretamente com a próxima tarefa sem pesquisar, basta passar os dados para o aplicativo por meio de um sinal ou retorno de chamada após a conclusão da E/S.
Na realidade, a E/S assíncrona possui implementações diferentes em diferentes sistemas operacionais. Por exemplo, *a plataforma NIX adota um pool de threads personalizado, enquanto o Windows Platform adota um modelo IOCP. O Node fornece Libuv como uma camada de encapsulamento abstrata para encapsular julgamentos de compatibilidade da plataforma e garante que a implementação da E/S assíncrona do nó superior e plataformas inferiores seja independente. Deve-se enfatizar que muitas vezes mencionamos que o nó é thread único, o que significa apenas que a execução do JavaScript está em um único thread, e existem outros pools de threads que realmente concluem as tarefas de E/S no nó.
2. A E/S assíncrona do Nó
2.1 Loop de eventos
O modelo de execução do Node é na verdade um loop de eventos. Quando o processo começa, o Node cria um loop infinito, e cada processo de execução do corpo do loop se torna um carrapato. Cada processo de carrapato é verificar se há eventos esperando para serem processados. Nesse caso, os eventos e suas funções de retorno de chamada relacionados serão removidas. Se houver funções de retorno de chamada associadas, elas serão executadas e o próximo loop será inserido. Se não houver mais processamento de eventos, saia do processo.
2.2 Observador
Existem vários observadores em cada loop de eventos e, perguntando a esses observadores, podemos determinar se existem eventos a serem processados. O loop de eventos é um modelo típico de produtor/consumidor. No nó, os eventos vêm principalmente de solicitações de rede, E/S de arquivo, etc. Esses eventos têm observadores de E/S de rede correspondentes, observadores de E/S de arquivo, etc. O loop de eventos retira o evento do observador e o processa.
2.3 Objeto de solicitação
Durante a transição do JavaScript para o kernel que executa operações de E/S, existe um produto intermediário chamado objeto de solicitação. Tomando o método mais simples do fs.open () no Windows (abra um arquivo e obtenha um descritor de arquivo de acordo com o caminho e os parâmetros especificados) como exemplo, desde chamadas JS até módulos internos, o sistema chama através do Libuv é realmente chamado de método uv_fs_open (). Durante o processo de chamada, um objeto de solicitação FSREQWRAP é criado e os parâmetros e métodos passados da camada JS são encapsulados neste objeto de solicitação. A função de retorno de chamada com a qual estamos mais preocupados é definida na propriedade Oncompete_Sym deste objeto. Depois que o objeto é embrulhado, empurre o objeto fsreqwrap no pool de threads e aguarde a execução.
Nesse ponto, a chamada JS retorna imediatamente e o Thread JS pode continuar a executar operações subsequentes. A operação de E/S atual está aguardando a execução no pool de threads, que completa a primeira etapa da chamada assíncrona.
2.4 Execute retornos de chamada
A notificação de retorno de chamada é a segunda fase da E/S assíncrona. Depois que a operação de E/S no pool de threads for chamada, os resultados obtidos serão armazenados e, em seguida, o IOCP é notificado de que a operação de objeto atual foi concluída e o encadeamento retorna o pool de threads. Durante cada execução de ticks, o observador de E/S do loop do evento chamará o método relevante para verificar se existem solicitações concluídas no pool de threads. Se existir, o objeto de solicitação será adicionado à fila do Observer de E/S e depois processado como um evento.
3. API assíncrona não i/o
Existem também algumas APIs assíncronas que não estão relacionadas à E/S no nó, como Timers setTimeout (), setInterval (), Process.NextTick () e SetImmdiate () que executam imediatamente tarefas de forma assíncrona etc., que serão introduzidas brevemente aqui.
3.1 API do timer
As APIs no lado do navegador de setTimeout () e setInterval () são consistentes. Seu princípio de implementação é semelhante ao E/S assíncrono, mas eles não exigem a participação do pool de threads de E/S. O temporizador criado chamando a API do timer será inserido em uma árvore vermelha e preta dentro do observador do timer. O carrapato de cada loop de evento iterará o objeto do timer da árvore vermelha e preta para verificar se o tempo excedeu. Se exceder, um evento será formado e a função de retorno de chamada será executada imediatamente. O principal problema com um cronômetro é que seu tempo de tempo não é particularmente preciso (milissegundos, dentro da tolerância).
3.2 API de execução de tarefas assíncronas
Antes do Node aparecer, muitas pessoas poderiam chamar isso para executar imediatamente uma tarefa de forma assíncrona:
A cópia do código é a seguinte:
setTimeout (function () {
// PENDÊNCIA
}, 0);
Devido às características dos loops de eventos, o temporizador não é preciso o suficiente e o uso de uma árvore vermelha e preta requer o uso de um timer, e a complexidade de vários tempo de operação é O (log (n)). O método Process.NextTick () apenas colocará a função de retorno de chamada na fila e o retiraria e o executará na próxima rodada de tick. A complexidade é O (1) e é mais eficiente.
Além disso, existe um método setImediate () semelhante ao método acima, atrasando a execução da função de retorno de chamada. No entanto, o primeiro tem maior prioridade que o último, porque o loop do evento verifica o observador em sequência. Além disso, a antiga função de retorno de chamada é salva em uma matriz e cada rodada de carrapato executará todas as funções de retorno de chamada na matriz; O último resultado é salvo em uma lista vinculada e cada rodada de tick executará apenas uma função de retorno de chamada.
4. Servidores orientados a eventos e de alto desempenho
O exemplo anterior ilustra como o nó implementa a E/S assíncrona. De fato, o Node também aplica a E/S assíncrona para processamento de soquete de rede, que também é a base para o nó construir um servidor da Web. Os modelos de servidor clássicos são:
1 Síncrono: apenas uma solicitação pode ser processada por vez, e o restante dos pedidos está em um estado de espera
2. Por processo/por solicitação: Inicie um processo para cada solicitação, mas os recursos do sistema são limitados e não têm escalabilidade.
3. Por thread/por solicitação: Inicie um thread para cada solicitação. Os threads são mais leves que os processos, mas cada thread ocupa uma certa quantidade de memória. Quando grandes solicitações simultâneas chegarem, a memória será exibida em breve.
O famoso Apache adota a forma por thread/por solicitação, e é por isso que é difícil lidar com a alta concorrência. O nó lida com solicitações através de métodos orientados a eventos, que podem salvar a sobrecarga de criar e destruir threads. Ao mesmo tempo, o sistema operacional possui menos threads ao agendar tarefas e o custo da troca de contexto também é muito baixo. O nó pode lidar com solicitações de maneira ordenada, mesmo com um grande número de conexões.
O conhecido servidor Nginx também abandona o método de multi-threading e adota o mesmo método orientado a eventos que o nó. Agora, o Nginx está em grande maneira para substituir o Apache. O NGINX é escrito em C Pure C e possui alto desempenho, mas é adequado apenas para servidores da Web, usado para proxy ou balanceamento de carga reverso, etc. O nó pode criar as mesmas funções que o NGINX e também pode lidar com várias empresas específicas, e seu próprio desempenho também é bom. Em projetos reais, podemos combiná -los para obter o melhor desempenho do aplicativo.