Pontos-chave
- A baixa sobrecarga do Fastify torna-o ideal para manter um grande número de ligações WebSocket abertas sem problemas de memória.
- O PM2 mantém um processo Node.js ativo em produção com recuperação instantânea após falhas, reinícios sem tempo de inatividade e gestão eficiente dos registos.
- Um mini-painel de telemetria construído com o Fastify e uma rota WebSocket mostra como os dados são transmitidos em tempo real para um navegador sem necessidade de polling.
- O Cloudways Velocity trata automaticamente da daemonização do PM2 e do encaminhamento por proxy inverso, pelo que uma aplicação WebSocket em Node.js pode passar do GitHub para a produção sem qualquer configuração manual do servidor.
Criar uma aplicação Node.js em tempo real no teu computador local é bastante simples. Basta instalar um pacote, abrir uma porta e começar a transmitir dados para um cliente. No entanto, é ao implementar essa mesma aplicação em ambiente de produção que as coisas se tornam mais complicadas.
Ao contrário dos pedidos HTTP normais, que são concluídos em milissegundos, os WebSockets mantêm as ligações abertas enquanto o cliente e o servidor precisarem de comunicar. Se a tua aplicação falhar ou o processo do Node.js parar, todos os clientes ligados perdem a ligação instantaneamente.
Para garantir a fiabilidade de uma aplicação Fastify em produção, não basta ter apenas um framework leve. Também precisas de um gestor de processos que mantenha a tua aplicação a funcionar em segundo plano e a reinicie automaticamente se algo correr mal.
Neste guia, vou explicar como o Fastify lida com WebSockets, porque é que o PM2 para Node.js é uma parte importante da execução de aplicações em produção, vou criar um projeto simples de WebSocket em Node.js e, depois, vou implementar toda a pilha no Cloudways Velocity, onde o PM2 já vem integrado.
Como os WebSockets afetam a tua infraestrutura de servidor
As arquiteturas HTTP REST padrão funcionam segundo um ciclo de vida rigoroso de pedido-resposta. Um cliente inicia um handshake TCP, envia cabeçalhos e dados do corpo, aguarda a resposta do servidor e encerra a ligação.Se um cliente precisar de atualizações do estado a cada poucos segundos, tem de repetir todo aquele processo de handshake vezes sem conta.Os WebSockets viram o HTTP tradicional de cabeça para baixo. Em vez de abrirem e fecharem um pedido totalmente novo sempre que precisas de dados, começam com um rápido handshake HTTP, atualizam o protocolo e deixam essa ligação totalmente aberta.
| Ligação HTTP | Ligação WebSocket |
| Cliente Pedido Servidor | Cliente Pedido de atualização Servidor |
| Cliente Resposta Servidor | Cliente 101 Protocolos de Comutação Servidor |
| A ligação é encerrada | A ligação permanece aberta |
| É criada uma nova ligação para cada pedido | A mesma ligação é reutilizada |
| A comunicação funciona apenas no modo pedido resposta | O cliente e o servidor podem trocar dados nos dois sentidos a qualquer momento |
Bem, manter uma ligação aberta para sempre parece ótimo, até veres o que isso realmente faz ao teu servidor nos bastidores:
A memória vai-se esgotando rapidamente. Cada socket que manténs aberto ocupa RAM. Se a tua estrutura estiver sobrecarregada, o teu servidor fica sem margem de manobra muito antes de atingires uma escala significativa.
Uma falha afeta toda a gente. Com as APIs REST padrão, se ocorrer um erro não tratado numa solicitação, um utilizador vê um erro 500. Mas e se um servidor WebSocket não gerido falhar? Boom. Todo o processo pára e milhares de utilizadores ativos ficam desconectados exatamente no mesmo milésimo de segundo.
Os proxies adoram cortar ligações inativas. Os equilibradores de carga, as firewalls e os proxies na nuvem cortam silenciosamente as ligações TCP se não houver tráfego durante alguns minutos. Tens de enviar sinais periódicos de «ping/pong» só para manter o canal ativo.
É precisamente aqui que o Fastify se destaca. Como a sua árvore de encaminhamento principal e a análise de JSON têm praticamente zero sobrecarga, consome muito menos memória por ligação do que os frameworks mais antigos — o que o torna ideal para manter abertas quantidades enormes de sockets.
Por que é que o PM2 é obrigatório para o Node.js em produção
Como o Node funciona numa única thread do ciclo de eventos, uma rejeição não tratada vai fazer com que toda a tua aplicação falhe. Ponto final.
E, obviamente, não podes simplesmente ligar-te por SSH a um servidor em funcionamento, executar o comando «node server.js» numa janela do terminal e fechar o teu portátil. Assim que a tua sessão do terminal terminar, a tua aplicação também deixa de funcionar.
É aí que entra o PM2. Pensa nele como um supervisor que fica mesmo por cima da tua aplicação Node, garantindo que esta se mantém ativa aconteça o que acontecer:
Execução em segundo plano: Transforma o teu processo num daemon para que funcione discretamente em segundo plano 24 horas por dia, 7 dias por semana.
Recuperação instantânea após falha: se um erro inesperado surgir e encerrar o thread, o PM2 reativa imediatamente a instância em milésimos de segundo.
Atualizações sem tempo de inatividade: podes implementar código novo e reiniciar as instâncias uma a uma, para que nenhum utilizador perca a ligação durante o processo.
Registo limpo: divide os teus registos padrão e fluxos de erros em ficheiros limpos e persistentes, sem que tenhas de configurar fluxos personalizados.
Normalmente, configurar tudo isto numa máquina Linux «a nu» implica lidar com ficheiros de serviços do systemd, criar scripts personalizados no ficheiro ecosystem.config.js, configurar a rotação de registos e configurar manualmente os proxies reversos do Nginx. Com o Cloudways Velocity, porém, não precisas de te preocupar com nada disso — a daemonização do PM2 e o encaminhamento do proxy são tratados automaticamente nos bastidores quando fazes a implementação.
Esquece o trabalho de DevOps e lança a tua aplicação
O Cloudways Velocity trata automaticamente da gestão de processos do PM2 e do proxy reverso, para que a tua aplicação permaneça online sem precisares de fazer nenhuma configuração manual.
Mini-projeto: Aplicação WebSocket em tempo real com Node.js e Fastify
Para mostrar como funciona a implementação do WebSocket com o Fastify, vou criar um mini-projeto como exemplo. Vou mantê-lo simples e criar um painel de telemetria em tempo real que recolhe os dados de utilização de memória e tempo de atividade do servidor e os apresenta numa página web.
Para esta configuração, vou primeiro criar e testar tudo localmente, usando o VS Code e o meu ambiente Node local. Depois, vou enviar tudo para o GitHub e implementar em produção no Cloudways Velocity.
Passo 1: Configurar o ambiente Fastify
Está na hora de preparar a pasta. Como tenho restrições de TI no meu portátil de trabalho, estou a usar um binário independente do Node.js. Isto significa que, por predefinição, o meu Prompt de Comando não sabe onde estão as minhas ferramentas do Node.Para resolver isso, vou abrir o CMD e indicar-lhe exatamente onde descompactei a minha pasta do Node. No meu caso, fica aqui:
set PATH=%PATH%;C:\Users\abdulrehman\Downloads\node-v24.18.0-win-x64\node-v24.18.0-win-x64

