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.
Al diseñar una aplicación, los requisitos de aplicación funcionales y no funcionales son críticos. En este área de diseño se describen los patrones de arquitectura y las estrategias de escalado que pueden ayudar a que la aplicación sea resistente a errores.
Importante
Este artículo forma parte de la serie de cargas de trabajo de misión crítica del Azure Well-Architected Framework. Si no está familiarizado con esta serie, le recomendamos que comience con ¿Qué es una carga de trabajo crítica para la misión?.
Arquitectura de unidad de escalado
Todos los aspectos funcionales de una solución deben ser capaces de escalar para satisfacer los cambios en la demanda, idealmente mediante escalado automático en respuesta a la carga. Se recomienda usar una arquitectura de unidad de escalado para optimizar la escalabilidad de un extremo a otro a través de la compartimentación y también para estandarizar el proceso de agregar y quitar capacidad. Una unidad de escalado es una unidad lógica o una función independiente que puede escalarse de forma autónoma. Una unidad se puede componer de componentes de código, plataformas de hospedaje de aplicaciones, los sellos de implementación que cubren los componentes relacionados e incluso suscripciones para admitir requisitos de múltiples inquilinos.
Se recomienda este enfoque porque aborda los límites de escala de los recursos individuales y toda la aplicación. Ayuda con escenarios complejos de implementación y actualización, ya que una unidad de escalado se puede implementar como una unidad. Además, puede probar y validar versiones específicas de componentes en un entorno antes de dirigir el tráfico del usuario a ellos.
Supongamos que la aplicación crítica es un catálogo de productos en línea. Tiene un flujo de usuario para procesar comentarios y clasificaciones del producto. El flujo usa API para recuperar y publicar comentarios y clasificaciones, así como componentes auxiliares como un punto de conexión de OAuth, un almacén de datos y colas de mensajes. Los puntos de conexión de la API sin estado representan unidades funcionales granulares que deben adaptarse a los cambios según sea necesario. La plataforma de aplicaciones subyacente también debe ser capaz de escalar en consecuencia. Para evitar cuellos de botella de rendimiento, los componentes y dependencias posteriores también deben escalar adecuadamente. Pueden escalar de forma independiente, como unidades de escalado independientes o juntas, como parte de una sola unidad lógica.
Unidades de escalado de ejemplo
La imagen a continuación muestra los posibles ámbito de las unidades de escalado. Los ámbitos van desde pods de microservicios hasta nodos de clúster y marcas de implementación regionales.
Diagrama que muestra varios ámbitos para las unidades de escalado.
Consideraciones de diseño
Ámbito. El ámbito de una unidad de escalado, la relación entre las unidades de escalado y sus componentes deben definirse según un modelo de capacidad. Tenga en cuenta los requisitos no funcionales para el rendimiento.
Límites de escala. Es posible que los límites y cuotas de escalado de suscripciones de Azure tengan un efecto en el diseño de aplicaciones, las opciones tecnológicas y la definición de unidades de escalado. Las unidades de escalado pueden ayudarle a eludir los límites de escala de un servicio. Por ejemplo, si un clúster de AKS en una unidad solo puede tener 1000 nodos, puede usar dos unidades para aumentar ese límite a 2000 nodos.
Carga esperada . Use el número de solicitudes para cada flujo de usuario, la tasa de solicitudes máximas esperada (solicitudes por segundo) y patrones de tráfico diarios, semanales o estacionales para informar a los requisitos de escalado principal. Tenga en cuenta también los patrones de crecimiento esperados para el tráfico y el volumen de datos. Rendimiento degradado aceptable. Determine si un servicio degradado con tiempos de respuesta elevados es aceptable bajo carga. Al modelar la capacidad necesaria, el rendimiento necesario de la solución bajo carga es un factor crítico.
Requisitos no funcionales. Los escenarios técnicos y empresariales tienen distintas consideraciones sobre resistencia, disponibilidad, latencia, capacidad y observabilidad. Analice estos requisitos en el contexto de los flujos de usuario de un extremo a otro clave. Tendrás flexibilidad relativa en el diseño, la toma de decisiones y las opciones tecnológicas a nivel de flujo de usuario.
Recomendaciones de diseño
Defina el alcance de una unidad de escalado y los límites que activarán el escalado de la unidad.
Asegúrese de que todos los componentes de la aplicación se pueden escalar de forma independiente o como parte de una unidad de escalado que incluya otros componentes relacionados.
Defina la relación entre las unidades de escalado, en función de un modelo de capacidad y los requisitos no funcionales.
Defina una marca de implementación regional para unificar el aprovisionamiento, la administración y el funcionamiento de los recursos de aplicación regionales en una unidad de escalado heterogéneo pero interdependente. A medida que aumenta la carga, se pueden implementar sellos adicionales, dentro de la misma región de Azure o en otras diferentes, para escalar horizontalmente la solución.
Use una suscripción de Azure como unidad de escalado para que los límites de escalado dentro de una sola suscripción no restrinjan la escalabilidad. Este enfoque se aplica a escenarios de aplicaciones a gran escala que tienen un volumen de tráfico significativo.
Modele la capacidad necesaria en torno a los patrones de tráfico identificados para asegurarse de que la capacidad suficiente se aprovisiona en horas punta para evitar la degradación del servicio. Como alternativa, optimice la capacidad durante las horas de poca actividad.
Mida el tiempo necesario para realizar operaciones de escalabilidad horizontal y vertical para garantizar que las variaciones naturales del tráfico no crean un nivel inaceptable de degradación del servicio. Realice un seguimiento de las duraciones de las operaciones de escalabilidad como métrica operativa.
Nota:
Cuando realice la implementación en una zona de aterrizaje de Azure, asegúrese de que la suscripción a la zona de aterrizaje está dedicada a la aplicación para proporcionar un límite de administración claro y evitar el antipatrón Noisy Neighbor.
Distribución global
Es imposible evitar errores en cualquier entorno altamente distribuido. En esta sección se proporcionan estrategias para mitigar muchos escenarios de error. La aplicación debe ser capaz de soportar errores regionales y zonales. Debe implementarse en un modelo activo o activo para que la carga se distribuya entre todas las regiones.
Vea este vídeo para obtener información general sobre cómo planear errores en aplicaciones críticas y maximizar la resistencia:
Consideraciones de diseño
Redundancia. La aplicación debe implementarse en varias regiones. Además, dentro de una región, recomendamos encarecidamente que utilice zonas de disponibilidad para permitir la tolerancia a fallos a nivel del centro de datos. Las zonas de disponibilidad tienen un perímetro de latencia inferior a 2 milisegundos entre zonas de disponibilidad. En el caso de las cargas de trabajo que son muy interactivas entre zonas, esta latencia puede introducir una penalización en el rendimiento para la transferencia de datos entre zonas.
Modelo activo/activo. Se recomienda una estrategia de implementación activa o activa porque maximiza la disponibilidad y proporciona un acuerdo de nivel de servicio (SLA) compuesto más alto. Sin embargo, puede presentar desafíos relacionados con la sincronización de datos y la coherencia para muchos escenarios de aplicación. Aborde los retos a nivel de plataforma de datos al tiempo que considera las compensaciones de un mayor coste y esfuerzo de ingeniería.
Una implementación activa o activa en varios proveedores de nube es una manera de mitigar potencialmente la dependencia de los recursos globales dentro de un único proveedor de nube. Sin embargo, una estrategia de implementación activo/activo multicloud introduce una cantidad significativa de complejidad en torno a CI/CD. Además, dadas las diferencias en las especificaciones y funcionalidades de los recursos entre los proveedores de nube, necesitaría sellos de implementación especializados para cada nube.
Distribución geográfica. La carga de trabajo puede tener requisitos de cumplimiento para la residencia de datos geográficas, la protección de datos y la retención de datos. Considere si hay regiones específicas en las que los datos deben residir o dónde deben implementarse los recursos.
Origen de la solicitud. La proximidad geográfica y la densidad de los usuarios o sistemas dependientes deben informar sobre las decisiones de diseño sobre la distribución global.
Conectividad. La forma en que los usuarios o sistemas externos acceden a la carga de trabajo influirán en el diseño. Tenga en cuenta si la aplicación está disponible a través de la red pública de Internet o las redes privadas que usan circuitos VPN o Azure ExpressRoute.
Para obtener recomendaciones de diseño y opciones de configuración a nivel de plataforma, consulte Plataforma de aplicación: Distribución global.
Arquitectura dirigida por eventos con acoplamiento suelto
El acoplamiento permite la comunicación entre servicios a través de interfaces bien definidas. Un acoplamiento débil permite que un componente de aplicación funcione de forma independiente. Un estilo de arquitectura de microservicios es consistente con los requisitos críticos de misión. Facilita la alta disponibilidad evitando errores en cascada.
Para un acoplamiento suelto, se recomienda encarecidamente incorporar diseño dirigido por eventos. El procesamiento asincrónico de mensajes a través de un intermediario puede crear resistencia.
Diagrama que muestra la comunicación asincrónica controlada por eventos.
En algunos escenarios, las aplicaciones pueden combinar acoplamiento flexible y ajustado, en función de los objetivos empresariales.
Consideraciones de diseño
Dependencias en tiempo de ejecución. Los servicios acoplados de forma flexible no deben restringirse para usar la misma plataforma de proceso, lenguaje de programación, tiempo de ejecución o sistema operativo.
Escalado. Los servicios deben ser capaces de escalar de forma independiente. Optimice el uso de recursos de infraestructura y plataforma.
Tolerancia a errores. Los errores se deben controlar por separado y no deben afectar a las transacciones de cliente.
Integridad transaccional. Considere el efecto de la creación y persistencia de datos que se produce en servicios independientes.
Seguimiento distribuido. El seguimiento integral puede que requiera una orquestación más compleja.
Recomendaciones de diseño
Alinee los límites de microservicios con flujos de usuario críticos.
Utiliza la comunicación asincrónica basada en eventos siempre que sea posible para mantener una escala sostenible y un rendimiento óptimo.
Utilice patrones como Outbox y Transactional Session para garantizar la coherencia y que cada mensaje se procese correctamente.
Ejemplo: Enfoque controlado por eventos
La implementación de referencia de Mission-Critical Online utiliza microservicios para procesar una transacción de negocio individual. Aplica operaciones de escritura de forma asíncrona con un broker de mensajes y un worker. Las operaciones de lectura son sincrónicas, con el resultado devuelto directamente al autor de la llamada.
Diagrama que muestra la comunicación controlada por eventos.
Patrones de resistencia y control de errores en el código de la aplicación
Una aplicación crítica debe diseñarse para ser resistente para que solucione tantos escenarios de error como sea posible. Esta resistencia maximiza la disponibilidad y confiabilidad del servicio. La aplicación debe tener capacidades de autocuración, que puedes implementar utilizando patrones de diseño como Reintentos con retroceso y Disyuntor.
Para fallos no transitorios que no puedes mitigar completamente en la lógica de la aplicación, el modelo de salud y las envolturas operacionales necesitan tomar acciones correctivas. El código de aplicación debe incorporar la instrumentación y el registro adecuados para informar al modelo de mantenimiento y facilitar la solución de problemas posterior o el análisis de la causa principal según sea necesario. Debe implementar trazado distribuido para proporcionar a la persona que llama un mensaje de error completo que incluya un identificador de correlación cuando ocurra un fallo.
Herramientas como Application Insights pueden ayudarle a consultar, correlacionar y visualizar trazas de aplicaciones.
Consideraciones de diseño
Configuraciones adecuadas. No es raro que los problemas transitorios causen errores en cascada. Por ejemplo, el reintento sin el retroceso adecuado agrava el problema cuando un servicio está siendo estrangulado. Puede espaciar los retardos de reintento linealmente o aumentarlos exponencialmente para retroceder a través de retardos crecientes.
Mantenimiento de punto de conexión. Puede exponer comprobaciones funcionales dentro del código de la aplicación mediante el uso de puntos de conexión de mantenimiento que las soluciones externas pueden sondear para recuperar el mantenimiento de estado de los componentes de la aplicación.
Recomendaciones de diseño
Aquí hay algunos patrones comunes de ingeniería de software para aplicaciones resilientes:
Patrón | Resumen |
---|---|
Queue-Based nivelación de carga | Introduce un búfer entre los consumidores y los recursos solicitados para asegurar niveles de carga consistentes. A medida que las solicitudes de los consumidores se ponen en cola, un proceso de trabajo las administra contra el recurso solicitado a un ritmo establecido por el trabajador y por la capacidad del recurso solicitado para procesar las solicitudes. Si los consumidores esperan respuestas a sus solicitudes, debe implementar un mecanismo de respuesta independiente. Aplique un orden priorizado para que las actividades más importantes se realicen primero. |
Disyuntor | Proporciona estabilidad esperando la recuperación o rechazando rápidamente las solicitudes en lugar de bloquear mientras espera un servicio remoto o recurso no disponible. Este patrón también controla los errores que pueden tardar una cantidad variable de tiempo en recuperarse cuando se realiza una conexión a un servicio remoto o recurso. |
Mamparo | Intenta crear particiones de instancias de servicio en grupos en función de los requisitos de carga y disponibilidad, aislando los errores para mantener la funcionalidad del servicio. |
Saga | Administra la coherencia de los datos entre microservicios que tienen almacenes de datos independientes asegurándose de que los servicios se actualizan entre sí a través de canales de mensajes o eventos definidos. Cada servicio realiza transacciones locales para actualizar su propio estado y publica un evento para desencadenar la siguiente transacción local en la saga. Si se produce un error en una actualización del servicio, la saga ejecuta transacciones compensatorias para contrarrestar los pasos anteriores a la actualización del servicio. Los pasos de actualización de servicios individuales pueden implementar por sí mismos patrones de resiliencia, como el reintento. |
Monitoreo de Puntos de Control de Salud | Implementa comprobaciones funcionales en una aplicación a la que las herramientas externas pueden acceder a través de puntos de conexión expuestos a intervalos regulares. Puede interpretar las respuestas de los puntos de conexión mediante métricas operativas clave para informar al estado de la aplicación y desencadenar respuestas operativas, como generar una alerta o realizar una implementación de reversión compensatoria. |
Reintentar | Controla los errores transitorios de forma elegante y transparente.
- Cancelar si es poco probable que el fallo sea transitorio y es poco probable que tenga éxito si se vuelve a intentar la operación. - Reintentar si el error es inusual o poco frecuente y es probable que la operación se realice correctamente si se intenta de nuevo inmediatamente. - Reintentar después de un retraso si el error se debe a una condición que podría necesitar un breve tiempo para recuperarse, como la conectividad de red o errores de alta carga. Aplique una estrategia de retroceso adecuada a medida que aumenten los retrasos de reintento. |
Limitación | Controla el consumo de recursos usados por los componentes de la aplicación, protegiéndolos de convertirse en sobrecargados. Cuando un recurso alcanza un umbral de carga, aplaza las operaciones de prioridad inferior y degrada la funcionalidad no esencial para que la funcionalidad esencial pueda continuar hasta que haya suficientes recursos disponibles para volver al funcionamiento normal. |
Estas son algunas recomendaciones adicionales:
Use los SDK proporcionados por el proveedor, como los SDK de Azure, para conectarse a servicios dependientes. Use funcionalidades de resistencia integradas en lugar de implementar funcionalidades personalizadas.
Aplique una estrategia de aplazamiento adecuada al reintentar llamadas de dependencia fallidas para evitar un escenario de DDoS autoinfligido.
Defina criterios de ingeniería comunes para todos los equipos de microservicios de aplicaciones para impulsar la coherencia y la velocidad en el uso de patrones de resistencia de nivel de aplicación.
Considere la posibilidad de implementar patrones de resistencia mediante paquetes estandarizados probados, como Polly para C# o Sentinel para Java. Además, los marcos de mensajería como NServiceBus o MassTransit proporcionan características de resistencia integradas, lo que ayuda a evitar la necesidad de código de confiabilidad adicional.
Use identificadores de correlación para todos los eventos de seguimiento y mensajes de registro para vincularlos a una solicitud determinada. Devuelva los identificadores de correlación al autor de la llamada para todas las llamadas, no solo las solicitudes con error.
Use el registro estructurado para todos los mensajes de registro. Seleccione un receptor de datos operativos unificado para seguimientos de aplicaciones, métricas y registros para permitir que los operadores puedan depurar fácilmente problemas. Para obtener más información, consulte Recopilación, agregación y almacenamiento de datos de supervisión para aplicaciones en la nube.
Asegúrese de que los datos operativos se usen junto con los requisitos empresariales para informar a un modelo de estado de la aplicación.
Selección del lenguaje de programación
Es importante seleccionar los marcos y lenguajes de programación adecuados. Estas decisiones suelen estar controladas por los conjuntos de aptitudes o las tecnologías estandarizadas de la organización. Sin embargo, es esencial evaluar el rendimiento, la resistencia y las funcionalidades generales de varios lenguajes y marcos.
Consideraciones de diseño
Capacidades del kit de desarrollo. Existen diferencias en las funcionalidades que ofrecen los SDK de servicio de Azure en varios lenguajes. Estas diferencias pueden influir en la elección de un servicio de Azure o lenguaje de programación. Por ejemplo, si Azure Cosmos DB es una opción factible, Es posible que Go no sea un lenguaje de desarrollo adecuado porque no hay ningún SDK de primera entidad.
Actualizaciones de características. Considere la frecuencia con la que se actualiza el SDK con nuevas características para el idioma seleccionado. Los SDK usados habitualmente, como las bibliotecas de .NET y Java, se actualizan con frecuencia. Es posible que otros SDK o SDK para otros lenguajes se actualicen con menos frecuencia.
Múltiples lenguajes de programación o frameworks. Puede usar varias tecnologías para admitir varias cargas de trabajo compuestas. Sin embargo, debe evitar la expansión porque presenta complejidad de administración y desafíos operativos.
Opción de proceso. Es posible que el software heredado o propietario no se ejecute en los servicios PaaS. Además, es posible que no pueda incluir software heredado o propietario en contenedores.
Recomendaciones de diseño
Evalúe todos los SDK de Azure pertinentes para las funcionalidades que necesita y los lenguajes de programación elegidos. Compruebe la alineación con los requisitos no funcionales.
Optimice la selección de lenguajes de programación y marcos en el nivel de microservicio. Use varias tecnologías según corresponda.
Priorice el SDK de .NET para optimizar la confiabilidad y el rendimiento. Los SDK de Azure de .NET suelen proporcionar más funcionalidades y se actualizan con frecuencia.
Paso siguiente
Revise las consideraciones para la plataforma de aplicaciones.