"Si un trabajador quiere hacer bien su trabajo, primero debe afilar sus herramientas." - Confucio, "Las Analectas de Confucio. Lu Linggong"
Página delantera > Programación > Disyuntor en aplicaciones Go

Disyuntor en aplicaciones Go

Publicado el 2024-09-12
Navegar:727

Hoy en día, es bastante común que nuestra aplicación dependa de otras, especialmente si estamos trabajando en un entorno de microservicios. Es bastante común que nuestra aplicación comience a informar errores y, al investigar, notamos que alguna API de un equipo asociado o proveedor no funciona.

Una buena práctica para aumentar la resiliencia de nuestra aplicación es cortar la comunicación con aquellas aplicaciones que se encuentran en estado obsoleto. Observando otras áreas, absorbimos el concepto de Disyuntor de la Ingeniería Eléctrica. En él se coloca un equipo, o disyuntor, que se apaga automáticamente si ocurre una falla. Esto es muy común en nuestros hogares, que cuentan con disyuntores que se apagan solos si la red eléctrica comienza a volverse inestable.

En informática, nuestro Disyuntor es un poco más complejo, ya que también definimos un estado intermedio. El siguiente dibujo explica mejor cómo funciona un disyuntor:

Circuit Breaker em aplicações Go

Finalmente, los estados son:

  • abierto: no hay comunicación entre aplicaciones. Al llegar a este estado, se inicia un temporizador para dar tiempo al servicio de reinicio. Al final del cronómetro, pasamos a medio abierto.
  • cerrado: hay comunicación entre aplicaciones. Por cada solicitud fallida, se actualiza un contador. Si se alcanza el límite de falla, hacemos la transición del circuito a abierto.
  • semiabierto: estado de recuperación hasta que la comunicación pueda fluir por completo. En él, se actualiza un contador de éxito con cada solicitud. Si se alcanza el número ideal de éxitos, pasamos el circuito a cerrado. Si las solicitudes fallan, volvemos a abrir.

Muy bien, ¿verdad? Pero para ejemplificar mejor el concepto, ¿qué tal si lo hacemos en la práctica?

Primero construyamos nuestro servicio A. Este será el responsable de recibir las solicitudes, es decir, será el servicio del que depende nuestra aplicación, el servicio del proveedor, etc. Para hacerlo más fácil, expondremos dos puntos finales, un /success que siempre devolverá 200 y un /failure que siempre devolverá 500.

package main

import (
    "fmt"
    "log"
    "net/http"
)

func main() {
    http.HandleFunc("/success", func(w http.ResponseWriter, r *http.Request) { 
    w.WriteHeader(http.StatusOK) })
    http.HandleFunc("/failure", func(w http.ResponseWriter, r *http.Request) { 
    w.WriteHeader(http.StatusInternalServerError) })

    fmt.Println("Server is running at http://localhost:8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

El servicio B será responsable de llamar al servicio A. Construirá nuestro disyuntor. ¡Por suerte para nosotros, la comunidad Go ya tiene la biblioteca gobreaker que implementa el patrón! Primero, definimos las propiedades de nuestro interruptor:

var st gobreaker.Settings
st.Name = "Circuit Breaker PoC"
st.Timeout = time.Second * 5
st.MaxRequests = 2
st.ReadyToTrip = func(counts gobreaker.Counts) bool {
    return counts.ConsecutiveFailures >= 1
}

Aunque la biblioteca nos permite personalizar más cosas, nos centraremos en tres:

  • Timeout: el tiempo que el circuito permanecerá en estado abierto. En nuestro caso, el tiempo se estableció en 5 segundos.
  • MaxRequests: número de solicitudes exitosas antes de cerrarse. En nuestro ejemplo, lo definimos como 2 solicitudes.
  • ReadyToTrip: define la condición para la transición de cerrado a abierto. Para facilitar las cosas, digamos que un fracaso es suficiente.

Luego podemos inicializar el disyuntor y realizar solicitudes:

cb := gobreaker.NewCircuitBreaker[int](st)

url := "http://localhost:8080/success"
cb.Execute(func() (int, error) { return Get(url) })
fmt.Println("Circuit Breaker state:", cb.State()) // closed!

url = "http://localhost:8080/failure"
cb.Execute(func() (int, error) { return Get(url) })
fmt.Println("Circuit Breaker state:", cb.State()) // open!

time.Sleep(time.Second * 6)
url = "http://localhost:8080/success"
cb.Execute(func() (int, error) { return Get(url) })
fmt.Println("Circuit Breaker state:", cb.State()) // half-open!

url = "http://localhost:8080/success"
cb.Execute(func() (int, error) { return Get(url) })
fmt.Println("Circuit Breaker state:", cb.State()) // closed!

Podemos notar que gobreaker funciona como un contenedor alrededor de una función. Si la función devuelve un error, aumenta la cantidad de errores, si no, aumenta la cantidad de éxitos. Entonces definamos esta función:

func Get(url string) (int, error) {
    r, _ := http.Get(url)

    if r.StatusCode != http.StatusOK {
        return r.StatusCode, fmt.Errorf("failed to get %s", url)
    }

    return r.StatusCode, nil
}

¡Y tenemos nuestro servicio Go usando un disyuntor! Al utilizar este patrón, puede aumentar la resiliencia y la tolerancia a fallos de sus servicios. Podemos notar que al usar la biblioteca, la complejidad se abstrajo por completo, lo que hace que el proceso de integración de esto en nuestra vida diaria sea muy simple. Si desea ver el código completo de prueba de concepto, simplemente vaya aquí.

Si tienes curiosidad por conocer otros patrones de resiliencia, ¡Elton Minetto publicó una excelente publicación sobre el tema!

Dime qué piensas de esta publicación en los comentarios y aquí tienes una pregunta: ¿alguna vez has usado disyuntores antes? ¡Oh, también puedes encontrarme en mi blog personal!

Declaración de liberación Este artículo se reproduce en: https://dev.to/mfbmina/circuit-breaker-em-aplicacoes-go-4hnn?1 Si hay alguna infracción, comuníquese con [email protected] para eliminarla.
Último tutorial Más>

Descargo de responsabilidad: Todos los recursos proporcionados provienen en parte de Internet. Si existe alguna infracción de sus derechos de autor u otros derechos e intereses, explique los motivos detallados y proporcione pruebas de los derechos de autor o derechos e intereses y luego envíelos al correo electrónico: [email protected]. Lo manejaremos por usted lo antes posible.

Copyright© 2022 湘ICP备2022001581号-3