Depois de selecionar a pasta do Node, vou criar a pasta do meu projeto e inicializar a aplicação do Node.
mkdir fastify-pm2-demo cd fastify-pm2-demo npm init -y

Agora vou instalar o framework Fastify e o plugin WebSocket com este comando:
npm install fastify @fastify/websocket

Assim que estiver feito, vou adicionar alguns scripts ao ficheiro package.json para poder iniciar a aplicação com o comando «npm run dev» durante o desenvolvimento e com o comando «npm start» em produção.
{
"name": "fastify-pm2-demo",
"version": "1.0.0",
"description": "",
"main": "server.js",
"scripts": {
"start": "node server.js",
"dev": "node --watch server.js"
},
"keywords": [],
"author": "",
"license": "ISC",
"type": "commonjs",
"dependencies": {
"@fastify/websocket": "^11.3.0",
"fastify": "^5.11.0"
}
}
E com isto, o ambiente básico da minha aplicação está pronto.
Passo 2: Criar o servidor WebSocket
Vou agora abrir a pasta do projeto recém-criado no VS Code.
Dentro da pasta, vou criar um ficheiro chamado server.js. Este será o ficheiro que vai conter toda a lógica do backend.Aqui está o código que vou adicionar ao meu ficheiro server.js:
const Fastify = require('fastify');
const fastifyWebsocket = require('@fastify/websocket');
const fs = require('fs');
const path = require('path');
const fastify = Fastify({ logger: true });
fastify.register(fastifyWebsocket, {
options: { maxPayload: 1048576 }
});
// Serve the HTML dashboard on the root URL
fastify.get('/', (req, reply) => {
reply.type('text/html').send(fs.createReadStream(path.join(__dirname, 'index.html')));
});
fastify.register(async function (app) {
app.get('/ws', { websocket: true }, (socket, req) => {
app.log.info('Client connected to WebSocket stream');
socket.isAlive = true;
socket.on('pong', () => {
socket.isAlive = true;
});
const interval = setInterval(() => {
if (socket.readyState === 1) {
const memoryUsage = process.memoryUsage();
socket.send(JSON.stringify({
timestamp: new Date().toISOString(),
uptime: Math.floor(process.uptime()),
heapUsedMb: (memoryUsage.heapUsed / 1024 / 1024).toFixed(2)
}));
}
}, 1000);
socket.on('close', () => {
app.log.info('Client disconnected');
clearInterval(interval);
});
socket.on('error', (err) => {
app.log.error(`Socket error: ${err.message}`);
clearInterval(interval);
});
});
});
const pingInterval = setInterval(() => {
if (!fastify.websocketServer) return;
fastify.websocketServer.clients.forEach((socket) => {
if (socket.isAlive === false) {
return socket.terminate();
}
socket.isAlive = false;
socket.ping();
});
}, 30000);
fastify.addHook('onClose', (instance, done) => {
clearInterval(pingInterval);
done();
});
const start = async () => {
try {
const port = process.env.PORT || 3000;
const host = process.env.HOST || '0.0.0.0';
await fastify.listen({ port: Number(port), host });
console.log(\`Server running on http://host:{host}: host:{port}\`);
} catch (err) {
fastify.log.error(err);
process.exit(1);
}
};
start();

O que este código vai fazer é abrir uma rota em /ws. E, a cada segundo, vai verificar a memória do servidor e transmitir esses dados a quem estiver ligado.Ah, e também adicionei um mecanismo de ping. O que isso significa é que ele verifica a cada 30 segundos para cortar ligações inativas, para que a memória do servidor não fique a perder memória e acabe por falhar com o tempo.
Passo 3: Criar o painel HTML
Agora vou criar a lógica para mostrar os dados que vão ser recolhidos do servidor.
Para isso, vou criar um ficheiro index.html mesmo ao lado do meu ficheiro do servidor. Vou escrever algum código JavaScript básico que se liga ao backend. Basicamente, quando receber uma mensagem, vai inserir os valores em alguns elementos HTML com estilo definido por CSS.
Então… em vez de deixar que falhe se o servidor reiniciar, adicionei uma lógica de reconexão. Se a ligação cair, vai tentar voltar a ligar-se a cada três segundos.
Este é o código que vou adicionar ao meu ficheiro index.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Fastify WebSocket Dashboard</title>
<style>
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; background: #f4f4f5; padding: 2rem; color: #333; }
.card { background: white; padding: 1.5rem; border-radius: 8px; box-shadow: 0 4px 6px rgba(0,0,0,0.1); max-width: 400px; margin: 0 auto; }
h1 { font-size: 1.25rem; margin-top: 0; margin-bottom: 1.5rem; text-align: center; }
.status-container { text-align: center; margin-bottom: 1.5rem; }
.status { font-weight: bold; padding: 6px 12px; border-radius: 999px; display: inline-block; font-size: 0.875rem; }
.status.connected { background: #dcfce7; color: #166534; }
.status.disconnected { background: #fee2e2; color: #991b1b; }
.metric { display: flex; justify-content: space-between; margin-bottom: 0.75rem; border-bottom: 1px solid #e4e4e7; padding-bottom: 0.75rem; }
.metric:last-child { border-bottom: none; margin-bottom: 0; padding-bottom: 0; }
</style>
</head>
<body>
<div class="card">
<h1>Server Dashboard</h1>
<div class="status-container">
<div id="status" class="status disconnected">Disconnected</div>
</div>
<div class="metric">
<span>Server Uptime</span>
<strong id="uptime">0s</strong>
</div>
<div class="metric">
<span>Heap Memory Usage</span>
<strong id="heap">0 MB</strong>
</div>
<div class="metric">
<span>Last Ping</span>
<strong id="ping">--:--:--</strong>
</div>
</div>
<script>
// Dynamically grab the current host/port so it works on localhost, 127.0.0.1, or a live server
const wsProtocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
const wsUrl = `${wsProtocol}//${window.location.host}/ws`;
let ws;
function connect() {
console.log(`Attempting to connect to ${wsUrl}...`);
ws = new WebSocket(wsUrl);
ws.onopen = () => {
console.log('WebSocket connected!');
const statusEl = document.getElementById('status');
statusEl.textContent = 'Connected';
statusEl.className = 'status connected';
};
ws.onmessage = (event) => {
try {
const data = JSON.parse(event.data);
// Update the DOM with the live data
document.getElementById('uptime').textContent = data.uptime + 's';
document.getElementById('heap').textContent = data.heapUsedMb + ' MB';
const date = new Date(data.timestamp);
document.getElementById('ping').textContent = date.toLocaleTimeString();
} catch (err) {
console.error("Error parsing WebSocket message:", err);
}
};
ws.onclose = () => {
console.log('WebSocket disconnected. Retrying in 3 seconds...');
const statusEl = document.getElementById('status');
statusEl.textContent = 'Disconnected';
statusEl.className = 'status disconnected';
// Auto-reconnect if the connection drops
setTimeout(connect, 3000);
};
ws.onerror = (error) => {
console.error('WebSocket Error:', error);
};
}
// Initialize connection
connect();
</script>
</body>
</html>

Passo 4: Executar e testar localmente
Está na hora de testar. De volta ao CMD, vou iniciar o servidor de desenvolvimento:
npm run dev
Depois, vou abrir http://localhost:3000 no meu navegador.Se tudo tiver corrido bem, o painel fica verde assim que a página carregar e os números começam a aumentar a cada segundo.
O que acontece é que o JavaScript abriu uma ligação persistente ao servidor Fastify e, agora, o servidor envia dados atualizados por essa ligação a cada segundo. O navegador não está a pedir atualizações; elas estão a ser-lhe enviadas.Queres ver a lógica de desconexão a funcionar? Vou parar o servidor no terminal. O painel de controlo fica vermelho por si só, sem precisar de atualizar a página.
Se o reiniciares, ele volta a ligar-se sozinho. Continuas sem precisar de atualizar a página.
Passo 5: Enviar o código para o GitHub
O código funciona localmente, por isso, a seguir vai para o GitHub.Mas antes de mais, vou adicionar um ficheiro .gitignore com os módulos do Node.js. Senão, acabaria por colocar milhares de ficheiros de dependências no repositório, o que não quero.
node_modules/

Depois, basta usar os comandos habituais do Git para enviar tudo:
git init git add . git commit -m "Initial commit for Fastify WebSocket application" git branch -M main git remote add origin https://github.com/your-username/fastify-pm2-demo git push -u origin main
Basta atualizar a página do repositório e lá estão todos os meus ficheiros.
Passo 6: Implementação no Cloudways Velocity
Com o projeto no GitHub, posso implementá-lo na hospedagem Node.js da Cloudways (Velocity).Vou entrar no Cloudways, procurar o Velocity no menu do lado esquerdo e clicar em «Começar».
O plano Starter é mais do que suficiente para o que preciso, por isso vou escolher esse e clicar em «Continuar».
A seguir, quer saber onde está o meu código. O Cloudways dá-te a opção de escolher entre o GitHub, o GitLab ou o Bitbucket, e o meu está no GitHub, por isso é esse que vou ligar.
No menu suspenso, vou selecionar o meu repositório fastify-pm2-demo e clicar em «Continuar».
O Cloudways deteta o projeto e preenche as configurações automaticamente. Vou dar uma olhadela rápida e clicar em «Deploy Now».
A partir daí, ele carrega o código, inicia a aplicação no PM2 e configura o proxy reverso para reencaminhar o tráfego WebSocket para a aplicação Node. Sem configuração manual do PM2, sem mexer no Nginx, o que é mesmo o objetivo de usar o Velocity para isto.
Passo 7: Teste final em produção
Assim que a implementação estiver concluída, o Cloudways dá-me um URL temporário para a aplicação.
Vou copiar isso e abrir no meu navegador. Como o JavaScript do frontend verifica o protocolo por si próprio, a mudança para uma ligação segura wss:// acontece automaticamente, sem que eu tenha de configurar nada.
A página carrega, o ícone fica verde e os dados de telemetria começam a ser transmitidos, exatamente como acontecia localmente.
E aqui está uma aplicação Fastify WebSocket pronta para produção, a funcionar com o PM2 a garantir a sua estabilidade, ao vivo na hospedagem Node.js da Cloudways (Velocity).
Leva a tua aplicação Node.js para produção ainda hoje
Obtém a recuperação após falhas do PM2 e o proxy WebSocket automático prontos a usar com o Cloudways Velocity.
Conclusão:
É assim que se leva uma aplicação em tempo real do teu portátil para um servidor de produção a sério. Expliquei como o Fastify te dá a velocidade necessária para manter muitas ligações WebSocket abertas sem grande sobrecarga, e como o PM2 é a parte que evita que essas ligações caiam sempre que a aplicação encontra um erro ou o processo falha.
O miniprojeto juntou tudo isto: um fluxo de telemetria em tempo real a funcionar inicialmente a nível local e, depois, implementado no Cloudways Velocity, onde a parte do PM2 já está tratada, pelo que não há nenhuma configuração manual com que te tenhas de preocupar.
O código-fonte completo está disponível no meu GitHub, caso queiras cloná-lo. E se tiveres alguma dificuldade em qualquer ponto — seja na configuração do Fastify, nas partes relacionadas com o WebSocket ou na própria implementação —, deixa um comentário aqui em baixo e eu ajudo-te.
P. Para que serve o Fastify?
É um framework leve do Node.js para criar APIs e páginas web dinâmicas. É rápido, tem pouca sobrecarga e funciona com plugins, por isso só precisas de incluir os componentes de que a tua aplicação realmente precisa.
P. Para que serve o PM2?
Mantém as aplicações Node.js a funcionar em segundo plano e reinicia-as se falharem. Também tens um equilibrador de carga integrado, recargas sem tempo de inatividade e gestão automática de registos basicamente tudo aquilo que preferes não ter de gerir manualmente num servidor em produção.
P. O Fastify é mesmo mais rápido do que o Express?
Na maioria dos testes de desempenho, sim. O Express costuma ficar entre as 10 000 e as 20 000 solicitações por segundo, enquanto o Fastify chega às 45 000 a 50 000. Grande parte dessa diferença deve-se à sua serialização JSON e ao encaminhamento por árvore radix. Se alguma vez vais notar a diferença depende do teu tráfego, mas a diferença existe mesmo.
P. Como ativar o monitoramento do PM2?
Adiciona a opção –watch quando iniciares a aplicação: pm2 start app.js –watch. Ou configura isso de forma permanente, colocando watch: true no teu ficheiro de configuração do ecosystem.
P. O Fastify dá para usar em produção?
Sim, dá. A validação de esquema ajuda imenso, pois rejeita pedidos mal formados antes de estes chegarem à tua lógica, o que te dá um pouco mais de segurança e velocidade de graça. Entre isso, os plugins e o HTTP/2 nativo, ele aguenta-se bem sob carga real.
P. Que empresas usam o Fastify?
Há algumas grandes, como a Capital One, o Walmart, a American Express, a Voodoo e a Handsontable, entre outras.
Start Growing with Cloudways Today.
Our Clients Love us because we never compromise on these
[email protected]
O Abdul é um profissional de marketing experiente em tecnologia, movido a café e criativo, que adora manter-se a par das últimas actualizações de software e gadgets tecnológicos. É também um escritor técnico competente que consegue explicar conceitos complexos de forma simples para um público alargado. Abdul gosta de partilhar os seus conhecimentos sobre a indústria da nuvem através de manuais de utilizador, documentação e publicações em blogues.