Patrón de disyuntores - AWS Guía prescriptiva

Las traducciones son generadas a través de traducción automática. En caso de conflicto entre la traducción y la version original de inglés, prevalecerá la version en inglés.

Patrón de disyuntores

Intención

El patrón de disyuntores puede evitar que el servicio del autor de las llamadas vuelva a intentar hacer una llamada a otro servicio (destinatario) cuando la llamada haya provocado anteriormente tiempos de espera repetidos o erróneos. El patrón también se utiliza para detectar cuándo vuelve a funcionar el servicio del destinatario de las llamadas.

Motivación

Cuando varios microservicios colaboran para gestionar las solicitudes, es posible que uno o varios servicios dejen de estar disponibles o presenten una latencia alta. Cuando las aplicaciones complejas utilizan microservicios, la interrupción de un microservicio puede provocar que se produzcan errores en la aplicación. Los microservicios se comunican a través de llamadas a procedimientos remotos y pueden producirse errores transitorios en la conectividad de la red y provocar fallas. (Los errores transitorios se pueden gestionar mediante el patrón de reintento con retroceso). Durante la ejecución sincrónica, la acumulación de tiempos de espera o errores puede provocar una mala experiencia de usuario.

Sin embargo, en algunas situaciones, los errores pueden tardar más en resolverse. Por ejemplo, cuando el servicio del destinatario de la llamada está inactivo o cuando una contención en la base de datos provoca tiempos de espera. En esos casos, si el servicio que hace la llamada vuelve a intentar hacer las llamadas varias veces, estos reintentos pueden provocar una contención en la red y un consumo del conjunto de subprocesos de la base de datos. Además, si varios usuarios vuelven a intentar utilizar la aplicación en repetidas ocasiones, esto empeorará el problema y puede provocar que disminuya el rendimiento de toda la aplicación.

Michael Nygard popularizó el patrón de los disyuntores en su libro Release It (Nygard 2018). Este patrón de diseño puede evitar que el servicio del autor de la llamada vuelva a intentar hacer una llamada al servicio que anteriormente había provocado tiempos de espera o errores repetidos. También puede detectar cuándo vuelve a funcionar el servicio del autor de la llamada.

Los disyuntores funcionan como disyuntores eléctricos que interrumpen de manera automática la corriente cuando hay una anomalía en el circuito. Los disyuntores eléctricos cortan o interrumpen el flujo de corriente cuando hay una falla. Del mismo modo, el disyuntor está situado entre el autor de la llamada y el servicio del destinatario de la llamada y se interrumpe si el destinatario de la llamada no está disponible.

Las falacias de la computación distribuida son un conjunto de afirmaciones de Peter Deutsch y otras personas de Sun Microsystems. Dicen que los programadores que se inician en el campo de las aplicaciones distribuidas hacen siempre suposiciones falsas. La fiabilidad de la red, las expectativas de latencia cero y las limitaciones de ancho de banda hacen que las aplicaciones de software se diseñen con una atención mínima a la gestión de los errores de red.

Durante una interrupción de la red, es posible que las aplicaciones esperen una respuesta de manera indefinida y consuman recursos de manera continua. Si no se vuelven a intentar las operaciones cuando la red está disponible, también se puede producir un deterioro de la aplicación. Si se agota el tiempo de espera de las llamadas de la API a una base de datos o a un servicio externo debido a problemas de red, las llamadas repetidas sin un disyuntor pueden afectar al costo y al rendimiento.

Aplicabilidad

Utilice este patrón cuando suceda lo siguiente:

  • El servicio de del autor de la llamada hace una llamada en la que es muy probable que se produzcan errores.

  • Si el servicio del destinatario de la llamada presenta una latencia alta (por ejemplo, cuando las conexiones a la base de datos son lentas), se agota el tiempo de espera del servicio del autor de la llamada.

  • El servicio del autor de la llamada hace una llamada sincrónica, pero el servicio del destinatario de la llamada no está disponible o presenta una latencia alta.

Problemas y consideraciones

  • Implementación independiente del servicio: para evitar la sobrecarga de código, le recomendamos implementar el objeto disyuntor de manera independiente de los microservicios y basada en la API.

  • Cierre del circuito por parte del destinatario de la llamada: cuando el destinatario de la llamada se recupera de un problema o error de rendimiento, puede actualizar el estado del circuito a CLOSED. Se trata de una extensión del patrón de los disyuntores y se puede implementar si es necesario para el objetivo de tiempo de recuperación (RTO).

  • Llamadas de varios subprocesos: el valor del tiempo de espera de vencimiento se define como el periodo que permanece desconectado el circuito antes de que se vuelvan a enrutar las llamadas para verificar la disponibilidad del servicio. Cuando se llama al servicio del destinatario de la llamada en varios subprocesos, la primera llamada erróneo define el valor del tiempo de espera de vencimiento. La implementación debe garantizar que las llamadas posteriores no prolonguen el tiempo de espera de vencimiento de manera indefinida.

  • Fuerce la apertura o el cierre del circuito: los administradores del sistema deben poder abrir o cerrar un circuito. Para ello, se puede actualizar el valor del tiempo de espera de vencimiento en la tabla de la base de datos.

  • Observabilidad: la aplicación debe tener un registro configurado para identificar las llamadas que presentan errores cuando está abierto el disyuntor.

