Implementando Circuit Breaker e Bulkhead em microservices NestJS com Cockatiel
Por John Pires · 09 de março de 2026

Implementando Circuit Breaker e Bulkhead em microservices NestJS com Cockatiel
Cenario pratico do dia a dia
O problema real: falhas em cadeia
Em arquiteturas de microservices, uma chamada lenta ou instável pode propagar falhas. Um downstream com latência crescente ou quedas intermitentes degrada a experiência do usuário e pode derrubar serviços dependentes. Sem mecanismos de contenção, o tráfego se acumula e a latência compõe em cascata.
Onde aplicar circuit breaker e bulkhead
Use circuit breaker para evitar que sistemas repetidamente tentem usar um recurso que está falhando. Use bulkhead para limitar concorrência e proteger recursos críticos de sobrecarga. Em NestJS, aplicamos ambos ao cliente HTTP usado para chamar serviços externos (HttpService/@nestjs/axios) ou adaptadores de cliente gRPC/AMQP.
Regra prática: proteja o ponto de falha mais crítico primeiro — normalmente a integração HTTP com terceiros ou com outro microservice.
Nos exemplos a seguir, consideramos um microservice que consome outro serviço via HTTP e precisa de resiliência. Assumimos familiaridade com módulos, providers e injeção do NestJS.
Arquitetura da solucao
Componentes principais
Arquitetura proposta:
- ResilienceModule: cria e configura políticas de Cockatiel (circuit breaker, bulkhead, retry, timeout).
- ResilientHttpService: wrapper injetável sobre HttpService que aplica políticas nas chamadas outbound.
- Controladores e services do domínio: dependem de ResilientHttpService em vez de HttpService direto.
- Fallbacks e adaptadores: tratam respostas quando políticas rejeitam/exaurem.
Fluxo de execução
Quando um controller solicita dados que dependem de uma chamada externa, o fluxo é:
- Controller chama Service de domínio.
- Service injeta ResilientHttpService e pede dados.
- ResilientHttpService executa a chamada via uma política combinada (bulkhead + circuit breaker + timeout [+ retry opcional]).
- Se a chamada for bem-sucedida, resposta retorna normalmente; se houver falha, a política decide: retry (se aplicável), abrir circuito ou rejeitar por bulkhead.
- Em casos de circuito aberto ou bulkhead saturado, retornamos uma resposta controlada ou lança exceção tratada por filtros globais.
Esse design separa responsabilidade de resiliência do código de negócio e mantém testabilidade.
Codigo completo com explicacoes
1) Instalacao e dependencias
Instale bibliotecas necessárias:
npm install @nestjs/axios axios cockatiel
Explicacao: @nestjs/axios fornece HttpService (baseado em Axios). Cockatiel é a lib de resiliência que fornece circuit breaker, bulkhead e outras políticas.
2) ResilienceModule: configurando as politicas
Este módulo cria instâncias de políticas e as registra como providers para reuso. Ajuste parâmetros conforme SLAs e comportamento real do seu ambiente.
import { Module, Global } from '@nestjs/common';
import { Policy } from 'cockatiel';
const circuitBreakerPolicy = Policy.handleAll().circuitBreaker(30000, 5);
const bulkheadPolicy = Policy.bulkhead(10, 50);
@Global()
@Module({
providers: [
{
provide: 'RESILIENCE_POLICIES',
useValue: {
circuitBreakerPolicy,
bulkheadPolicy,
},
},
],
exports: ['RESILIENCE_POLICIES'],
})
export class ResilienceModule {}
Contexto: o código cria políticas e as exporta globalmente. Policy.handleAll() captura exceções para a política de circuit breaker. Ajuste os parâmetros: o primeiro valor em circuitBreaker é tempo de meia-abertura e o segundo, limiar de falhas consecutivas antes de abrir (valores ilustrativos).
3) ResilientHttpService: wrapper que aplica politicas
O wrapper centraliza a aplicação das políticas em métodos HTTP comuns (get/post/put/delete). Aqui usamos toPromise() para converter o Observable do HttpService para Promise.
import { Injectable, Inject } from '@nestjs/common';
import { HttpService } from '@nestjs/axios';
import { firstValueFrom } from 'rxjs';
@Injectable()
export class ResilientHttpService {
constructor(
private readonly http: HttpService,
@Inject('RESILIENCE_POLICIES') private readonly policies: any,
) {}
private wrapCall<T>(fn: () => Promise<T>): Promise<T> {
const { circuitBreakerPolicy, bulkheadPolicy } = this.policies;
const combined = Policy.wrap(bulkheadPolicy, circuitBreakerPolicy);
return combined.execute(() => fn());
}
async get<T>(url: string, config?: any): Promise<T> {
return this.wrapCall(() => firstValueFrom(this.http.get<T>(url, config)));
}
async post<T>(url: string, body: any, config?: any): Promise<T> {
return this.wrapCall(() => firstValueFrom(this.http.post<T>(url, body, config)));
}
}
O que acontece: antes de executar uma chamada HTTP, combinamos as politicas (bulkhead + circuit breaker). Se o bulkhead estiver cheio ou o circuito aberto, a Promise será rejeitada conforme implementado pela cockatiel.
4) Usando o ResilientHttpService no dominio
Exemplo de service que consome outro microservice usando o wrapper.
import { Injectable, HttpException } from '@nestjs/common';
import { ResilientHttpService } from './resilient-http.service';
@Injectable()
export class OrdersService {
constructor(private readonly http: ResilientHttpService) {}
async fetchOrderDetails(orderId: string) {
try {
const res = await this.http.get(`http://inventory.svc.local/items/${orderId}`);
return res.data ?? res; // depende do formato do retorno
} catch (err) {
// transformamos em excecao do Nest para filtros padrao
throw new HttpException('Serviço de inventory indisponível', 503);
}
}
}
Depois: o controller injeta OrdersService normalmente. Note o tratamento de excecao para mapear erros de resiliência para códigos HTTP adequados.
Tratamento de erros e edge cases
Erros comuns e armadilhas
Listagem dos pontos de atenção:
- Usar params de circuit breaker muito sensíveis (ex: limiar baixo) pode gerar circuitos abertos indevidos.
- Não combinar timeout com circuit breaker: se timeout efetivamente cancela a request, a política deve contar esse evento como falha.
- Bulkhead sem fila adequada pode retornar erro sob pico subitamente; decida entre fila curta ou rejeitar imediatamente.
- Conversão de Observable para Promise: firstValueFrom pode lançar se stream completa sem valor — adapte conforme a forma que HttpService retorna o corpo.
Tratamento de respostas quando politicas rejeitam
Ao detectar um circuito aberto ou bulkhead saturado, não reenvie indiscriminadamente. Decisões possíveis:
- Retornar resposta degradada (cache local, resposta estática).
- Retornar fallback mínimo informando indisponibilidade com 503/429.
- Implementar uma fila assíncrona (background retry) se a operação puder ser eventual.
Exemplo de catch detalhado no wrapper para mapear saturação de bulkhead para 429 (Too Many Requests):
import { HttpException } from '@nestjs/common';
// dentro do ResilientHttpService.wrapCall
return combined.execute(() => fn()).catch(err => {
// err.code ou err.message depende da implementacao da cockatiel
if (err.message && err.message.includes('Bulkhead')) {
throw new HttpException('Serviço sobrecarregado, tente novamente mais tarde', 429);
}
if (err.message && err.message.includes('Circuit is open')) {
throw new HttpException('Dependência com problema, operação bloqueada', 503);
}
throw err; // rethrow para handlers superiores
});
Contexto: adapte as mensagens de erro de acordo com a versão da cockatiel — a ideia é identificar a causa e traduzir para um código HTTP sem vazar detalhes internos.
Performance e otimizacao
Métricas e observabilidade
Medir é crítico. Colete métricas por política:
- Contagem de aberturas de circuito
- Taxa de rejeição do bulkhead
- Latência das chamadas protegidas
Exporte essas métricas para Prometheus ou outro observability stack. Por exemplo, incremente contadores quando a política dispara fallback. Assim você ajusta parâmetros com dados reais.
Otimizações práticas
Boas práticas que impactam performance e estabilidade:
- Dimensione bulkhead com base em capacidade real: threads/processo e latência média da operação.
- Use timeout na camada HTTP (Axios) para liberar conexões e evitar esperas infinitas.
- Combine circuit breaker com retry com backoff exponencial, mas limite o retry para não congestionar o sistema.
- Seed time windows com produção de dados reais; não assuma taxas de erro em dev.
Exemplo de configuração que adiciona timeout e retry (ilustrativo):
const timeoutPolicy = Policy.timeout(5000); // aborta se >5s
const retryPolicy = Policy.handleAll().retry().attempts(2).exponential(100, 1000);
const combined = Policy.wrap(bulkheadPolicy, circuitBreakerPolicy, retryPolicy, timeoutPolicy);
O que acontece: as políticas são aplicadas na ordem do wrap — bulkhead controla concorrência; circuit breaker evita chamadas a recursos com falha; retry tenta reexecutar quando aplicável; timeout garante que chamadas longas sejam abortadas.
Consideracoes finais e conclusao pratica
Checklist para implantar em produção
Antes de rodar em produção verifique:
- Parâmetros das políticas baseados em testes de carga e em latências reais.
- Métricas e alertas configurados para alterações de estado do circuit breaker.
- Fallbacks definidos (cache, mensagens ou respostas degradadas) que preservem a experiência mínima.
- Tratamento adequado das exceções no código de negócio (HTTP status map, logs, correlation ids).
Resumo prático
Separar a lógica de resiliência em um módulo e wrapper injetável simplifica a adoção das políticas por toda a base de código. Use Bulkhead para limitar concorrência, Circuit Breaker para evitar tentativas inúteis a dependências quebradas e combine com timeout e retry quando fizer sentido. Observe métricas e ajuste parâmetros com dados reais.
Implementar circuit breaker e bulkhead com Cockatiel em NestJS exige atenção a detalhes: escolha correta de limites, tratamento de erros e integração com observabilidade. Ao seguir um padrão centralizado (ResilienceModule + ResilientHttpService) você ganha consistência e mantém o código de domínio limpo.
Próximos passos práticos: instrumente um endpoint crítico com as políticas, gere tráfego sintético para observar comportamento em pico e refine limiares até equilibrar disponibilidade e proteção.
Assuntos: NestJS, microservices, cockatiel, circuit-breaker, bulkhead, resilience
Leia também
Interceptor global de logs e tempo no NestJS: implementar e testar
Implemente um interceptor global no NestJS para logs e medição de tempo, registre-o no bootstrap e teste com Jest e supertest. Artigo prático passo a passo…
Limitador de requisições no NestJS com Throttler + Redis: passo a passo para iniciantes
Guia prático para iniciantes: configure rate limiting no NestJS usando @nestjs/throttler e Redis. Passo a passo com exemplos funcionais, Docker, dicas de…
NestJS: criar e testar microservice TCP e gateway HTTP passo a passo
Passo a passo prático para criar e testar um microservice TCP em NestJS e um gateway HTTP. Mostro o jeito errado, o jeito certo, exemplos funcionais e como…
Propagando contexto de requisição e trace IDs entre microservices com NestJS
Guia prático para propagar trace IDs em NestJS: middleware, AsyncLocalStorage, interceptors e wrappers de ClientProxy. Padrões, armadilhas e exemplos reais…
Rate limiting por usuário no NestJS com Guard + Redis (sliding window)
Aprenda a implementar rate limiting por usuário no NestJS usando Guard + Redis com sliding window. Guia passo a passo, exemplos práticos (Lua + ioredis)…
Autorização por permissões no NestJS usando decorators e guards
Aprenda a controlar autorização por permissões no NestJS usando decorators e guards. Tutorial prático, passo a passo, com exemplos funcionais, integração JWT…