Executando verificação de segurança...
2

Elevando a Resiliência: Implementando o Padrão Circuit Breaker com Cache Redis em Node.js

Quando se trata de criar sistemas robustos e confiáveis, a implementação do padrão Circuit Breaker é essencial. Este padrão ajuda a lidar com falhas em serviços externos, evitando chamadas repetidas quando o serviço está com problemas. Além disso, podemos melhorar o desempenho do nosso sistema ao adicionar um cache para armazenar respostas de serviços externos. Neste artigo, vamos explorar como implementar o padrão Circuit Breaker com cache Redis em Node.js.

Circuit Breaker

O padrão Circuit Breaker implementa cinco estados distintos para lidar com chamadas a serviços externos de forma mais resiliente. O estado "Close" é o estado inicial, onde as chamadas são permitidas a passar pelo Circuit Breaker normalmente. Quando um número significativo de chamadas começa a falhar, o Circuit Breaker entra no estado "Open", bloqueando todas as chamadas subsequentes e acionando um período de espera, evitando assim que a carga adicional seja direcionada ao serviço externo problemático. Depois de um tempo, ele entra no estado "Half-Open", permitindo que algumas chamadas sejam testadas novamente. Se essas chamadas forem bem-sucedidas, o Circuit Breaker retorna ao estado "Close", caso contrário, permanece em "Open". Os estados "Success" e "Fallback" representam eventos após uma chamada. "Success" ocorre quando uma chamada é bem-sucedida, enquanto "Fallback" ocorre quando uma chamada é substituída por um valor de fallback predefinido, ajudando a manter a funcionalidade quando o serviço externo está inacessível. Esses estados combinados oferecem uma estratégia poderosa para melhorar a resiliência de um sistema.

Entendendo o Código de Exemplo

Antes de prosseguir vamos dar uma olhada rápida no código fornecido.

O código usa o Node.js e a biblioteca Express para criar um servidor web. Ele utiliza o pacote axios para fazer chamadas a um serviço externo e o cache é configurado para armazenar respostas do serviço externo, com um tempo de vida (TTL) de um minuto.

O Circuit Breaker é implementado usando o pacote opossum. Este Circuit Breaker tem um tempo limite de 3 segundos, aciona o fallback se mais de 50% das chamadas falharem e espera 10 segundos antes de tentar reabrir o circuito.

import axios from "axios"
import express from "express"
import CircuitBreaker from "opossum"
import { createClient } from "redis"

const app = express()
const redisClient = createClient()
const redisTTL = 1 * 60 //one minute
const cacheKey = "api-response"

redisClient.on("error", (err) => console.log("Redis Client Error", err))
redisClient.connect()

const circuitBreakerOptions = {
  timeout: 3000, // Timeout to call fallback Tempo limite para uma solicitação antes de acionar o fallback
  errorThresholdPercentage: 50, // Porcentagem de erros antes de acionar o fallback
  resetTimeout: 10000 // Tempo de espera antes de tentar reabrir o circuito
}

async function asyncHttpCall() {
  const cacheResponse = await redisClient.get(cacheKey)

  if (cacheResponse) return JSON.parse(cacheResponse)

  const response = await axios.get("http://localhost:3001")

  await redisClient.set(cacheKey, JSON.stringify(response.data), {
    EX: redisTTL, //expire time, in seconds.
    NX: true //only set the key if it does not already exist
  })

  return response.data
}

const circuitBreaker = new CircuitBreaker(asyncHttpCall, circuitBreakerOptions)

circuitBreaker
  .on("close", () => console.log("CLOSE"))
  .on("open", () => console.log("OPEN"))
  .on("halfOpen", () => console.log("HALF"))
  .on("success", () => console.log("SUCCESS"))
  .on("fallback", () => console.log("FALLBACK"))
  .fallback(() => {
    return { id: 1, title: "iphone", price: "2.000" } //default object to return on fallback, here we can adopt any strategy
  })

// External endpoint to call
app.get("/", async (req, res) => {
  const response = await circuitBreaker.fire()

  res.send(response)
})

app.listen(8080, () => {
  console.log("Server running at door 8080!")
})

Cache

O objetivo de implementar o cache é armazenar as respostas do serviço no Redis e recuperá-las, evitando chamadas repetidas.
Primeiro verificamos se há uma resposta no cache Redis. Se a resposta estiver disponível, ela será retornada diretamente do cache, economizando chamadas ao serviço externo. Se a resposta não estiver no cache, fazemos a chamada ao serviço externo como antes, mas agora também armazenamos a resposta no Redis com um tempo de vida definido pelo “redisTTL”. Isso garante que as próximas chamadas possam aproveitar o cache.

Conclusão

A implementação do padrão Circuit Breaker com cache Redis pode melhorar significativamente a robustez e o desempenho de seus aplicativos. Evita chamadas repetidas a serviços externos quando estão com problemas e utiliza um cache para acelerar as respostas.
Lembre-se de que, em um ambiente de produção real, você precisará ajustar as configurações de acordo com suas necessidades e adicionar tratamento de erros mais abrangente. No entanto, esta implementação serve como um ponto de partida sólido para criar sistemas mais resilientes e eficientes em Node.js.

Repositório com o código completo github

Carregando publicação patrocinada...