Implementación

Arquitectura de alto nivel

En el ejemplo siguiente, el autor de la llamada es el servicio de pedidos y el destinatario de la llamada es el servicio de pago.

Cuando no se produce ningún error, el servicio de pedidos dirige todas las llamadas al servicio de pago mediante el disyuntor, tal y como se muestra en el diagrama siguiente.

Patrón de disyuntores sin errores.

Si se agota el tiempo de espera del servicio de pago, el disyuntor puede detectar el tiempo de espera y hacer un seguimiento del error.

Disyuntor con error en el servicio de pago.

Si los tiempos de espera superan un umbral especificado, la aplicación abre el circuito. Cuando el circuito está abierto, el objeto del disyuntor no enruta las llamadas al servicio de pago. Se produce un error inmediato cuando el servicio de pedidos llama al servicio de pago.

El disyuntor detiene el enrutamiento al servicio de pago.

El objeto del disyuntor intenta verificar de manera periódica si las llamadas al servicio de pago se hicieron de manera correcta.

El disyuntor reintenta de manera periódica el servicio de pago.

Cuando la llamada al servicio de pago se hace de manera correcta correctamente, el circuito se cierra y todas las demás llamadas se vuelven a dirigir al servicio de pago.

Disyuntor con error servicio de pago en funcionamiento.

Implementación mediante los servicios de AWS

La solución de muestra utiliza flujos de trabajo rápidos en AWS Step Functions para implementar el patrón de disyuntores. La máquina de estados de Step Functions permite configurar las funcionalidades de reintento y el flujo de control basados en decisiones que son necesarios para la implementación del patrón.

La solución también utiliza una tabla de Amazon DynamoDB como almacén de datos para hacer un seguimiento del estado del circuito. Esto se puede sustituir por un almacén de datos en memoria, como Amazon ElastiCache (Redis OSS), para mejorar el rendimiento.

Cuando un servicio quiere llamar a otro servicio, inicia el flujo de trabajo con el nombre del servicio del destinatario de la llamada. El flujo de trabajo obtiene el estado del disyuntor de la tabla CircuitStatus de DynamoDB, que almacena los servicios actualmente degradados. Si CircuitStatus contiene un registro vigente del destinatario de la llamada, el circuito está abierto. El flujo de trabajo de Step Functions devuelve un error inmediato y se cierra con un estado FAIL.

Si la tabla CircuitStatus no contiene un registro del destinatario de la llamada o contiene un registro vencido, el servicio está operativo. El paso ExecuteLambda de la definición de la máquina de estados llama a la función de Lambda que se envía a través de un valor de parámetro. Si la llamada es correcta, el flujo de trabajo de Step Functions finaliza con un estado SUCCESS.

Implementación de disyuntores con AWS Step Functions y DynamoDB.

Si se produce un error en la llamada del servicio o se agota el tiempo de espera, la aplicación vuelve a intentarlo con un retroceso exponencial durante un número definido de veces. Si se produce un error en la llamada del servicio después de los reintentos, el flujo de trabajo inserta un registro en la tabla CircuitStatus para el servicio con un valor ExpiryTimeStamp y el flujo de trabajo sale con un estado FAIL. Las llamadas posteriores al mismo servicio producen un error inmediato mientras el disyuntor esté abierto. El paso Get Circuit Status de la definición de la máquina de estados verifica la disponibilidad del servicio según el valor ExpiryTimeStamp. Los elementos vencidos se eliminan de la tabla CircuitStatus mediante la característica periodo de vida (TTL) de DynamoDB.

Código de muestra

El código siguiente utiliza la función de Lambda GetCircuitStatus para verifica el estado del disyuntor.

var serviceDetails = _dbContext.QueryAsync<CircuitBreaker>(serviceName, QueryOperator.GreaterThan, new List<object> {currentTimeStamp}).GetRemainingAsync(); if (serviceDetails.Result.Count > 0) { functionData.CircuitStatus = serviceDetails.Result[0].CircuitStatus; } else { functionData.CircuitStatus = ""; }

En el siguiente código se muestran las declaraciones de Amazon States Language en el flujo de trabajo de Step Functions.

"Is Circuit Closed": { "Type": "Choice", "Choices": [ { "Variable": "$.CircuitStatus", "StringEquals": "OPEN", "Next": "Circuit Open" }, { "Variable": "$.CircuitStatus", "StringEquals": "", "Next": "Execute Lambda" } ] }, "Circuit Open": { "Type": "Fail" }

Repositorio GitHub

Para obtener una implementación completa de la arquitectura de ejemplo para este patrón, consulte el repositorio de GitHub en https://github.com/aws-samples/circuit-breaker-netcore-blog.

Referencias de blogs

Contenido relacionado