Diseño para la recuperación automática
Diseñe la aplicación para que se recupere automáticamente cuando esto suceda.
En un sistema distribuido, hay que esperar que se produzcan fallos. Pueden producirse errores de hardware. La red puede tener errores transitorios. En raras ocasiones, un servicio completo, un centro de datos o incluso una región de Azure podrían experimentar una interrupción. Sin embargo, incluso eso debe planificarse en la arquitectura de la carga de trabajo. La resistencia y la recuperación deben abordarse al principio del diseño de la carga de trabajo.
Por tanto, diseñe una aplicación que se recupere automáticamente cuando esto suceda. Esto requiere un enfoque basado en tres puntos:
- Detectar errores.
- Responder a los errores correctamente.
- Registrar y supervisar errores a fin de conseguir una perspectiva operativa.
La manera de responder a un determinado tipo de error depende de los requisitos de disponibilidad de su aplicación. Por ejemplo, si necesita alta disponibilidad, puede implementar en varias zonas de disponibilidad de una región. Para evitar interrupciones, incluso en el caso improbable de que toda una región de Azure experimente interrupciones, puede conmutar por error automáticamente a una región secundaria durante una interrupción regional. Sin embargo, eso generará un coste más alto y un rendimiento potencialmente más bajo que una implementación de una sola región.
Además, no piense solo en grandes eventos como interrupciones regionales, que suelen ser poco frecuentes. Debe centrarse igualmente, si no más, en la gestión de los errores locales y de corta duración, como los errores de conectividad de red o los errores en la conexión a una base de datos.
Recomendaciones
Uso de componentes desacoplados que se comunican de forma asincrónica. Lo ideal es que los componentes estén desacoplados en el tiempo y el espacio. Desacoplado en el tiempo significa que los componentes no necesitan estar presentes al mismo tiempo para que la comunicación sea posible. Desacoplado en el espacio significa que el emisor y el receptor no tienen por qué ejecutarse en el mismo proceso, pero pueden hacerlo siempre que sea más eficiente. Lo ideal es que los componentes desacoplados usaran eventos para comunicarse entre sí. Esto ayuda a minimizar la posibilidad de errores en cascada.
Vuelva a intentar las operaciones que den error. Los errores transitorios pueden deberse a una pérdida momentánea de conectividad de red, una conexión de base de datos caída o un tiempo de espera agotado cuando un servicio está ocupado. Cree la lógica de reintento en la aplicación para controlar los errores transitorios. En muchos de los servicios de Azure, el SDK de cliente implementa los reintentos automáticos. Para más información, consulte Control de errores transitorios y Patrón Retry.
Proteja los servicios remotos defectuosos (Circuit Breaker) . Es conveniente hacer un reintento después de un error transitorio, pero si el error persiste, puede acabar con demasiados llamadores agolpándose en un servicio con errores. Esto puede provocar errores en cascada, ya que se hace una copia de seguridad de las solicitudes. Use el patrón Circuit Breaker para fracasar y responder rápido a los errores (sin realizar la llamada remota) cuando sea probable que una operación genere un error.
Aísle recursos críticos (Bulkhead) . Los errores de un subsistema a veces pueden reproducirse en cascada. Esto puede ocurrir si un error provoca que algunos recursos, como subprocesos o sockets, no se liberen a tiempo, lo que conduce a un agotamiento de estos. Para evitarlo, utilice el patrón Bulkhead para realizar una partición de un sistema en grupos aislados, de modo que un error en una partición no destruya todo el sistema.
Realice una redistribución de la carga. Las aplicaciones pueden experimentar picos repentinos en el tráfico, lo que puede sobrecargar los servicios en el back-end. Para evitarlo, use el patrón Queue-Based Load Leveling para poner en cola los elementos de trabajo que se ejecutan de forma asincrónica. La cola actúa como un búfer que suaviza los picos de carga.
Realice una conmutación por error. Si no se puede tener acceso a una instancia, aplique una conmutación por error a otra instancia. En el caso de los elementos que no tienen estado, como un servidor web, coloque varias instancias detrás de un equilibrador de carga o el administrador de tráfico. En el caso de los elementos que almacenan el estado, como una base de datos, use réplicas y la conmutación por error. En función del almacén de datos y del modo en que se replique, la aplicación puede tener que lidiar con la coherencia final.
Compense las transacciones con error. En general, evite las transacciones distribuidas, ya que requieren coordinación a través de servicios y recursos. En su lugar, componga una operación a partir de transacciones individuales más pequeñas. Si la operación genera un error a mitad del proceso, use las transacciones de compensación para deshacer cualquier paso que ya haya completado.
Aplique puntos de control a las transacciones de ejecución prolongada. Los puntos de control pueden proporcionar resistencia si se produce un error en una operación de ejecución prolongada. Cuando la operación se reinicia (por ejemplo, si la recoge otra máquina virtual), es posible reanudar desde el último punto de control. Considere implementar un mecanismo que registre información sobre el estado de la tarea en intervalos regulares y que guarde este estado en almacenamiento duradero accesible para cualquier instancia del proceso que ejecute la tarea. De este modo, si el proceso se cierra, el trabajo que estaba realizando puede reanudarse desde el último punto de comprobación con otra instancia. Hay bibliotecas que proporcionan esta funcionalidad, como NServiceBus y MassTransit. Conservan de forma transparente el estado, donde los intervalos se alinean con el procesamiento de mensajes de colas en Azure Service Bus.
Degradación correcta y mantenimiento de la capacidad de respuesta durante errores. En ocasiones no es posible solucionar un problema, pero puede proporcionar una funcionalidad reducida que sigue siendo útil. Piense en una aplicación que muestre un catálogo de libros. Si la aplicación no puede recuperar la imagen en miniatura de la portada, puede que muestre una imagen de marcador de posición. Es posible que para la aplicación no sea esencial contar con subsistemas completos. Por ejemplo, en un sitio de comercio electrónico, mostrar las recomendaciones de productos es probablemente menos crítico que procesar los pedidos.
Limite los clientes. En ocasiones, un número reducido de usuarios crea una carga excesiva, lo que puede reducir la disponibilidad de la aplicación para otros usuarios. En esta situación, limite el cliente durante un período de tiempo determinado. Consulte el patrón Throttling.
Bloquee a los actores no válidos. El hecho de que limite a un cliente no significa que ese cliente estuviera actuando de manera malintencionada. Simplemente significa que el cliente ha superado su cuota de servicio. Pero si un cliente supera constantemente su cuota o se comporta de manera incorrecta, tiene la posibilidad de bloquearlo. Defina un proceso fuera de banda para que el usuario solicite ser desbloqueado.
Use Leader Election. Cuando necesite coordinar una tarea, use el patrón Leader Election para seleccionar un coordinador. De este modo, el coordinador no es un único punto de error. Si se produce un error en el coordinador, se selecciona uno nuevo. En lugar de implementar un algoritmo de elección de líder desde el principio, considere la posibilidad de una solución inmediata, como Zookeeper.
Realice pruebas con inserción de errores. Con demasiada frecuencia, la ruta de acceso correcta está bien probada, pero no así la ruta de acceso para errores. Un sistema podría ejecutarse en producción durante mucho tiempo antes de que se utilice una ruta de acceso para errores. Use la inserción de errores para probar la resistencia del sistema a los errores, ya sea mediante la activación de errores reales o mediante su simulación.
Adopte la ingeniería del caos. La ingeniería del caos amplía la noción de inserción de errores mediante la inserción aleatoria de errores o condiciones irregulares en instancias de producción.
Uso de zonas de disponibilidad. Muchas regiones de Azure proporcionan zonas de disponibilidad, que son conjuntos aislados de centros de datos dentro de la región. Algunos servicios de Azure se pueden implementar de forma zonal, lo que garantiza que se coloquen en una zona específica y puede ayudar a reducir la latencia en la comunicación entre componentes en la misma carga de trabajo. Como alternativa, algunos servicios se pueden implementar con redundancia de zona, lo que significa que Azure replica automáticamente el recurso entre zonas para lograr una alta disponibilidad. Tenga en cuenta qué enfoque proporciona el mejor conjunto de inconvenientes para la solución. Para más información sobre cómo diseñar la solución para usar regiones y zonas de disponibilidad, vea Recomendaciones para usar zonas de disponibilidad y regiones.
Para consultar un enfoque estructurado que haga que sus aplicaciones se recuperen de manera automática, consulte Diseño de aplicaciones confiables de Azure.