skip to content
Gustavo Bindandi

Como eu escalei meu serviço SQS sem load balance

Nesta leitura eu explico o que eu fiz para processar mais de uma fila sem escalar novo servidor

Clusterização

o que é um cluster?

Cluster é um termo em inglês que significa “aglomerar” ou “aglomeração” e pode ser aplicado em vários contextos. No caso da computação, o termo define uma arquitetura de sistema capaz combinar vários computadores para trabalharem em conjunto ou pode denominar o grupo em si de computadores combinados. Cada estação é denominada “nodo” e, combinadas, formam o cluster. Em alguns casos, é possível ver referências como “supercomputadores” ou “computação em cluster” para o mesmo cenário, representando o hardware usado ou o software especialmente desenvolvido para conseguir combinar esses equipamentos. (fonte: opservices).


Qual o problema que eu tive?

Eu opero um serviço que consome mensagens de uma fila SQS padrão (não FIFO), responsável por enviar notificações via WhatsApp. À medida que a demanda aumentava, cada solicitação começou a levar entre 30 minutos e uma hora para ser processada.

Isso causou um impacto significativo, pois cada nova solicitação tinha que aguardar muito tempo até ser atendida.

Diante disso, considerei uma solução: adicionar um servidor adicional para trabalhar em paralelo com o primeiro. Essa abordagem reduziu o tempo de espera, mas os custos associados foram consideravelmente altos. Funcionou? Sim. No entanto, foi uma solução que não cabia no meu bolso.


Casos que podem precisar dessa solução:

  • Sistema para disparo de e-mails, imagina o cliente enviar uma campanha de 500k emails, o próximo que criar a campanha vai ter sua campanha enviada quando ?
  • Envio de SMS para confirmação de conta em sistemas de alta demanda (exemplo o Caixa Tem na covid)

Solucão

Diante disso, explorei outras alternativas disponíveis no mercado e descobri uma maneira de implementar clustering com o NestJS. Essa abordagem foi extremamente útil, e o código resultante é o seguinte:

main.ts:

import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ClusterService } from './cluster.service';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  await app.listen(8081);
}

ClusterService.clusterize(bootstrap);

cluster.service.ts

import * as cluster from 'cluster';
import * as os from 'os';
import { Injectable } from '@nestjs/common';

const numCPUs = 3 || os.cpus().length;

@Injectable()
export class ClusterService {
    static clusterize(callback: Function): void {
        // @ts-ignore: Ignorar erro relacionado a `isPrimary`
        if (cluster.isPrimary) { // Substituindo isMaster por isPrimary
            console.log(`Master server started on ${process.pid}`);
            for (let i = 0; i < numCPUs; i++) {
              // @ts-ignore: Ignorar erro relacionado a `isPrimary`
                cluster.fork();
            }
            // @ts-ignore:
            cluster.on('exit', (worker, code, signal) => {
                console.log(`Worker ${worker.process.pid} died. Restarting`);
                // @ts-ignore:
                cluster.fork();
            });
        } else {
            console.log(`Cluster server started on ${process.pid}`);
            callback();
        }
    }
}

Com este código, consegui executar meu servidor criando três ou mais processos idênticos, cada um operando de forma independente na memória. Essencialmente, esse código permite que o projeto seja executado múltiplas vezes no mesmo servidor, sem impactar o tráfego na porta utilizada pelo projeto.

Espero que esta solução possa ser útil para você também. Boa sorte!

Agradeço por ler este artigo! Até breve.