Nota:
El acceso a esta página requiere autorización. Puede intentar iniciar sesión o cambiar directorios.
El acceso a esta página requiere autorización. Puede intentar cambiar los directorios.
Sugerencia
Este contenido es un extracto del libro electrónico, Arquitectura de aplicaciones .NET nativas de nube para Azure, disponible en .NET Docs o como un PDF descargable gratuito que se puede leer sin conexión.
La primera línea de defensa es la resistencia de la aplicación.
Aunque podría invertir mucho tiempo escribiendo su propio marco de resistencia, estos productos ya existen.
Polly es una biblioteca completa de resistencia de .NET y control de errores transitorios que permite a los desarrolladores expresar directivas de resistencia de forma fluida y segura para subprocesos. Polly tiene como destino las aplicaciones compiladas con .NET Framework o .NET 7. En la tabla siguiente se describen las características de resistencia, denominadas policies, disponibles en la biblioteca polly. Se pueden aplicar individualmente o agrupar.
| Política | Experiencia |
|---|---|
| Volver a intentar | Configura las operaciones de reintento en las operaciones designadas. |
| Disyuntor | Bloquea las operaciones solicitadas durante un período predefinido cuando los errores superan un umbral configurado |
| Tiempo de espera | Coloca el límite durante el cual un autor de la llamada puede esperar una respuesta. |
| Compartimentado | Restringe las acciones a un grupo de recursos de tamaño fijo para evitar que las llamadas con errores saturen un recurso. |
| Caché | Almacena las respuestas automáticamente. |
| Alternativa | Define el comportamiento estructurado tras un error. |
Observe cómo, en la ilustración anterior, las directivas de resistencia se aplican a los mensajes de solicitud, tanto si proceden de un cliente externo como de un servicio back-end. El objetivo es compensar la solicitud de un servicio que podría ser momentáneamente no disponible. Estas interrupciones de corta duración suelen manifestarse con los códigos de estado HTTP que se muestran en la tabla siguiente.
| Código de estado HTTP | Causa |
|---|---|
| 404 | No encontrado |
| 408 | Tiempo de espera de solicitud |
| 429 | Demasiadas solicitudes (lo más probable es que se haya limitado) |
| 502 | Puerta de enlace incorrecta |
| 503 | Servicio no disponible |
| 504 | Tiempo de espera de la puerta de enlace |
Pregunta: ¿Reintentaría un código de estado HTTP de 403 - Prohibido? No. Aquí, el sistema funciona correctamente, pero informa al autor de la llamada de que no están autorizados para realizar la operación solicitada. Se debe tener cuidado para reintentar solo las operaciones causadas por errores.
Como se recomienda en el capítulo 1, los desarrolladores de Microsoft que construyen aplicaciones nativas de nube deben tener como destino la plataforma .NET. La versión 2.1 introdujo la biblioteca HTTPClientFactory para crear instancias de cliente HTTP para interactuar con recursos basados en direcciones URL. Reemplazando la clase HTTPClient original, la clase de fábrica admite muchas características mejoradas, una de las cuales es la integración estrecha con la biblioteca de resiliencia Polly. Con él, puede definir fácilmente directivas de resistencia en la clase de inicio de la aplicación para controlar errores parciales y problemas de conectividad.
A continuación, vamos a expandir los patrones de reintentos y disyuntores.
Patrón Retry
En un entorno nativo de nube distribuido, las llamadas a servicios y recursos en la nube pueden producir errores debido a errores transitorios (de corta duración), que normalmente se corrigen después de un breve período de tiempo. La implementación de una estrategia de reintento ayuda a un servicio nativo en la nube a mitigar estos escenarios.
El patrón Retry permite a un servicio reintentar una operación de solicitud con error un número de veces (configurable) con un tiempo de espera exponencialmente creciente. En la figura 6-2 se muestra un reintento en acción.
Figura 6-2. Patrón de reintento en acción
En la ilustración anterior, se ha implementado un patrón de reintento para una operación de solicitud. Está configurado para permitir hasta cuatro reintentos antes de que se produzca un error con un intervalo de retroceso (tiempo de espera) a partir de dos segundos, que se duplica exponencialmente para cada intento posterior.
- Se produce un error en la primera invocación y se devuelve un código de estado HTTP de 500. La aplicación espera dos segundos y vuelve a intentar la llamada.
- La segunda invocación también produce un error y devuelve un código de estado HTTP de 500. La aplicación ahora duplica el intervalo de retroceso a cuatro segundos y vuelve a intentar la llamada.
- Por último, la tercera llamada tiene éxito.
- En este escenario, la operación de reintento habría intentado hasta cuatro reintentos al duplicar la duración del retroceso antes de producir un error en la llamada.
- Si se produjo un error en el 4º intento de reintento, se invocaría una directiva de reserva para controlar correctamente el problema.
Es importante aumentar el período de retroceso antes de reintentar la llamada para permitir que el tiempo de servicio sea autocorrección. Es un procedimiento recomendado implementar un retroceso exponencialmente creciente (duplicando el período en cada reintento) para permitir un tiempo de corrección adecuado.
Patrón de disyuntor
Aunque el patrón de reintento puede ayudar a salvar una solicitud entrelazada en un error parcial, hay situaciones en las que los errores pueden deberse a eventos imprevistos que requerirán períodos de tiempo más largos para resolverse. La gravedad de estos errores puede abarcar desde una pérdida parcial de la conectividad hasta la total detención de un servicio. En estas situaciones, es inútil que una aplicación vuelva a intentar continuamente una operación que es poco probable que se realice correctamente.
Para empeorar las cosas, la ejecución de operaciones continuas de reintento en un servicio no con capacidad de respuesta puede moverle a un escenario de denegación de servicio autoimpuesto en el que inunda el servicio con llamadas continuas que agotan recursos, como memoria, subprocesos y conexiones de base de datos, lo que provoca errores en partes no relacionadas del sistema que usan los mismos recursos.
En estas situaciones, sería preferible que la operación falle inmediatamente y solo intente invocar el servicio si es probable que tenga éxito.
El patrón Circuit Breaker puede impedir que una aplicación intente ejecutar repetidamente una operación que probablemente produzca un error. Después de un número predefinido de llamadas erróneas, bloquea todo el tráfico al servicio. Periódicamente, se realizará una llamada de prueba para determinar si la falla se ha resuelto. En la figura 6-3 se muestra el patrón Circuit Breaker en acción.
Figura 6-3. Patrón de disyuntor en acción
En la ilustración anterior, se ha agregado un patrón de disyuntor al patrón de reintento original. Observe cómo después de 100 solicitudes con error, los disyuntores se abren y ya no permiten llamadas al servicio. El valor CheckCircuit, establecido en 30 segundos, especifica la frecuencia con la que la biblioteca permite que una solicitud continúe con el servicio. Si esa llamada se realiza correctamente, el circuito se cierra y el servicio vuelve a estar disponible para el tráfico.
Tenga en cuenta que la intención del patrón Circuit Breaker es diferente de la del patrón Retry. El patrón Retry permite a una aplicación reintentar una operación con la expectativa de que se realizará correctamente. El patrón Circuit Breaker impide que una aplicación realice una operación que probablemente produzca un error. Normalmente, una aplicación combinará estos dos patrones mediante el patrón Retry para invocar una operación a través de un disyuntor.
Pruebas de resistencia
Las pruebas de resistencia no siempre se pueden realizar de la misma manera que se prueba la funcionalidad de la aplicación (mediante la ejecución de pruebas unitarias, pruebas de integración, etc.). En su lugar, debe probar cómo se comporta la carga de trabajo de extremo a extremo en condiciones de fallo, que solo se producen de forma intermitente. Por ejemplo: insertar errores bloqueando procesos, certificados expirados, hacer que los servicios dependientes no estén disponibles, etc. Los marcos como chaos-monkey se pueden usar para estas pruebas de caos.
La resistencia de la aplicación es un elemento necesario para controlar las operaciones solicitadas problemáticas. Pero es sólo la mitad de la historia. A continuación, tratamos las características de resistencia disponibles en la nube de Azure.