Como começar rapidamente com o VUE3.0: Entre no aprendizado
Depois que nosso serviço for lançado, ele será inevitavelmente agendado pelo ambiente em execução (como contêineres, pm2, etc.), as atualizações do serviço causarão reinicializações e várias exceções causarão. o processo trava; em geral, o ambiente em execução tem monitoramento de integridade do processo de serviço e reinicia o processo quando o processo é anormal. Durante a atualização, há também uma estratégia de atualização contínua. No entanto, a estratégia de agendamento do ambiente em execução trata nosso processo de serviço como uma caixa preta e não se preocupa com as condições internas de execução do processo de serviço. Portanto, nosso processo de serviço precisa detectar ativamente as ações de agendamento do ambiente em execução e então executar. algumas ações de limpeza de saída.
Portanto, hoje vamos resolver as várias situações que podem causar a saída do processo Node.js e o que podemos fazer ouvindo esses eventos de saída do processo.
Princípio:
Quando um processo deseja sair, não há nada mais do que duas situações: uma é que o processo sai ativamente e a outra é que ele recebe um sinal do sistema exigindo a saída do processo.
Saída de notificação de sinal do sistema
Sinais comuns do sistema estão listados no documento oficial do Node.js. Nós nos concentramos principalmente em alguns:
Ao receber um sinal de saída não forçado, o processo Node.js pode ouvir o sinal de saída e fazer alguma lógica de saída personalizada. Por exemplo, escrevemos uma ferramenta cli que leva muito tempo para executar uma tarefa. Se o usuário quiser sair do processo por meio de ctrl+c antes que a tarefa seja concluída, o usuário pode ser solicitado a esperar:
const readline = require(' linha de leitura'); process.on('SIGINT', () => { // Usamos readline para simplesmente implementar a interação na linha de comando const rl = readline.createInterface({ entrada: process.stdin, saída: process.stdout }); rl.question('A tarefa ainda não foi concluída, tem certeza que deseja sair?', resposta => { if (resposta === 'sim') { console.log('Execução da tarefa interrompida, processo de saída'); processo.exit(0); } outro { console.log('A tarefa continua...'); } rl.close(); }); }); //Simula uma tarefa que leva 1 minuto para ser executada const longTimeTask = () => { console.log('início da tarefa...'); setTimeout(() => { console.log('fim da tarefa'); }, 1000*60); }; longTimeTask();
O efeito é o seguinte.
O processo sai ativamente
do Node.js O processo sai ativamente, incluindo principalmente as seguintes situações:
Sabemos que pm2 tem o efeito de um processo daemon. No seu caso, quando o seu processo termina com um erro, o pm2 também implementa o efeito de um daemon. processo filho no modo cluster do Node.js (na verdade, pm2 tem lógica semelhante):
const cluster = require('cluster' ); const http = requer('http'); const numCPUs = require('os').cpus().length; const processo = require('processo'); //Código do processo principal if (cluster.isMaster) { console.log(`Iniciar o processo principal: ${process.pid}`); // Com base no número de núcleos da CPU, crie um processo de trabalho para (let i = 0; i < numCPUs; i++) { cluster.fork(); } //Ouça o evento de saída do processo de trabalho cluster.on('exit', (worker, code, signal) => { console.log(`Processo de trabalho ${worker.process.pid} encerrado, código de erro: ${código || sinal}, reiniciando...`); //Reinicia o processo filho cluster.fork(); }); } // Código do processo de trabalho if (cluster.isWorker) { // Ouça eventos de erro não detectados process.on('uncaughtException', error => { console.log(`Ocorreu um erro no processo de trabalho ${process.pid}`, erro); process.emit('desconectar'); processo.exit(1); }); //cria servidor web // Cada processo de trabalho escutará a porta 8000 (o Node.js irá lidar com isso internamente e não causará conflitos de porta) http.createServer((req, res) => { res.writeHead(200); res.end('olá mundon'); }).ouvir(8000); console.log(`Iniciar processo de trabalho: ${process.pid}`); }
Prática de aplicação
As diversas situações em que o processo Node.js sai foram analisadas acima. Agora faremos uma ferramenta para monitorar a saída do processo. Quando o processo Node.js termina, o usuário tem permissão para executar sua própria lógica de saída.
//hook de saída. //Salva as tarefas de saída que precisam ser executadas const tasks = []; //Adicionar tarefa de saída const addExitTask = fn => tasks.push(fn); const handleExit = (código, erro) => { // ...a implementação do handleExit é mostrada abaixo}; //Ouvir vários eventos de saída process.on('exit', code => handleExit(code)); // De acordo com as especificações POSIX, usamos 128 + número do sinal para obter o código de saída final // Consulte a imagem abaixo para obter o número do sinal. Você pode executar kill -l no sistema Linux para visualizar todos os números de sinal process.on. ('SIGHUP', () => handleExit(128 + 1)); process.on('SIGINT', () => handleExit(128 + 2)); process.on('SIGTERM', () => handleExit(128 + 15)); // Pressione ctrl+break para sair do sinal process.on('SIGBREAK', () => handleExit(128 + 21)); // O código de saída 1 representa um erro não detectado que fez com que o processo fosse encerrado process.on('uncaughtException', error => handleExit(1, error)); process.on('unhandledRejection', error => handleExit(1, error))
;
Em seguida, precisamos implementar a função real de saída do processo handleExit, porque a função de tarefa passada pelo usuário pode ser síncrona ou assíncrona, podemos usar process.nextTick para garantir que o código de sincronização do usuário foi executado, o que pode ser facilmente compreendido; .nextTick será executado após a conclusão da execução do código síncrono em cada estágio do loop de eventos (entenda process.nextTick); para tarefas assíncronas, precisamos que o usuário chame o retorno de chamada para nos informar que a tarefa assíncrona foi concluída:
// Marque se está saindo, evite múltiplas execuções de let isExiting = false; const handleExit = (código, erro) => { if (isExiting) retornar; isExiting = verdadeiro; // Marca que a ação de saída foi executada para evitar múltiplas chamadas para let hasDoExit = fasle; const doExit = () => { se (hasDoExit) retornar; hasDoExit = verdadeiro process.nextTick(() => process.exit(código)) } // Registra quantas tarefas assíncronas existem let asyncTaskCount = 0; // Após o término da tarefa assíncrona, o retorno de chamada que o usuário precisa chamar let ayncTaskCallback = () => { process.nextTick(() => { asyncTaskCount-- if (asyncTaskCount === 0) doExit() }) } //Executa todas as tarefas de saída task.forEach(taskFn => { // Se o número de parâmetros da função taskFn for maior que 1, considera-se que o parâmetro callback foi passado e é uma tarefa assíncrona if (taskFn.length > 1) { asyncTaskCount++ taskFn(erro, ayncTaskCallback) } outro { tarefaFn(erro) } }); // Se houver uma tarefa assíncrona if (asyncTaskCount > 0) { // Depois de mais de 10s, forçar a saída setTimeout(() => { doExit(); }, 10*1000) } outro { fazerSair() } };
Neste ponto, nossa ferramenta de monitoramento de saída de processo está concluída. Para a implementação completa, você pode visualizar esta biblioteca de código aberto async-exit-hook
https://github.com/darukjs/daruk-exit-hook
. sai do
nosso servidor web Ao reiniciar, sendo agendado por um contêiner em execução (pm2 ou docker, etc.), ou quando ocorre uma exceção e o processo termina, esperamos realizar ações de saída, como completar a resposta às solicitações conectadas ao. serviço, limpando a conexão do banco de dados, imprimindo logs de erros, acionando alarmes, etc., faça Depois de concluir a ação de saída e sair do processo, podemos usar a ferramenta de monitoramento de saída do processo agora mesmo para implementar:
const http = require(' http'); //cria servidor web servidor const = http.createServer((req, res) => { res.writeHead(200); res.end('olá mundon'); }).ouvir(8000); // Use a ferramenta que desenvolvemos acima para adicionar uma tarefa de saída do processo addExitTask((error, callback) => { // Imprime logs de erros, aciona alarmes, libera conexões de banco de dados, etc. console.log('Processo encerrado anormalmente', erro) // Pare de aceitar novas solicitações server.close((error) => { se (erro) { console.log('Erro ao parar de aceitar novas solicitações', erro) } outro { console.log('Parar de aceitar novas solicitações') } }) // Uma abordagem mais simples é esperar um certo período de tempo (aqui esperamos 5s) para que as solicitações existentes sejam concluídas // Se você deseja garantir completamente que todas as solicitações sejam processadas, você precisa registrar cada conexão e esperar até todas as conexões são liberadas. Execute a ação de saída // Você pode consultar a biblioteca de código aberto https://github.com/sebhildebrandt/http-graceful-shutdown setTimout(retorno de chamada, 5 * 1000) })
Resumo
Através do texto acima, acredito que você já esteja ciente das diversas situações que causam o encerramento do processo Node.js. Depois que o serviço estiver online, embora ferramentas como k8s e pm2 possam puxar continuamente o processo quando ele sai de forma anormal para garantir a disponibilidade do serviço, devemos também detectar ativamente a anormalidade ou agendamento do processo no código, de modo que para poder detectar problemas mais cedo.