Sei sulla pagina 1di 329

●● Guía

Cloud de
arquitectura
Application
de aplicaciones
Architecture
en el cloud
Guide
PUBLICADO POR
Microsoft Press
Una división de Microsoft Corporation
One Microsoft Way
Redmond, Washington 98052-6399

Copyright © 2017 de Microsoft Corporation

Todos los derechos reservados. Ninguna parte del contenido de este libro puede reproducirse ni
transmitirse en forma alguna ni por ningún medio sin la autorización previa por escrito de la editorial.

Los libros de Microsoft Press están a la venta en librerías y distribuidores de todo el mundo. Si
tiene alguna duda relativa a este libro, escriba un correo electrónico a Microsoft Press Support a la
dirección mspinput@microsoft.com. Denos su opinión acerca de este libro en http://aka.ms/tellpress.

Este libro se proporciona “tal cual” y expresa los puntos de vista y opiniones de los autores. Los
puntos de vista, las opiniones y la información vertidos en este libro, incluidas las direcciones URL
y otras referencias a sitios web de Internet, pueden cambiar sin previo aviso.

Algunos ejemplos recogidos en este libro tienen un carácter únicamente ilustrativo y son ficticios.
No debe suponerse ni derivarse ninguna asociación o conexión real.

Microsoft y las marcas comerciales enumeradas en http://www.microsoft.com, en la página web


de marcas comerciales, son marcas comerciales del grupo empresarial Microsoft. Todas las demás
marcas son propiedad de sus respectivos titulares.

Editor de adquisiciones:
Christopher Bennage

Editores de desarrollo:
Mike Wasson, Masashi Narumoto y el equipo de modelos y prácticas de Microsoft

Producción editorial:
Phil Evans

Corrector de estilo:
Jamie Letain

i
Contenido
Información general ��������������������������������������������������������������������������������������������������������������������������� vii
Introducción ������������������������������������������������������������������������������������������������������������������������������������������������������������������� viii
Capítulo 1: Elección de un estilo de arquitectura ����������������������������������������������������������������������������� 1
Recorrido rápido por los estilos �������������������������������������������������������������������������������������������������������������������������������� 2
Los estilos de arquitectura como restricciones ���������������������������������������������������������������������������������������������������� 4
Consideración de las dificultades y las ventajas ������������������������������������������������������������������������������������������������� 5
Capítulo 1a: Estilo de arquitectura N-tier ����������������������������������������������������������������������������������������� 6
Cuándo utilizar esta arquitectura ������������������������������������������������������������������������������������������������������������������������������ 7
Ventajas ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� 7
Dificultades ������������������������������������������������������������������������������������������������������������������������������������������������������������������������ 7
Procedimientos recomendados ��������������������������������������������������������������������������������������������������������������������������������� 8
La arquitectura N-tier en máquinas virtuales ������������������������������������������������������������������������������������������������������� 8
Consideraciones adicionales ��������������������������������������������������������������������������������������������������������������������������������������� 9
Capítulo 1b: Estilo de arquitectura Web-Queue-Worker �������������������������������������������������������������� 10
Cuándo utilizar esta arquitectura ��������������������������������������������������������������������������������������������������������������������������� 11
Ventajas ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������� 11
Dificultades ��������������������������������������������������������������������������������������������������������������������������������������������������������������������� 11
Procedimientos recomendados ������������������������������������������������������������������������������������������������������������������������������ 11
Web-Queue-Worker en Azure App Service ������������������������������������������������������������������������������������������������������� 12
Consideraciones adicionales ������������������������������������������������������������������������������������������������������������������������������������ 12
Capítulo 1c: Estilo de arquitectura de microservicios �������������������������������������������������������������������� 14
Cuándo utilizar esta arquitectura ��������������������������������������������������������������������������������������������������������������������������� 15
Ventajas ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������� 15
Dificultades ��������������������������������������������������������������������������������������������������������������������������������������������������������������������� 16
Procedimientos recomendados ������������������������������������������������������������������������������������������������������������������������������ 17
Microservicios a través de Azure Container Service ��������������������������������������������������������������������������������������� 19
Capítulo 1d: Estilo de arquitectura CQRS ���������������������������������������������������������������������������������������� 20
Cuándo utilizar esta arquitectura ��������������������������������������������������������������������������������������������������������������������������� 21
Ventajas ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������� 21
Dificultades ��������������������������������������������������������������������������������������������������������������������������������������������������������������������� 22
Procedimientos recomendados ������������������������������������������������������������������������������������������������������������������������������ 22
CQRS en microservicios ��������������������������������������������������������������������������������������������������������������������������������������������� 22

ii Contenido
Capítulo 1e: Estilo de arquitectura controlada por eventos ��������������������������������������������������������� 24
Cuándo utilizar esta arquitectura ��������������������������������������������������������������������������������������������������������������������������� 25
Ventajas ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������� 25
Dificultades ��������������������������������������������������������������������������������������������������������������������������������������������������������������������� 25
Arquitecturas IoT ���������������������������������������������������������������������������������������������������������������������������������������������������������� 26
Capítulo 1f: Estilo de arquitectura de Big Data ������������������������������������������������������������������������������ 27
Ventajas ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������� 29
Dificultades ��������������������������������������������������������������������������������������������������������������������������������������������������������������������� 29
Procedimientos recomendados ������������������������������������������������������������������������������������������������������������������������������ 30
Capítulo 1g: Estilo de arquitectura de Big Compute ���������������������������������������������������������������������� 31
Cuándo utilizar esta arquitectura ��������������������������������������������������������������������������������������������������������������������������� 32
Ventajas ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������� 32
Dificultades ��������������������������������������������������������������������������������������������������������������������������������������������������������������������� 32
Big Compute con Azure Batch �������������������������������������������������������������������������������������������������������������������������������� 33
Big Compute en máquinas virtuales ��������������������������������������������������������������������������������������������������������������������� 33
Capítulo 2: Elección de las tecnologías de proceso y de almacén de datos �������������������������������� 35
Capitulo 2a: Información general de las opciones de proceso ����������������������������������������������������� 37
Capítulo 2b: Comparación de procesos ������������������������������������������������������������������������������������������� 39
Modelo de hosting ������������������������������������������������������������������������������������������������������������������������������������������������������ 39
DevOps ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������� 40
Escalabilidad ������������������������������������������������������������������������������������������������������������������������������������������������������������������� 41
Disponibilidad ���������������������������������������������������������������������������������������������������������������������������������������������������������������� 41
Seguridad ������������������������������������������������������������������������������������������������������������������������������������������������������������������������ 42
Otros ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� 42
Capítulo 2c: Información general de almacén de datos ���������������������������������������������������������������� 43
Sistemas de administración de bases de datos relacionales ����������������������������������������������������������������������� 44
Almacenes de pares clave-valor ����������������������������������������������������������������������������������������������������������������������������� 44
Bases de datos de documentos ������������������������������������������������������������������������������������������������������������������������������ 45
Bases de datos de gráficos ��������������������������������������������������������������������������������������������������������������������������������������� 46
Bases de datos de familias de columnas ������������������������������������������������������������������������������������������������������������� 47
Análisis de datos ����������������������������������������������������������������������������������������������������������������������������������������������������������� 48
Bases de datos de motores de búsqueda ����������������������������������������������������������������������������������������������������������� 48
Bases de datos de series temporales �������������������������������������������������������������������������������������������������������������������� 48
Almacenamiento de objetos ������������������������������������������������������������������������������������������������������������������������������������ 49
Archivos compartidos ������������������������������������������������������������������������������������������������������������������������������������������������� 49
Capítulo 2d: Comparación de almacenes de datos ������������������������������������������������������������������������ 50
Criterios para la elección de un almacén de datos ����������������������������������������������������������������������������������������� 50
Consideraciones generales ��������������������������������������������������������������������������������������������������������������������������������������� 50
Sistemas de administración de bases de datos relacionales (RDBMS) ��������������������������������������������������� 52
Bases de datos de documentos ������������������������������������������������������������������������������������������������������������������������������ 53
Almacenes de pares clave-valor ����������������������������������������������������������������������������������������������������������������������������� 54

iii Contenido
Bases de datos de gráficos ��������������������������������������������������������������������������������������������������������������������������������������� 55
Bases de datos de familias de columnas ������������������������������������������������������������������������������������������������������������� 56
Bases de datos de motores de búsqueda ����������������������������������������������������������������������������������������������������������� 57
Almacenamiento de datos ���������������������������������������������������������������������������������������������������������������������������������������� 57
Bases de datos de series temporales �������������������������������������������������������������������������������������������������������������������� 58
Almacenamiento de objetos ������������������������������������������������������������������������������������������������������������������������������������ 58
Archivos compartidos ������������������������������������������������������������������������������������������������������������������������������������������������� 59
Capítulo 3: Diseño de la aplicación Azure: principios de diseño �������������������������������������������������� 60
Capítulo 3a: Diseño para la recuperación automática ������������������������������������������������������������������� 62
Recomendaciones �������������������������������������������������������������������������������������������������������������������������������������������������������� 62
Capítulo 3b: Hacer que todo sea redundante ��������������������������������������������������������������������������������� 64
Recomendaciones �������������������������������������������������������������������������������������������������������������������������������������������������������� 64
Capítulo 3c: Reducción de la coordinación ������������������������������������������������������������������������������������� 66
Recomendaciones �������������������������������������������������������������������������������������������������������������������������������������������������������� 67
Capítulo 3d: Diseño pensando en la escalabilidad ������������������������������������������������������������������������� 69
Recomendaciones �������������������������������������������������������������������������������������������������������������������������������������������������������� 69
Capítulo 3e: Creación de particiones para evitar límites ��������������������������������������������������������������� 71
Recomendaciones �������������������������������������������������������������������������������������������������������������������������������������������������������� 72
Capítulo 3f: Diseño para el equipo de operaciones ����������������������������������������������������������������������� 73
Recomendaciones �������������������������������������������������������������������������������������������������������������������������������������������������������� 73
Capítulo 3g: Uso de servicios administrados ���������������������������������������������������������������������������������� 75
Capítulo 3h: Uso del mejor almacén de datos para el trabajo ������������������������������������������������������ 76
Recomendaciones �������������������������������������������������������������������������������������������������������������������������������������������������������� 77
Capítulo 3i: Diseño para permitir la evolución ������������������������������������������������������������������������������� 78
Recomendaciones �������������������������������������������������������������������������������������������������������������������������������������������������������� 78
Capítulo 3j: Desarrollo en función de las necesidades del negocio ��������������������������������������������� 80
Recomendaciones �������������������������������������������������������������������������������������������������������������������������������������������������������� 80
Capítulo 3k: Diseño de aplicaciones resistentes para Azure ��������������������������������������������������������� 82
¿Qué entendemos por resistencia? ����������������������������������������������������������������������������������������������������������������������� 82
Proceso para obtener resistencia ��������������������������������������������������������������������������������������������������������������������������� 83
Definición de los requisitos de resistencia ��������������������������������������������������������������������������������������������������������� 83
Diseño para garantizar la resistencia �������������������������������������������������������������������������������������������������������������������� 87
Estrategias de resistencia ����������������������������������������������������������������������������������������������������������������������������������������� 87
Implementación resistente ��������������������������������������������������������������������������������������������������������������������������������������� 91
Supervisión y diagnóstico ���������������������������������������������������������������������������������������������������������������������������������������� 92
Respuestas manuales ante errores ������������������������������������������������������������������������������������������������������������������������ 93
Resumen �������������������������������������������������������������������������������������������������������������������������������������������������������������������������� 94
Capítulo 4: Diseño de la aplicación Azure: uso de los pilares de la calidad �������������������������������� 95
Escalabilidad ������������������������������������������������������������������������������������������������������������������������������������������������������������������� 96
Disponibilidad ���������������������������������������������������������������������������������������������������������������������������������������������������������������� 98
Resistencia ����������������������������������������������������������������������������������������������������������������������������������������������������������������������� 99

iv Contenido
Administración y DevOps ���������������������������������������������������������������������������������������������������������������������������������������� 100
Seguridad ���������������������������������������������������������������������������������������������������������������������������������������������������������������������� 101
Capítulo 5: Diseño de la aplicación Azure: patrones de diseño �������������������������������������������������� 103
Dificultades en el desarrollo en el cloud ����������������������������������������������������������������������������������������������������������� 103
Administración de datos ����������������������������������������������������������������������������������������������������������������������������������������� 104
Diseño e implementación ��������������������������������������������������������������������������������������������������������������������������������������� 104
Mensajería ��������������������������������������������������������������������������������������������������������������������������������������������������������������������� 105
Administración y supervisión �������������������������������������������������������������������������������������������������������������������������������� 106
Rendimiento y escalabilidad ���������������������������������������������������������������������������������������������������������������������������������� 107
Resistencia ��������������������������������������������������������������������������������������������������������������������������������������������������������������������� 108
Seguridad ���������������������������������������������������������������������������������������������������������������������������������������������������������������������� 109
Capítulo 6: Catálogo de patrones ��������������������������������������������������������������������������������������������������� 110
Patrón embajador ������������������������������������������������������������������������������������������������������������������������������������������������������� 110
Patrón de capa para evitar daños ������������������������������������������������������������������������������������������������������������������������ 112
Patrón Back-ends for Front-ends �������������������������������������������������������������������������������������������������������������������������� 114
Patrón bulkhead ���������������������������������������������������������������������������������������������������������������������������������������������������������� 116
Patrón Cache-Aside ��������������������������������������������������������������������������������������������������������������������������������������������������� 119
Patrón de interruptor de circuito �������������������������������������������������������������������������������������������������������������������������� 124
Patrón CQRS ����������������������������������������������������������������������������������������������������������������������������������������������������������������� 132
Patrón de transacciones de compensación ������������������������������������������������������������������������������������������������������ 139
Patrón de consumidores competitivos �������������������������������������������������������������������������������������������������������������� 143
Patrón de consolidación de recursos de proceso ������������������������������������������������������������������������������������������ 148
Patrón de abastecimiento de eventos ���������������������������������������������������������������������������������������������������������������� 156
Patrón de almacén de configuración externa ������������������������������������������������������������������������������������������������� 162
Patrón de identidad federada �������������������������������������������������������������������������������������������������������������������������������� 170
Patrón de Gatekeeper ����������������������������������������������������������������������������������������������������������������������������������������������� 174
Patrón de agregación de puerta de enlace ������������������������������������������������������������������������������������������������������ 176
Patrón de descarga de puerta de enlace ���������������������������������������������������������������������������������������������������������� 180
Patrón de enrutamiento de puerta de enlace ������������������������������������������������������������������������������������������������� 182
Patrón de supervisión de puntos de conexión de estado �������������������������������������������������������������������������� 185
Patrón de tabla de índice ���������������������������������������������������������������������������������������������������������������������������������������� 191
Patrón de elección del líder ������������������������������������������������������������������������������������������������������������������������������������ 197
Patrón de vistas materializadas ����������������������������������������������������������������������������������������������������������������������������� 204
Patrón de canalizaciones y filtros ������������������������������������������������������������������������������������������������������������������������� 208
Patrón de colas de prioridad ���������������������������������������������������������������������������������������������������������������������������������� 215
Patrón de nivelación de carga basada en cola ������������������������������������������������������������������������������������������������ 221
Patrón de reintento ���������������������������������������������������������������������������������������������������������������������������������������������������� 224
Patrón de programador-agente-supervisor ����������������������������������������������������������������������������������������������������� 227
Patrón de particionamiento ����������������������������������������������������������������������������������������������������������������������������������� 234
Patrón de sidecar �������������������������������������������������������������������������������������������������������������������������������������������������������� 243

v Contenido
Patrón de hosting de contenido estático ���������������������������������������������������������������������������������������������������������� 246
Patrón de estrangulador ������������������������������������������������������������������������������������������������������������������������������������������ 250
Patrón de limitación �������������������������������������������������������������������������������������������������������������������������������������������������� 252
Patrón de llave de valet �������������������������������������������������������������������������������������������������������������������������������������������� 256
Capítulo 7: Listas de comprobación de revisión del diseño �������������������������������������������������������� 263
Lista de comprobación de DevOps ��������������������������������������������������������������������������������������������������������������������� 264
Lista de comprobación de disponibilidad �������������������������������������������������������������������������������������������������������� 270
Lista de comprobación de escalabilidad ����������������������������������������������������������������������������������������������������������� 276
Lista de comprobación de resistencia ���������������������������������������������������������������������������������������������������������������� 276
Servicios de Azure ������������������������������������������������������������������������������������������������������������������������������������������������������ 286
Capítulo 8: Resumen������������������������������������������������������������������������������������������������������������������������� 291
Capítulo 9: Arquitecturas de referencia de Azure ������������������������������������������������������������������������ 292
Administración de identidades ����������������������������������������������������������������������������������������������������������������������������� 293
Red híbrida �������������������������������������������������������������������������������������������������������������������������������������������������������������������� 298
Red DMZ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������ 303
Aplicación web administrada ��������������������������������������������������������������������������������������������������������������������������������� 306
Ejecución de cargas de trabajo en máquina virtual de Linux �������������������������������������������������������������������� 310
Ejecución de cargas de trabajo en máquina virtual de Windows ����������������������������������������������������������� 315

vi Contenido
Guía de arquitectura
de aplicaciones en el
cloud
Esta guía presenta un enfoque estructurado para el diseño de aplicaciones
en el cloud escalables, resistentes y con una elevada disponibilidad. Las
recomendaciones de este libro electrónico pretenden ayudarle a tomar
decisiones de arquitectura independientemente de la plataforma del cloud,
aunque utilizaremos Azure para poder compartir los procedimientos
recomendados derivados de muchos años de relaciones con nuestros clientes.

En los capítulos siguientes, haremos un breve recorrido a través de una


selección de consideraciones y recursos importantes que le ayudarán a
determinar cuál es el mejor enfoque para su aplicación de cloud:

1. Elección del estilo de arquitectura correcto para una aplicación en función del
tipo de solución que se está construyendo.

2. Elección de las tecnologías más apropiadas de proceso y almacén de datos.

3. Incorporación de los diez principios de diseño de alto nivel para garantizar


que la aplicación es escalable, resistente y manejable.

4. Uso de los cinco pilares de calidad del software para garantizar el éxito.

5. Aplicación de patrones de diseño concretos para el problema que se intenta


solucionar.

vii Introducción
Introducción
El cloud está cambiando la forma en que se diseñan las aplicaciones. En lugar
de monolitos, las aplicaciones se descomponen en servicios más pequeños
y descentralizados. Estos servicios se comunican a través de API o mediante
mensajería asincrónica o eventos. Las aplicaciones crecen horizontalmente,
agregando nuevas instancias a medida que crece la demanda.
Estas tendencias comportan nuevas dificultades. El estado de las aplicaciones se distribuye. Las
operaciones se realizan en paralelo y de forma asincrónica. El sistema en su conjunto debe ser flexible
cuando surgen errores. Las implementaciones deben ser automáticas y predecibles. La supervisión y la
telemetría son críticas para un mayor conocimiento del sistema. La Guía de arquitectura de aplicaciones
Azure está diseñada para ayudarle a incorporar estos cambios.

Local, tradicional En el cloud, moderno


●● Monolítico, centralizado ●● Descompuesto, descentralizado
●● Diseño para escalabilidad ●● Diseño para escalado flexible
predecible ●● Persistencia políglota (combinación de
●● Base de datos relacional tecnologías de almacenamiento)
●● Gran consistencia ●● Coherencia entre eventos
●● Procesamiento serie y sincronizado ●● Procesamiento paralelo y asincrónico
●● Diseño para evitar errores (MTBF) ●● Diseño pensando en el error (MTTR)
●● Grandes actualizaciones ocasionales ●● Pequeñas actualizaciones frecuentes
●● Administración manual ●● Administración de autoservicio
●● Servidores de copo de nieve ●● Infraestructura inmutable

El cloud está cambiando la forma en que se diseñan las aplicaciones. En lugar de monolitos, las
aplicaciones se descomponen en servicios más pequeños y descentralizados. Estos servicios se comunican
a través de API o mediante mensajería asincrónica o eventos. Las aplicaciones crecen horizontalmente,
agregando nuevas instancias a medida que crece la demanda.

Estas tendencias comportan nuevas dificultades. El estado de las aplicaciones se distribuye. Las operaciones
se realizan en paralelo y de forma asincrónica. El sistema en su conjunto debe ser flexible cuando surgen
errores. Las implementaciones deben ser automáticas y predecibles. La supervisión y la telemetría son
críticas para un mayor conocimiento del sistema. La Guía de arquitectura de aplicaciones en el cloud está
diseñada para ayudarle a entender e incorporar estos cambios.

Cómo está estructurada esta guía


La Guía de arquitectura de aplicaciones en el cloud está organizada como una serie de pasos, que van
desde la arquitectura y el diseño hasta la implementación. Cada paso cuenta con directrices que le
ayudarán a diseñar la arquitectura de su aplicación.

viii
Estilos de arquitectura. La primera decisión es la más importante. ¿Qué tipo de arquitectura construye?
Podría ser una arquitectura de microservicios, una aplicación N-tier más tradicional o una solución de
Big Data. Hemos identificado siete estilos de arquitectura distintos. Cada uno de ellos tiene ventajas
y dificultades.

●● Las Arquitecturas de referencia de Azure muestran las implementaciones recomendadas en Azure,


junto con consideraciones de escalabilidad, disponibilidad, facilidad de uso y seguridad. La mayoría
también incluyen plantillas de administrador de recursos que pueden implementarse.

Opciones de tecnología. Existen dos opciones de tecnología sobre las que cabe tomar una decisión
desde el principio, porque afectan a toda la arquitectura. Se trata de la elección de las tecnologías
de proceso y almacenamiento. El término "proceso" se refiere al modelo de hosting de los recursos
informáticos sobre los que se ejecutan las aplicaciones. El almacenamiento incluye las bases de datos, pero
también el almacenamiento de colas de mensajes, cachés, datos de IoT, datos de registro no estructurados
y cualquier otra cosa que una aplicación pueda almacenar de forma persistente.

●● Las opciones de proceso y las opciones de almacenamiento proporcionan criterios detallados de


comparación para seleccionar servicios de proceso y de almacenamiento.

Principios de diseño. Durante el proceso de diseño, tenga presentes estos diez principios de diseño de
alto nivel.

●● Para acceder a artículos de procedimientos recomendados que ofrecen orientación específica


sobre escalado automático, almacenamiento en caché, partición de datos, diseño de API y más,
vea https://docs.microsoft.com/en-us/azure/architecturebest-practices/index.

Pilares. Una aplicación de cloud con éxito está centrada en estos cinco pilares de la calidad del software:
escalabilidad, disponibilidad, resistencia, administración y seguridad.

●● Utilice nuestras listas comprobación para la revisión del diseño y revise su diseño de acuerdo con
estos pilares de la calidad.

Patrones de diseño del cloud. Estos patrones de diseño son útiles para construir aplicaciones de Azure
fiables, escalables y seguras. Cada patrón describe un problema, un patrón que aborda el problema y un
ejemplo basado en Azure.

●● Vea el catálogo completo de patrones de diseño del cloud.

Antes de comenzar
Obtenga
ayuda de los
Si todavía no lo ha hecho, crea una cuenta de Azure gratuita para poder
empezar a trabajar con este libro electrónico.

• Un crédito de 200 dólares para utilizar con cualquier producto de Azure


durante 30 días. expertos
• Acceso gratuito durante 12 meses a la mayoría de nuestros
Póngase en contacto con
productos más populares de distintas categorías, como proceso,
almacenamiento, redes y bases de datos. nosotros en
aka.ms/azurespecialist
• Más de 25 productos que siempre son gratuitos.

ix Introducción
1

Elección de un estilo
de arquitectura
La primera decisión que debe tomar al diseñar una aplicación en el cloud
se refiere a la arquitectura. Elija la mejor arquitectura para la aplicación
que está creando en función de su complejidad, el tipo de dominio, si es
una aplicación de IaaS o PaaS y lo que hará la aplicación. También debe
tener en cuenta los conocimientos de los equipos de desarrolladores
y DevOps, y si la aplicación cuenta con una arquitectura existente.

Un estilo de arquitectura impone restricciones en el diseño que determinan su "forma" al restringir


las opciones. Estas restricciones plantean tanto ventajas como dificultades para el diseño. Utilice la
información de esta sección para entender cuáles son las ventajas y desventajas derivadas de la adopción
de cualquiera de estos estilos.

Esta sección describe los diez principios de diseño que debe tener en cuenta durante el desarrollo. Seguir
estos principios le ayudará a crear una aplicación más escalable, resistente y administrable.

Hemos identificado un conjunto de estilos de arquitectura que se encuentran habitualmente en las


aplicaciones del cloud. El artículo para cada estilo incluye:

●● Una descripción y el diagrama lógico del estilo.


●● Recomendaciones sobre cuándo elegir este estilo.
●● Ventajas, dificultades y procedimientos recomendados.
●● Una implementación recomendada utilizando los servicios relevantes de Azure.

1 CAPÍTULO 1 | Elección de un estilo de arquitectura


Recorrido rápido por los estilos
Esta sección describe brevemente los estilos de arquitectura que hemos identificado y ofrece algunas
consideraciones de alto nivel para su uso. Encontrará más información en los temas enlazados.

N-tier
N-tier es una arquitectura tradicional para aplicaciones empresariales. Las dependencias se gestionan
dividiendo la aplicación en capas que realizan funciones lógicas, como presentación, lógica de negocio y
acceso a los datos. Una capa solo puede hacer llamadas a las capas que tienen por debajo. Sin embargo, esta
estratificación horizontal por capas puede convertirse en un pasivo. Puede ser difícil introducir cambios en una
parte de la aplicación sin tocar el resto de la aplicación. Esto convierte las actualizaciones frecuentes son todo
un reto y limita la rapidez con que se introducen las nuevas características.

El uso de N-tier es apropiado para migrar las aplicaciones existentes que ya utilizan una arquitectura en
capas. Por este motivo, N-tier se ve más a menudo en soluciones de infraestructura como servicio (IaaS)
o aplicaciones que utilizan una mezcla de IaaS y servicios gestionados.

Web-Queue-Worker
Para una solución plenamente PaaS, considere una arquitectura de tipo Web-Queue-Worker.
En este estilo, la aplicación cuenta con un front-end web que controla las solicitudes HTTP y un trabajo
de back-end que realiza tareas que requieren un uso intenso de la CPU u operaciones de larga duración.
El front-end se comunica con el trabajo a través de una cola de mensajes asincrónicos.

El modelo Web-Queue-Worker es apropiado para dominios relativamente simples con algunas tareas
que requieren un uso de recursos intensivo. Al igual que N-tier, la arquitectura es fácil de entender. El uso
de servicios gestionados simplifica la implementación y las operaciones. Pero con dominios complejos,
puede ser difícil administrar las dependencias. El front-end y el trabajo pueden convertirse fácilmente en
componentes grandes y monolíticos, difíciles de mantener y actualizar. Igual que sucede con N-tier, esto
puede reducir la frecuencia de las actualizaciones y limitar la innovación.

2 CAPÍTULO 1 | Elección de un estilo de arquitectura


Microservicios
Si su aplicación tiene un dominio más complejo, plantéese pasar a una arquitectura de microservicios.
Una aplicación de microservicios está compuesta por muchos pequeños servicios independientes.
Cada servicio implementa una capacidad empresarial única. Los servicios se combinan libremente,
comunicándose a través de contratos de API.

Cada servicio lo puede crear un pequeño equipo de desarrollo especializado. Es posible implementar
servicios individuales sin demasiada coordinación entre los equipos, lo que anima a realizar
actualizaciones frecuentes. Una arquitectura de microservicios es más compleja de construir y manejar que
una arquitectura N-tier o web-queue-worker. Requiere una cultura de desarrollo y DevOps madura. Pero
bien hecho, este estilo de arquitectura permite ganar velocidad, innovar con mayor rapidez y contar con
una arquitectura más resistente.

CQRS
El estilo CQRS (Command and Query Responsibility Segregation) separa las operaciones de lectura y
escritura en modelos separados. De esta forma se aíslan las partes del sistema que actualizan los datos de
las partes que leen los datos. Además, las lecturas pueden ejecutarse en una vista materializada que está
físicamente separada de la base de datos de escritura. Esto permite escalar las cargas de trabajo de lectura
y escritura de forma independiente y optimizar la vista materializada para consultas.

El uso de CQRS es más lógico cuando se aplica a un subsistema de una arquitectura más grande. Por
lo general, no debe imponerse en toda la aplicación pues solo provocaría una complejidad innecesaria.
Considere su uso en dominios colaborativos donde muchos usuarios acceden a los mismos datos.

3 CAPÍTULO 1 | Elección de un estilo de arquitectura


Arquitectura controlada por eventos
Las arquitecturas controladas por eventos utilizan un modelo de publicación-suscripción (pub-sub),
en el que los productores publican eventos y los consumidores se suscriben a ellos. Los productores son
independientes de los consumidores y los consumidores son independientes entre sí.

Plantéese el uso de una arquitectura controlada por eventos para aquellas aplicaciones que ingieren y
procesan un gran volumen de datos con una latencia muy baja, como las soluciones de IoT. Este estilo
también es útil cuando diferentes subsistemas deben realizar diferentes tipos de procesamiento para los
mismos datos de evento.

Big Data, Big Compute


Big Data y Big Compute son estilos de arquitectura especializados para cargas de trabajo que se ajustan
a determinados perfiles específicos. Big Data divide un conjunto de datos muy grande en trozos y realiza
procesos en paralelo en todo el conjunto, para el análisis y la presentación de informes. Big Computing,
conocido también como informática de alto rendimiento (HPC), realiza procesos paralelos en un gran número
(miles) de núcleos. Los dominios incluyen simulaciones, modelado y representación 3D.

Los estilos de arquitectura como restricciones


Un estilo de arquitectura impone restricciones sobre el diseño, incluyendo el conjunto de elementos que
pueden aparecer y las relaciones permitidas entre dichos elementos. Las restricciones guían la "forma"
de una arquitectura restringiendo la variedad de opciones. Cuando una arquitectura se ajusta a las
restricciones de un estilo particular, emergen ciertas propiedades deseables.

Por ejemplo, las restricciones en microservicios incluyen:

●● Un servicio representa una responsabilidad individual.


●● Cada servicio es independiente de los demás.
●● Los datos son privados para el servicio que los posee. Los servicios no comparten datos.

Respetando estas restricciones, lo que surge es un sistema en el que los servicios pueden
implementarse de forma independiente, se aíslan los errores, permite actualizaciones frecuentes y resulta
fácil introducir nuevas tecnologías en la aplicación.

4 CAPÍTULO 1 | Elección de un estilo de arquitectura


Antes de elegir un estilo de arquitectura, asegúrese de que entiende los principios subyacentes y las
restricciones de ese estilo. De lo contrario, puede acabar con un diseño que se ajuste al estilo en un nivel
superficial, pero que no alcance todo el potencial de dicho estilo. También es importante ser pragmático.
A veces es mejor suavizar una restricción, en lugar de insistir en la pureza de la arquitectura.

La tabla siguiente resume cómo se ocupa de las dependencias cada estilo y los tipos de dominio más
adecuados para cada uno.

Estilo de arquitectura Administración de dependencias Tipo de dominio


N-tier Niveles horizontales divididos por subred. Dominio de negocio tradicional. La frecuencia de
actualizaciones es baja.

Web-Queue-Worker Trabajos de front-end y back-end, desacoplados Dominio relativamente simple con algunas tareas
mediante mensajería asincrónica. que requieren un uso de recursos intensivo.

Microservicios Servicios descompuestos de forma vertical Dominio complejo. Actualizaciones frecuentes.


(funcionalmente) que se llaman unos a otros a
través de API.

CQRS Segregación de lectura y escritura. El esquema y la Dominio colaborativo en el que muchos usuarios
escala se optimizan por separado. acceden a los mismos datos.

Arquitectura controlada por Productor/consumidor. Vista independiente por IoT y sistemas en tiempo real.
eventos subsistema.

Divide un enorme conjunto de datos en trozos pequeños. Análisis de datos en tiempo real y por lotes. Análisis
Big Data Procesamiento paralelo en conjuntos de datos locales. predictivo con ML.

Big Compute Asignación de datos a miles de núcleos. Dominios de proceso intensivo como la simulación.

Consideración de las dificultades y las ventajas


Las restricciones también plantean dificultades, por lo que es importante entender cuál es la contrapartida
de la adopción de cualquiera de estos estilos. ¿Los beneficios del estilo de arquitectura superan las
dificultades para este subdominio y contexto acotado?

Estas son algunas de las dificultades que cabe considerar al seleccionar un estilo de arquitectura:
●● Complejidad. ¿La complejidad de la arquitectura está justificada para su dominio? O, por el contrario,
¿el estilo es demasiado sencillo para su dominio? En ese caso, corre el riesgo de acabar en un barrizal,
porque la arquitectura no le ayudará a administrar adecuadamente las dependencias.

●● Mensajería asincrónica y coherencia al cabo del tiempo. La mensajería asincrónica permite


desvincular los servicios y aumentar la fiabilidad (porque los mensajes pueden reintentarse) y la
escalabilidad. Aunque también crea dificultades como la semántica del tipo "siempre-una vez" y la
coherencia al cabo del tiempo.
●● Comunicación entre servicios. Cuando una aplicación se descompone en servicios separados,
existe el riesgo de que la comunicación entre los servicios provoque una latencia inaceptable o una
congestión de la red (por ejemplo, en una arquitectura de microservicios).

●● Facilidad de uso. ¿Es difícil administrar la aplicación, supervisarla, implementar las actualizaciones,
etc.?

5 CAPÍTULO 1 | Elección de un estilo de arquitectura


1a

Estilo de arquitectura
N-tier
Una arquitectura N-tier divide una aplicación en capas lógicas y niveles físicos.

Las capas son una manera de separar responsabilidades y gestionar dependencias. Cada capa tiene una
responsabilidad específica. Una capa superior puede utilizar los servicios de una capa más baja, pero no al
revés.

Los niveles están físicamente separados y se ejecutan en máquinas independientes. Un nivel puede
llamar directamente a otro nivel o utilizar la mensajería asincrónica (cola de mensajes). Aunque cada capa
puede estar alojada en su propio nivel, no es obligatorio. Varias capas pueden alojarse en el mismo nivel.
Separar físicamente los niveles mejora la escalabilidad y la resistencia, pero también añade la latencia de la
comunicación de red adicional.
Una aplicación tradicional de tres niveles tiene un nivel de presentación, un nivel intermedio y un nivel de
base de datos. El nivel intermedio es opcional. Las aplicaciones más complejas pueden tener más de tres
niveles. El diagrama superior muestra una aplicación con dos niveles intermedios en los que se encapsulan
diferentes áreas de funcionalidad.

6 CAPÍTULO 1a | Estilo de arquitectura N-tier


Una aplicación N-tier puede tener una arquitectura de capa cerrada o una arquitectura de capa abierta:

●● En una arquitectura de capa cerrada, una capa solo puede llamar a la siguiente capa inmediatamente
por debajo
●● En una arquitectura de capa abierta, una capa puede llamar a cualquiera de las capas situadas por
debajo de ella.
Una arquitectura de capa cerrada limita las dependencias entre las capas, pero puede crear tráfico de red
innecesario si una capa se limita a pasar las solicitudes a la capa siguiente.

Cuándo utilizar esta arquitectura


Las arquitecturas N-tier normalmente se implementan como aplicaciones de infraestructura como servicio
(IaaS) en las que cada nivel se ejecuta en un conjunto separado de máquinas virtuales. De todos modos,
una aplicación N-tier no necesita ser un modelo puro de IaaS. Con frecuencia resulta útil utilizar servicios
gestionados para algunas partes de la arquitectura, especialmente el almacenamiento en caché, la
mensajería y el almacenamiento de datos.

Plantéese el uso de una arquitectura N-tier para:


●● Aplicaciones web sencillas.
●● Migrar una aplicación local a Azure con refactorización mínima.
●● Desarrollo unificado de aplicaciones locales y en el cloud.

Las arquitecturas N-tier son muy comunes en las aplicaciones tradicionales locales, por lo que son una
elección natural para la migración de las cargas de trabajo existentes a Azure.

Ventajas
●● Portabilidad entre cloud y local, y entre plataformas del cloud.
●● Menor curva de aprendizaje para la mayoría de desarrolladores.
●● Evolución natural del modelo tradicional de aplicaciones.
●● Abierta a entornos heterogéneos (Windows/Linux)

Dificultades
●● Es fácil terminar con un nivel intermedio que simplemente realiza operaciones de CRUD en la base de
datos y agrega latencia adicional sin hacer ningún trabajo útil.

●● El diseño monolítico evita la implementación independiente de características.


●● La administración de una aplicación de IaaS es más trabajo que una aplicación que utiliza únicamente
servicios gestionados.
●● Puede resultar difícil administrar la seguridad de la red en un sistema grande.

7 CAPÍTULO 1a | Estilo de arquitectura N-tier


Procedimientos recomendados
●● Uso del escalado automático para ocuparse de los cambios en las cargas. Vea Procedimientos
recomendados para el escalado automático.
●● Uso de la mensajería asincrónica para separar niveles.
●● Almacenamiento en caché de datos semiestáticos. Vea Procedimientos recomendados para el
almacenamiento en caché.
●● Configure el nivel de base de datos para alta disponibilidad utilizando una solución como
grupos de disponibilidad AlwaysOn de SQL Server.
●● Coloque un firewall de aplicaciones web (WAF) entre el front-end e Internet.
●● Coloque cada nivel en su propia subred y utilice subredes como barrera de seguridad.
●● Restrinja el acceso al nivel de datos, permitiendo únicamente solicitudes de los niveles intermedios.

La arquitectura N-tier en máquinas virtuales


Esta sección describe una arquitectura N-tier recomendada en ejecución en máquinas virtuales.

Esta sección describe una arquitectura N-tier recomendada en ejecución en máquinas virtuales. Cada nivel
consta de dos o más máquinas virtuales, en un conjunto de disponibilidad o un conjunto de escalado de
máquinas virtuales. Múltiples máquinas virtuales ofrecen resistencia en caso de que una máquina virtual falle.
Se utilizan equilibradores de carga para distribuir solicitudes a las máquinas virtuales de un nivel. Un nivel
puede escalarse horizontalmente añadiendo más máquinas virtuales al grupo.

Cada nivel se ubica también dentro de su propia subred, lo que significa que sus direcciones IP internas
se engloban dentro del mismo rango de direcciones. De esta forma resulta fácil aplicar reglas de grupo de
seguridad de red (NSG) y tablas de ruta a niveles individuales.

Los niveles de web y empresarial no tienen estado. Cualquier máquina virtual puede ocuparse de cualquier
solicitud de ese nivel. El nivel de datos debe consistir en una base de datos replicada. Para Windows,
recomendamos SQL Server, utilizando Grupos de disponibilidad AlwaysOn para alta disponibilidad. Para
Linux, elija una base de datos que permita la replicación, como Apache Cassandra.

Los Grupos de seguridad de red (NSG) restringen el acceso a cada nivel. Por ejemplo, el nivel de base de
datos solo permite el acceso desde el nivel empresarial.

8 CAPÍTULO 1a | Estilo de arquitectura N-tier


Para obtener más detalles y una plantilla de administrador de recursos que puede implementarse, vea las
siguientes arquitecturas de referencia:

●● Ejecutar máquinas virtuales de Windows para una aplicación N-tier


●● Ejecutar máquinas virtuales de Linux para una aplicación N-tier

Consideraciones adicionales
●● Las arquitecturas N-tier no están restringidas a tres niveles. Para aplicaciones más complejas, es
habitual tener más niveles. En ese caso, plantéese utilizar el enrutamiento de capa 7 para enrutar
solicitudes a un nivel particular.

●● Los niveles son los límites de escalabilidad, fiabilidad y seguridad. Plantéese tener niveles separados
para servicios con requisitos diferentes en esas áreas.

●● Utilice conjuntos de escalado de máquinas virtuales para el escalado automático.

●● Busque lugares en la arquitectura donde pueda utilizar un servicio gestionado sin una refactorización
importante. En concreto, analice el almacenamiento en caché, la mensajería, el almacenamiento y las
bases de datos.

●● Para mayor seguridad, coloque una red DMZ frente a la aplicación. La DMZ incluye dispositivos de
red virtuales (NVA) que implementan la funcionalidad de seguridad como firewalls e inspección de
paquetes. Para obtener más información, vea Arquitectura de referencia de la red DMZ.

●● Para alta disponibilidad, coloque dos o más NVA en un conjunto de disponibilidad, con un
equilibrador de cargas externas para distribuir las solicitudes de Internet en las instancias. Para
obtener más información, vea Implementar dispositivos virtuales de red de alta disponibilidad.

●● No permita el acceso directo de RDP o SSH a máquinas virtuales que ejecutan el código de la
aplicación. En lugar de ello, los operadores deben iniciar sesión en un jumpbox, también conocido
como pasarela de aplicaciones. Se trata de una máquina virtual en la red que los administradores
utilizan para conectarse a las otras máquinas virtuales. El jumpbox tiene un NSG que permite RDP o
SSH solo desde direcciones IP públicas aprobadas.

●● Puede extender la red virtual de Azure a su red local usando una red privada virtual (VPN) de sitio
a sitio o Azure ExpressRoute. Para obtener más información, vea Arquitectura de referencia de red
híbrida.

●● Si su organización utiliza Active Directory para administrar la identidad, puede que desee extender
su entorno de Active Directory a la Azure VNet. Para obtener más información, vea Arquitectura de
referencia de administración de identidad.

●● Si necesita mayor disponibilidad que la que ofrece el SLA de Azure para máquinas virtuales, replique
la aplicación en dos regiones y utilice Traffic Manager de Azure para conmutación por error. Para
obtener más información, vea Ejecutar máquinas virtuales de Windows en varias regiones o
Ejecutar máquinas virtuales de Linux en varias regiones.

9 CAPÍTULO 1a | Estilo de arquitectura N-tier


1b

Estilo de arquitectura
Web-Queue-Worker
Los componentes básicos de esta arquitectura son un front-end web
que sirve las solicitudes del cliente y un trabajo que realiza tareas que
requieren un uso intensivo de recursos, flujos de trabajo de larga duración
o trabajos por lotes. El front-end web se comunica con el trabajo a través
de una cola de mensajes.

Otros componentes que habitualmente se incorporan a esta arquitectura son:


●● Una o más bases de datos.
●● Una caché para almacenar los valores de la base de datos para lecturas rápidas.
●● CDN para servir contenido estático.

10 CAPÍTULO 1b | Estilo de arquitectura Web-Queue-Worker


El web y el trabajo no tienen estado. El estado de la sesión se puede almacenar en una memoria caché
distribuida. Cualquier trabajo de larga duración se realiza de forma asincrónica por parte del trabajo.
El trabajo puede estar desencadenado por los mensajes en la cola o ejecutarse según un programa de
procesamiento por lotes. El trabajo es un componente opcional. Si no hay ninguna operación de larga
duración, el trabajo puede omitirse.

El front-end puede consistir en una API web. En el lado del cliente, la API web se puede usar con una
aplicación de una sola página que realiza las llamadas AJAX o con una aplicación cliente nativa.

Cuándo utilizar esta arquitectura


La arquitectura Web-Queue-Worker normalmente se implementa utilizando servicios de proceso
gestionados, ya sea Azure App Service o Azure Cloud Services.

Plantéese el uso de este estilo de arquitectura para:


●● Aplicaciones con un dominio relativamente sencillo.
●● Aplicaciones con algunos flujos de trabajo de larga duración u operaciones por lotes.
●● Cuando desee utilizar servicios gestionados, en lugar de infraestructura como servicio (IaaS).

Ventajas
●● Arquitectura relativamente sencilla y fácil de entender.
●● Fácil de implementar y administrar.
●● Clara separación de problemas.
●● El front-end se desacopla del trabajo por medio de la mensajería asincrónica.
●● El front-end y el trabajo pueden escalarse de forma independiente.

Dificultades
●● Sin un diseño cuidadoso, el front-end y el trabajo pueden convertirse en componentes grandes y
monolíticos difíciles de mantener y actualizar.

●● Puede haber dependencias ocultas si el front-end y el trabajo comparten esquemas de datos o


módulos de código.

Procedimientos recomendados
●● Utilice la persistencia políglota cuando sea apropiado. Vea Uso del mejor almacén de datos para el
trabajo.

●● Para ver los artículos de procedimientos recomendados que ofrecen orientación específica sobre el
escalado automático, el almacenamiento en caché, la partición de datos, el diseño API y más, vaya a
https://docs.microsoft.com/en-us/azure/architecture/best-practices/index.

11 CAPÍTULO 1b | Estilo de arquitectura Web-Queue-Worker


Web-Queue-Worker en Azure App Service
Esta sección describe una arquitectura Web-Queue-Worker recomendada que utiliza Azure App Service.

El front-end se implementa como una aplicación web de Azure App Service y el trabajo se implementa
como un WebJob. La aplicación web y el WebJob se asocian con un plan de servicios de aplicaciones que
proporciona las instancias de máquina virtual.

Puede utilizar colas de Azure Service Bus o Azure Storage para la cola de mensajes. (El diagrama muestra
una cola de Azure Storage).

Azure Redis Cache almacena el estado de la sesión y otros datos que necesitan acceso de baja latencia.

Azure CDN se utiliza para almacenar en caché contenido estático como imágenes, CSS o HTML.

Para el almacenamiento, elija las tecnologías de almacenamiento que mejor se adapten a las necesidades
de la aplicación. Puede utilizar múltiples tecnologías de almacenamiento (persistencia políglota). Para
ilustrar esta idea, el diagrama muestra Azure SQL Database y Azure Cosmos DB.

Para obtener más información, vea Arquitectura de referencia de aplicación web administrada.

Consideraciones adicionales
●● No todas las transacciones deben pasar por la cola y el trabajo para el almacenamiento. El front-end
web puede realizar operaciones sencillas de lectura/escritura directamente. Los trabajos están diseñados
para tareas que requieren un uso intensivo de recursos o flujos de trabajo de larga duración. En algunos
casos, quizás no necesite un trabajo en absoluto.

●● Utilice la función integrada de escalado integrado de App Service para ampliar el número de
instancias de máquina virtual. Si la carga de la aplicación sigue patrones predecibles, utilice el
escalado automático basado en programación. Si la carga es impredecible, utilice reglas de escalado
automático basado en métricas.

12 CAPÍTULO 1b | Estilo de arquitectura Web-Queue-Worker


●● Considere colocar la aplicación web y el WebJob en planes separados de App Service. De esta
forma se alojan en instancias independientes de máquina virtual y pueden escalarse de manera
independiente.

●● Utilice planes independientes de App Service para la producción y las pruebas. De lo contrario,
si utiliza el mismo plan para producción y pruebas, significa que las pruebas se ejecutan en sus
máquinas virtuales de producción.

●● Utilice ranuras de implementación para administrar las implementaciones. Esto permite implementar
una versión actualizada en un período de ensayo y, posteriormente, cambiar a la nueva versión.
También le permite volver a la versión anterior si ha habido algún problema en la actualización.

13 CAPÍTULO 1b | Estilo de arquitectura Web-Queue-Worker


1c

Estilo de arquitectura
de microservicios
Una arquitectura de microservicios consiste en una colección de
servicios pequeños y autónomos. Cada servicio es autónomo y debería
implementar una única capacidad empresarial.

De alguna manera, los microservicios son la evolución natural de las arquitecturas orientadas a servicios
(SOA) pero existen diferencias entre los microservicios y las SOA. Estas son algunas características
definitorias de un microservicio:

●● En una arquitectura de microservicios, los servicios son pequeños, independientes y tienen un


acoplamiento flexible.

●● Cada servicio es una base de código independiente que puede administrar un pequeño equipo de
desarrollo.

●● Los servicios pueden implementarse independientemente. Un equipo puede actualizar un servicio


existente sin reconstruir y redistribuir toda la aplicación.

●● Los servicios son responsables de conservar sus propios datos o estado externo. Esto difiere del
modelo tradicional, donde una capa de datos independiente se ocupa de la persistencia de los datos.

14 CAPÍTULO 1c | Estilo de arquitectura de microservicios


●● Los servicios se comunican entre ellos utilizando API bien definidas. Los detalles de implementación
internos de cada servicio se ocultan de otros servicios.

●● No es necesario que los servicios compartan el mismo conjunto de tecnologías, bibliotecas o marcos
de trabajo.

Además de los propios servicios, en una arquitectura típica de microservicios aparecen otros
componentes:

Administración. El componente de administración se ocupa de asignar servicios a nodos, identificar


errores, reequilibrar servicios entre los nodos y sucesivamente.

Detección de servicios. Mantiene una lista de servicios y los nodos en que se encuentran. Permite la
búsqueda de servicios para encontrar el punto de conexión de un servicio.

Puerta de enlace de API. La puerta de enlace API es el punto de entrada para los clientes. Los clientes no
llaman directamente a los servicios. En su lugar, llaman a la puerta de enlace API, que remite la llamada a
los servicios apropiados en el back-end. La puerta de enlace API podría agregar las respuestas de varios
servicios y devolver la respuesta agregada.

Estas son algunas de las ventajas de utilizar una puerta de enlace de API:

●● Desvincula a los clientes de los servicios. Los servicios pueden versionarse o reestructurarse sin
necesidad de actualizar todos los clientes.

●● Los servicios pueden utilizar protocolos de mensajería que no son aptos para el web, como AMQP.

●● La puerta de enlace API puede realizar otras funciones transversales como autenticación, registro,
terminación SSL y equilibrio de carga.

Cuándo utilizar esta arquitectura


Plantéese el uso de este estilo de arquitectura para:
●● Aplicaciones de gran tamaño que requieren mayor velocidad.

●● Aplicaciones complejas que requieren alta escalabilidad.

●● Aplicaciones con ricos dominios o muchos subdominios.

●● Una organización que está formada por equipos de desarrollo pequeños.

Ventajas
●● Implementaciones independientes. Puede actualizar un servicio sin volver a implementar toda la
aplicación y revertir o recuperar una actualización si algo sale mal. Las correcciones de errores y los
lanzamientos de características son más manejables y entrañan menos riesgos.

●● Desarrollo independiente. Un solo equipo de desarrollo puede construir, probar e implementar un


servicio. El resultado es la innovación continua y una cadencia más rápida de lanzamiento.

15 CAPÍTULO 1c | Estilo de arquitectura de microservicios


●● Equipos pequeños y enfocados. Equipos que pueden centrarse en un servicio. Los servicios tienen
un ámbito más pequeño y, por lo tanto, la base de código es más fácil de entender y resulta más fácil
para los nuevos miembros del equipo ponerse al día.

●● Aislamiento de errores. Si un servicio falla, no arrastrará consigo a toda la aplicación. Pero eso
no quiere decir que obtenga resistencia a cambio de nada. Deberá seguir los procedimientos
recomendados y los patrones de diseño para poder beneficiarse de dicha resistencia. Vea Diseño de
aplicaciones resistentes para Azure.

●● Pilas de tecnología mixta. Los equipos pueden elegir la tecnología que mejor se adapte a su servicio.

●● Escalado pormenorizado. Los servicios pueden escalarse de forma independiente. Al mismo tiempo,
existe una mayor densidad de servicios por máquina virtual, lo que significa que los recursos de la
máquina virtual se aprovechan plenamente. Las restricciones de emplazamiento permiten emparejar
un servicio con un perfil de máquina virtual (uso elevado de la CPU, uso elevado de la memoria y así
sucesivamente).

Dificultades
●● Complejidad. Una aplicación de microservicios tiene más partes móviles que la aplicación monolítica
equivalente. Los servicios individuales son más simples, pero todo el sistema en conjunto es más
complejo.

●● Desarrollo y pruebas. El desarrollo sobre la base de las dependencias de los servicios requiere un
enfoque diferente. Las herramientas existentes no están diseñadas necesariamente para trabajar
con las dependencias de los servicios. Una refactorización que se extienda más allá de los límites de
servicio puede ser difícil. También resulta complicado comprobar las dependencias de los servicios,
especialmente si la aplicación evoluciona rápidamente.

●● Falta de gobernabilidad. El enfoque descentralizado de la construcción de microservicios tiene


ventajas, pero también puede provocar problemas. Puede terminar con una aplicación con tantos
lenguajes y marcos de trabajo diferentes que sea difícil de mantener. Puede ser útil establecer algunos
estándares para todo el proyecto, sin restringir demasiado la flexibilidad de los equipos. Esto se aplica
especialmente a las funcionalidades transversales, como el registro.

●● Congestión de la red y latencia. La utilización de muchos servicios pequeños y pormenorizados


puede dar como resultado una mayor comunicación entre servicios. Además, si la cadena de
dependencias de los servicios acaba siendo demasiado larga (el servicio A llama a B, que llama a C...),
la latencia adicional puede llegar a ser un problema. Deberá diseñar las API cuidadosamente. Evite las
API demasiado "locuaces", plantéese el uso de formatos de serialización y busque lugares en los que
pueda usar patrones de comunicación asincrónica.

●● Integridad de los datos. Con cada microservicio responsable de su propia persistencia de datos.
Como resultado, puede ser complicado garantizar la consistencia de los datos. Adopte la coherencia
entre eventos siempre que sea posible.

●● Administración. Tener éxito con los microservicios requiere una cultura de DevOps madura.
El registro correlacionado entre servicios puede ser complicado. Por lo general, el registro debe
correlacionar múltiples llamadas de servicio para una operación de un solo usuario.

●● Control de versiones. Las actualizaciones de un servicio no deben romper los servicios que
dependen de él. En cualquier momento podrían actualizarse múltiples servicios, por lo que sin
un diseño cuidadoso podría tener problemas de compatibilidad hacia delante o hacia atrás.

●● Conjunto de habilidades. Los microservicios son sistemas altamente distribuidos. Evalúe con
atención si el equipo cuenta con las habilidades y la experiencia para tener éxito.

16 CAPÍTULO 1c | Estilo de arquitectura de microservicios


Procedimientos recomendados
●● Modele los servicios alrededor del dominio empresarial.

●● Descentralícelo todo. Los equipos individuales son responsables del diseño y la construcción de
servicios. Evite compartir esquemas de código o datos.

●● El almacenamiento de los datos debe ser privado para el servicio que posee los datos. Utilice el mejor
almacenamiento para cada tipo de servicio y de datos.

●● Los servicios se comunican a través de API bien diseñadas. Evite la filtración de detalles de la
implementación. Las API deben modelar el dominio, no la implementación interna del servicio.

●● Evite el acoplamiento entre servicios. Las causas de acoplamiento incluyen protocolos de


comunicación rígidos y esquemas de base de datos compartidos.

●● Descargue las cuestiones de transversalidad, tales como autenticación y terminación SSL, a la puerta
de enlace.

●● Mantenga el conocimiento del dominio fuera de la puerta de enlace. La puerta de enlace debe
manejar y enrutar las solicitudes del cliente, sin conocimiento de las reglas de negocio o la lógica de
dominio. De lo contrario, la puerta de enlace se convierte en una dependencia y puede provocar el
acoplamiento entre servicios.

●● Los servicios deben contar con un acoplamiento flexible y una alta cohesión funcional. Las funciones
que pueden cambiar juntas deben empaquetarse e implementarse juntas. Si residen en diferentes
servicios, dichos servicios terminan asociados entre sí de una forma muy estrecha porque un cambio
en un servicio requerirá la actualización del otro servicio. Una comunicación demasiado locuaz entre
dos servicios puede ser un síntoma de baja cohesión y emparejamiento inflexible.

●● Aísle los errores Utilice estrategias de resistencia para evitar errores dentro de un servicio derivados de
un efecto en cascada. Vea el diseño de aplicaciones resistentes.

Para acceder a una lista y resumen de los patrones de resistencia disponibles en Azure, acceda a
https://docs.microsoft.com/en-us/azure/architecture/patterns/category/resiliency.

17 CAPÍTULO 1c | Estilo de arquitectura de microservicios


Microservicios a través de Azure Container
Service
Puede utilizar Azure Container Service para configurar y aprovisionar un clúster de Docker. Azure
Container Services es compatible con varios organizadores de contenedores populares, incluyendo
Kubernetes, DC/OS y Docker Swarm.

Nodos públicos. Estos nodos son accesibles a través de un equilibrador de cargas público. La puerta de
enlace de API se aloja en estos nodos.

Nodos de back-end. En estos nodos se ejecutan servicios a los que los clientes acceden a través de la
puerta de enlace de API. Estos nodos no reciben directamente el tráfico de Internet. Los nodos de back-end
pueden incluir más de una agrupación de máquinas virtuales, cada una con un perfil de hardware diferente.
Por ejemplo, podría crear agrupaciones separadas para cargas de trabajo de procesos generales, cargas de
trabajo que requieran un consumo intensivo de la CPU y cargas de trabajo que hagan un uso intensivo de la
memoria.

Administración de máquinas virtuales. En estas máquinas virtuales se ejecutan los nodos maestros para
el organizador de contenedores.

Redes. Los nodos públicos, los nodos de back-end y las máquinas virtuales de administración se ubican en
subredes independientes dentro de la misma red virtual (VNet).

Equilibradores de cargas. Un equilibrador de cargas externo se ubica delante de los nodos públicos.
Distribuye las solicitudes de Internet a los nodos públicos. Otro equilibrador de cargas se coloca delante
de las máquinas virtuales de administración para permitir el tráfico de shell seguro (ssh) a las máquinas
virtuales de administración utilizando reglas de NAT.

Para mayor fiabilidad y escalabilidad, cada servicio se replica en múltiples máquinas virtuales. Puesto
que los servicios son también relativamente ligeros (en comparación con una aplicación monolítica),
normalmente se agrupan múltiples servicios en una única máquina virtual. Una mayor densidad permite
una mejor utilización de los recursos. Si un determinado servicio no usa muchos recursos, no es necesario
que dedique una máquina virtual completa a la ejecución de dicho servicio.

18 CAPÍTULO 1c | Estilo de arquitectura de microservicios


El siguiente diagrama muestra tres nodos en el que se ejecutan cuatro servicios diferentes (se indican
mediante formas diferentes). Observe que cada servicio tiene al menos dos instancias.

Microservicios mediante Azure Service Fabric


El diagrama siguiente muestra una arquitectura de microservicios a través de Azure Service Fabric.

El clúster Service Fabric se implementa en uno o más conjuntos de escalado de máquinas virtuales. Puede
tener más de un conjunto de escalado de máquina virtual en el clúster para tener una combinación de
tipos de máquinas virtuales. Una puerta de enlace de API se coloca delante del clúster Service Fabric, con
un equilibrador de cargas externo para recibir las solicitudes del cliente.

El tiempo de ejecución Service Fabric se ocupa de la administración del clúster, incluyendo la colocación
de servicios, la conmutación por error en nodos y la supervisión del estado. El tiempo de ejecución se
implementa en los propios nodos del clúster. No existe un conjunto independiente de máquinas virtuales
de administración de clústeres.

Los servicios se comunican entre ellos utilizando el proxy inverso integrado en Service Fabric. Service
Fabric ofrece un servicio de detección que puede resolver el punto de conexión para un servicio
determinado.

19 CAPÍTULO 1c | Estilo de arquitectura de microservicios


1d

Estilo de arquitectura
CQRS
CQRS (Command and Query Responsibility Segregation) es un estilo de
arquitectura que separa las operaciones de lectura de las de escritura.

En las arquitecturas tradicionales, se utiliza el mismo modelo de datos para consultar y actualizar una base de
datos. Es un procedimiento sencillo y funciona bien para las operaciones básicas de CRUD. En aplicaciones
más complejas, pero este procedimiento puede llegar a ser difícil de manejar. Por ejemplo, en el lado de la
lectura, la aplicación puede realizar muchas consultas diferentes, devolviendo objetos de transferencia de
datos (DTO) con formas diferentes. La asignación de objetos puede llegar a ser complicada. En el lado de la
escritura, el modelo puede implementar una lógica validación y de negocio compleja. El resultado puede ser
un modelo excesivamente complejo que hace demasiado.

Otro problema potencial es que las cargas de trabajo de lectura y escritura a menudo son asimétricas, con
requerimientos de rendimiento y escala muy diferentes.

CQRS aborda estos problemas separando la lectura y la escritura en modelos diferentes mediante
comandos que actualizan los datos y consultas que los leen.

●● Los comandos deben estar basados en tareas, en lugar de centrarse en los datos. ("Reservar
habitación de hotel" en lugar de "establecer EstadoReserva en Reservado"). Los comandos pueden
colocarse en una cola para su procesamiento asincrónico, en lugar de procesarse sincrónicamente.

●● Las consultas nunca modifican la base de datos. Una consulta devuelve un DTO que no encapsula
ningún conocimiento del dominio.

20 CAPÍTULO 1d | Estilo de arquitectura CQRS


Para mayor aislamiento, puede separar físicamente los datos de lectura de los datos de escritura. En ese
caso, la base de datos de lectura puede utilizar su propio esquema de datos optimizado para consultas.
Por ejemplo, puede guardar una vista materializada de los datos para evitar uniones complejas o
asignaciones de O/RM complejas. Incluso podría utilizar un tipo de almacén de datos diferente. Por
ejemplo, la base de datos de escritura puede ser relacional y la base de datos de lectura puede ser una
base de datos de documentos.

Si se utilizan bases de datos de lectura y escritura separadas, deben mantenerse sincronizadas.


Normalmente esto se consigue haciendo que el modelo de escritura publique un evento cada vez
que actualice la base de datos. La actualización de la base de datos y la publicación del evento deben
producirse en una sola transacción.

Algunas implementaciones de CQRS utilizan el patrón de abastecimiento de eventos. Con este patrón, el
estado de la aplicación se almacena como una secuencia de eventos. Cada evento representa un conjunto de
cambios de los datos. El estado actual se construye mediante la reproducción de los eventos. En un contexto
CQRS, una de las ventajas del abastecimiento de eventos es que pueden utilizarse los mismos eventos para
notificar otros componentes, en particular, para notificar el modelo de lectura. El modelo de lectura utiliza los
eventos para crear una instantánea del estado actual, que es más eficiente para consultas. De todos modos,
el abastecimiento de eventos complica el diseño.

Cuándo utilizar esta arquitectura


Plantéese el uso de CQRS para dominios de colaboración en los que muchos usuarios acceden a los
mismos datos, especialmente cuando las cargas de trabajo de lectura y escritura son asimétricas.

CQRS no es una arquitectura de alto nivel válida para todo un sistema. Utilice CQRS solo en aquellos
subsistemas en los que la separación de la lectura y la escritura resulte en un beneficio claro. De lo
contrario, creará mayor complejidad y no obtendrá ningún beneficio.

Ventajas
●● Escalado independiente. CQRS permite el escalado independiente de las cargas de trabajo de lectura
y escritura y puede desencadenar menos contenciones de bloqueos.

●● Esquemas de datos optimizados. El lado de lectura puede utilizar un esquema que esté optimizado
para las consultas, mientras que el lado de la escritura utiliza un esquema optimizado para las
actualizaciones.

●● Seguridad. Es más fácil garantizar que solo las entidades de dominio adecuadas escriben en los datos.

21 CAPÍTULO 1d | Estilo de arquitectura CQRS


●● Separación de problemas. Separar la lectura y la escritura puede dar como resultado modelos más
fáciles de mantener y más flexibles. La mayor parte de la compleja lógica de negocio se encuentra en
el modelo de escritura. El modelo de lectura puede ser relativamente sencillo.
●● Consultas más sencillas. Si se guarda una vista materializada en la base de datos de lectura, la
aplicación puede evitar uniones complejas al realizar consultas.

Dificultades
●● Complejidad. La idea básica de CQRS es sencilla. Pero puede llevar a un diseño de aplicaciones más
complejo, especialmente si incluyen el patrón de abastecimiento de eventos.

●● Mensajería. Aunque CQRS no requiere mensajería, es habitual utilizar la mensajería para procesar
comandos y publicar eventos de actualización. En ese caso, la aplicación debe gestionar errores de
mensajes o mensajes duplicados.

●● Coherencia entre eventos. Si se separan las bases de datos de lectura y escritura, los datos de lectura
pueden quedar obsoletos.

Procedimientos recomendados
●● Para obtener más información sobre la implementación de CQRS, vaya a https://docs.microsoft.com/
en-us/azure/architecture/patterns/cqrs.
●● Para obtener información sobre cómo utilizar el patrón de abastecimiento de eventos para evitar
conflictos de actualización, vaya a https://docs.microsoft.com/en-us/azure/architecture/patterns/
event-sourcing.
●● Para obtener información sobre cómo utilizar el patrón de vista materializada para el modelo de
lectura, para optimizar el esquema para consultas, vaya a https://docs.microsoft.com/en-us/azure/
architecture/patterns/materialized-view.

CQRS en microservicios
CQRS puede ser especialmente útil en una arquitectura de microservicios. Uno de los principios de los
microservicios es que un servicio no puede acceder directamente al almacén de datos de otro servicio.

22 CAPÍTULO 1d | Estilo de arquitectura CQRS


En el siguiente diagrama, el servicio A escribe en un almacén de datos y el servicio B mantiene una vista
materializada de los datos. El servicio A publica un evento cada vez que escribe en el almacén de datos. El
servicio B se suscribe al evento.

23 CAPÍTULO 1d | Estilo de arquitectura CQRS


1e

Estilo de arquitectura
controlada por eventos
Una arquitectura controlada por eventos consiste en productores de
eventos que generan un flujo de eventos y consumidores de eventos
que atienden los eventos.

Los eventos se entregan prácticamente en tiempo real, por lo que los consumidores pueden
responder inmediatamente a los eventos que se producen. Los productores están desvinculados de los
consumidores, es decir un productor no sabe qué consumidores reciben los eventos. Los consumidores
también están desvinculados unos de otros y cada consumidor ve todos los eventos. Es diferente del
patrón de consumidores competitivos, en el que los consumidores extraen mensajes de una cola y cada
mensaje se procesa solo una vez (suponiendo que no hay errores). En algunos sistemas, tales como IoT,
es preciso ingerir eventos a volúmenes muy altos.

Una arquitectura controlada por eventos puede utilizar un modelo de publicación-suscripción (pub/sub)
o un modelo de flujo de eventos.

●● Pub/sub: la infraestructura de mensajería realiza un seguimiento de las suscripciones. Cuando se


publica un evento, envía el evento a todos los suscriptores. Tras la recepción de un evento, no puede
reproducirse y los suscriptores nuevos no ven el evento.

●● Streaming de eventos: los eventos se escriben en un registro. Los eventos se ordenan estrictamente
(dentro de una partición) y son duraderos. Los clientes no se suscriben a un flujo, sino que un cliente
puede leer desde cualquier parte del flujo. El cliente se ocupa de avanzar su posición en el flujo.
Eso significa que un cliente puede unirse en cualquier momento y puede reproducir eventos.

24 CAPÍTULO 1e | Estilo de arquitectura controlada por eventos


En el lado del consumidor, existen algunas variaciones comunes:

●● Procesamiento simple de eventos. Un evento desencadena inmediatamente una acción en el


consumidor. Por ejemplo, puede utilizar funciones de Azure con un desencadenador de Service Bus para
que se ejecute una función cada vez que se publique un mensaje en un tema de Service Bus.

●● Procesamiento complejo de eventos. Un consumidor procesa una serie de eventos, buscando


patrones en los datos del evento con una tecnología como Azure Stream Analytics o Apache Storm.
Por ejemplo, podría agregar lecturas desde un dispositivo incrustado a lo largo de una ventana
temporal y generar una notificación si la media móvil cruza cierto umbral.

●● Procesamiento de flujo de eventos. Utilice una plataforma de streaming de datos, como Azure
IoT Hub o Apache Kafka, como canalización para ingerir eventos y alimentarlos a procesadores de
flujos. Los procesadores de flujo actúan para procesar o transformar el flujo. Puede haber múltiples
procesadores de flujo para diferentes subsistemas de la aplicación. Este enfoque es adecuado para las
cargas de trabajo de IoT.

El origen de los eventos puede ser externo al sistema, como dispositivos físicos de una solución de IoT. En
ese caso, el sistema debe ser capaz de introducir los datos en el volumen y con la capacidad que requiere
el origen de datos.

En el diagrama lógico anterior, cada tipo de consumidor se muestra como un cuadro único. En la práctica,
es habitual tener varias instancias de un consumidor para evitar que se convierta en un punto único
de error del sistema. También pueden ser necesarias varias instancias para gestionar el volumen y la
frecuencia de eventos. Además, un único consumidor puede procesar eventos en varios subprocesos. Esto
puede crear problemas si los eventos deben procesarse en orden o deben procesarse exactamente una
vez. Vea Reducción de la coordinación.

Cuándo utilizar esta arquitectura


●● Múltiples subsistemas deben procesar los mismos eventos.
●● Procesamiento en tiempo real con tiempo de retardo mínimo.
●● Procesamiento de eventos complejo, como coincidencia de patrones o agregación a lo largo de
ventanas temporales.
●● Alto volumen y alta velocidad de datos, como IoT.

Ventajas
●● Los productores y consumidores se desvinculan.
●● No existen las integraciones punto a punto. Es fácil añadir nuevos consumidores en el sistema.
●● Los consumidores pueden responder a los eventos inmediatamente a medida que llegan.
●● Altamente escalable y distribuida.
●● Los subsistemas tienen vistas independientes del flujo de eventos.

Dificultades
●● Entrega garantizada. En algunos sistemas, especialmente en los escenarios de IoT, es crucial garantizar
que se entregan los eventos.

●● Procesamiento de eventos en orden o exactamente una vez. Cada tipo de consumidor normalmente
se ejecuta en múltiples instancias, para mayor resistencia y escalabilidad. Esto puede plantear una
dificultad si los eventos deben procesarse en orden (dentro de un tipo de consumidor) o si la lógica
de procesamiento no es idempotente.

25 CAPÍTULO 1e | Estilo de arquitectura controlada por eventos


Arquitectura IoT
Las arquitecturas controladas por eventos son fundamentales para las soluciones de IoT. El siguiente
diagrama muestra una posible arquitectura lógica para IoT. El diagrama hace hincapié en los componentes
de streaming de eventos de la arquitectura.

La puerta de enlace del cloud introduce eventos de dispositivo en el límite del cloud con un sistema de
mensajería de baja latencia.

Los dispositivos pueden enviar eventos directamente a la puerta de enlace del cloud o a través de
una puerta de enlace in situ. Una puerta de enlace in situ es un dispositivo o software especializado,
normalmente colocado con los dispositivos, que recibe eventos y los reenvía a la puerta de enlace del
cloud. La puerta de enlace in situ también puede preprocesar los eventos de dispositivos raw y realizar
funciones tales como filtración, agregación o transformación del protocolo.

Después de su introducción, los eventos pasan por uno o más procesadores de flujos que pueden
enrutar los datos (por ejemplo, al almacenamiento) o realizar el análisis y otros procesos.
A continuación encontrará una lista con algunos tipos comunes de procesamiento. (Esta lista no es
exhaustiva).

●● Escritura de datos de evento en almacenamiento en frío para archivo o análisis de lote.

●● Análisis de ruta de acceso activa, analizando el flujo de eventos en tiempo (casi) real, para detectar
anomalías, reconocer patrones a lo largo de ventanas temporales graduales o desencadenar alertas
cuando se produce una situación específica en el flujo.

●● Gestión de tipos especiales de mensajes que no son de telemetría procedentes de dispositivos tales
como notificaciones y alarmas.
Machine learning.
Los cuadros sombreados en gris muestran los componentes de un sistema IoT que no están directamente
relacionados con la transmisión de eventos, pero se incluyen para garantizar la integridad.
●● El registro de dispositivos es una base de datos de los dispositivos suministrados, que incluye los ID
del dispositivo y metadatos del dispositivo, tales como la ubicación.

●● La API de aprovisionamiento es una interfaz externa común para el aprovisionamiento y registro de


dispositivos nuevos.

●● Algunas soluciones de IoT permiten enviar mensajes de comando y control a dispositivos.


Esta sección presenta una visión de muy alto nivel de IoT con múltiples matices y dificultades que
cabe tener en cuenta. Para obtener más información y una arquitectura de referencia detallada, vaya a
https://azure.microsoft.com/en-us/updates/microsoft-azure-iot-reference-architecture-available/
(descarga PDF).

26 CAPÍTULO 1e | Estilo de arquitectura controlado por eventos


1f

Estilo de arquitectura
de Big Data
Una arquitectura de Big Data está diseñada para gestionar la
introducción, procesamiento y análisis de datos demasiado voluminosos
o demasiado complejos para los sistemas de base de datos tradicionales.

Las soluciones de Big Data generalmente involucran uno o más de los siguientes tipos de carga de trabajo:

●● Procesamiento por lotes de orígenes de Big Data en reposo.


●● Procesamiento en tiempo real de Big Data en movimiento.
●● Exploración interactiva de Big Data.
●● Análisis predictivo y machine learning.

La mayoría de arquitecturas de Big Data incluyen algunos de los siguientes componentes o todos ellos:

●● Orígenes de datos: todas las soluciones de Big Data comienzan con uno o más orígenes de datos.
Ejemplos:
• Almacenes de datos de aplicaciones, como bases de datos relacionales.
• Archivos estáticos producidos por aplicaciones, como archivos de registro de servidor web.
• Orígenes de datos en tiempo real, tales como dispositivos de IoT.

●● Almacenamiento de datos: los datos para las operaciones de procesamiento por lotes normalmente
se almacenan en un almacén de archivos distribuidos que puede contener grandes volúmenes de
archivos de gran tamaño en distintos formatos. Este tipo de almacén a menudo se denomina "lago de
datos". Las opciones para la implementación de este almacenamiento incluyen Azure Data Lake Store
o contenedores de blobs en Azure Storage.

27 CAPÍTULO 1f | Estilo de arquitectura de Big data


●● Procesamiento por lotes. Puesto que los conjuntos de datos son tan grandes, a menudo una
solución de Big Data debe procesar archivos de datos mediante trabajos por lotes de ejecución
prolongada para filtrar, agregar y preparar los datos de cualquier otro modo para el análisis. Estos
trabajos normalmente implican leer los archivos de origen, procesarlos y escribir la salida en archivos
nuevos. Las opciones incluyen ejecutar trabajos U-SQL en Azure Data Lake Analytics, utilizando
trabajos de asignación/reducción de Hive, Pig o personalizados en un clúster HDInsight Hadoop
o utilizando programas de Java, Scala o Python en un clúster HDInsight Spark.

●● Introducción de mensajes en tiempo real. Si la solución incluye orígenes en tiempo real, la


arquitectura debe incluir una manera de capturar y almacenar los mensajes en tiempo real para el
procesamiento de flujos. Podría tratarse de un almacén de datos sencillo, en el que los mensajes
entrantes se colocan en una carpeta para su procesamiento. Sin embargo, muchas soluciones necesitan
un almacén de introducción de mensajes que actúe como búfer de mensajes y permita escalar
horizontalmente el procesamiento, ofrezca una entrega fiable y otras semánticas de cola de mensajes.
Las opciones incluyen Azure Event Hubs, Azure IoT Hubs y Kafka.

●● Procesamiento de flujos. Después de capturar mensajes en tiempo real, la solución debe procesarlos
para filtrarlos, agregar y preparar de otros modos los datos para el análisis. Los datos de flujo
procesados se escriben entonces en un receptor de salida. Azure Stream Analytics proporciona un
servicio de procesamiento de flujo administrado basado en la ejecución perpetua de consultas SQL
que funcionan en flujos sin límites. También puede utilizar tecnologías Apache de streaming de código
abierto como Storm y Spark Streaming en un clúster HDInsight.

●● Almacén de datos analíticos. Muchas soluciones de Big Data preparan los datos para el análisis y sirven
estos datos procesados en un formato estructurado que se puede consultar utilizando herramientas
analíticas. El almacén de datos analíticos utilizado para servir estas consultas puede ser un almacén
de datos relacionales de estilo Kimball, como el de la mayoría de soluciones de Business intelligence
(BI) tradicionales. También es posible presentar los datos a través de una tecnología de NoSQL de
baja latencia como HBase o una base de datos Hive interactiva que proporciona una abstracción de
metadatos de los archivos de datos en el almacén de datos distribuidos. El almacenamiento de datos
SQL de Azure proporciona un servicio administrado para el almacenamiento de datos a gran escala,
basado en el cloud. HDInsight es compatible con Interactive Hive, HBase y Spark SQL, que también
pueden utilizarse para servir datos para el análisis.

●● Análisis y elaboración de informes. El objetivo de la mayoría de soluciones de Big Data es


proporcionar conocimiento sobre los datos a través del análisis y la elaboración informes. Para que
los usuarios puedan analizar los datos, la arquitectura puede incluir una capa de modelado de datos,
como un cubo OLAP multidimensional o un modelo de datos tabulares en Azure Analysis Services.
También puede ser compatible con el BI de autoservicio, utilizando las tecnologías de modelado
y visualización de Microsoft Power BI o Microsoft

●● Excel. El análisis y la elaboración de informes también pueden adoptar la forma de exploración


interactiva de los datos por parte de científicos o analistas de datos. En estos casos, muchos servicios
de Azure son compatibles con los cuadernos analíticos, tales como Jupyter, de forma que estos usuarios
pueden aprovechar sus conocimientos existentes con Python o R. Para la exploración de datos a gran
escala, puede utilizar Microsoft Server R, como sistema independiente o con Spark.

●● Coordinación. La mayoría de soluciones de Big Data consisten en operaciones de procesamiento de


datos repetidos, encapsuladas en flujos de trabajo, que transforman los datos de origen, mueven los
datos entre múltiples orígenes y receptores, cargan los datos procesados en un almacén de datos
analíticos o insertan los resultados directamente en un Informe o panel. Para automatizar estos flujos
de trabajo, puede utilizar una tecnología de coordinación como Azure Data Factory o Apache Oozie
y Sqoop.

Azure incluye muchos servicios que pueden utilizarse en una arquitectura de Big Data. Se engloban
básicamente en dos categorías:
●● Servicios administrados, incluyendo Azure Data Lake Store, Azure Data Lake Analytics, Azure Data
Warehouse, Azure Stream Analytics, Azure Event Hub, Azure IoT Hub y Azure Data Factory.

28 CAPÍTULO 1f | Estilo de arquitectura de Big data


●● Tecnologías de código abierto basadas en la plataforma Apache Hadoop, incluyendo HDFS, HBase,
Hive, Pig, Spark, Storm, Oozie, Sqoop y Kafka. Estas tecnologías están disponibles en Azure en el
servicio Azure HDInsight.

Estas opciones no son mutuamente excluyentes y muchas soluciones combinan tecnologías de código
abierto con los servicios de Azure.

Ventajas
●● Opciones de tecnología. Es posible mezclar y combinar servicios administrados de Azure y
tecnologías Apache en clústeres HDInsight para aprovechar los conocimientos existentes o las
inversiones en tecnología.

●● Rendimiento mediante el paralelismo. Las soluciones de Big Data aprovechan el paralelismo, que
permite soluciones de alto rendimiento que se adaptan a grandes volúmenes de datos.

●● Escala elástica. Todos los componentes de la arquitectura de Big Data admiten el aprovisionamiento
de escalado horizontal de forma que pueda ajustar su solución a cargas de trabajo pequeñas
o grandes y pagar solo por los recursos que utiliza.

●● Interoperabilidad con las soluciones existentes. Los componentes de la arquitectura de Big data
también se utilizan para soluciones de procesamiento de IoT y BI empresarial, y permiten crear una
solución integrada a través de cargas de trabajo de datos.

Dificultades
●● Complejidad. Las soluciones de Big data pueden ser extremadamente complejas, con numerosos
componentes para controlar la ingestión de datos procedentes de múltiples orígenes de datos.
Construir, probar y solucionar problemas de procesos de Big Data puede ser todo un reto. Por otra
parte, puede haber un gran número de opciones de configuración en múltiples sistemas que deberán
utilizarse para optimizar el rendimiento.

●● Conjunto de habilidades. La mayoría de tecnologías de Big Data están altamente especializadas y


utilizan marcos de trabajo y lenguajes que no son habituales en las arquitecturas de aplicaciones más
generales. Por otra parte, las tecnologías de Big Data están evolucionando nuevas API que pueden
utilizar más lenguajes establecidos. Por ejemplo, el lenguaje U-SQL de Azure Data Lake Analytics se
basa en una combinación de Transact-SQL y C#. Asimismo, existen API basadas en SQL disponibles
para Hive, HBase y Spark.

●● Madurez de la tecnología. Muchas de las tecnologías que se usan en Big Data están evolucionando.
Aunque algunas tecnologías Hadoop tales como Hive y Pig se han estabilizado, las tecnologías
emergentes tales como Spark introducen grandes cambios y mejoras en cada nuevo lanzamiento.
Los servicios administrados como Azure Data Lake Analytics y Azure Data Factory son relativamente
jóvenes, en comparación con otros servicios de Azure y probablemente evolucionarán con el tiempo.

●● Seguridad. Las soluciones de Big Data generalmente se basan en almacenar todos los datos estáticos
en un data lake centralizado. Garantizar el acceso a estos datos puede ser difícil, especialmente
cuando hay múltiples plataformas y aplicaciones en las que ingerir y que consumen estos datos,

29 CAPÍTULO 1f | Estilo de arquitectura de Big data


Procedimientos recomendados
●● Aprovechar el paralelismo. La mayoría de tecnologías de procesamiento de Big data distribuyen la
carga de trabajo en múltiples unidades de procesamiento. Esto requiere la creación de archivos de
datos estáticos su almacenamiento en un formato divisible. Los sistemas de archivos distribuidos,
como HDFS, pueden optimizar el rendimiento de lectura y escritura y el proceso real se realiza
mediante varios nodos del clúster en paralelo, con lo que se reducen los tiempos de trabajo totales.

●● Datos de partición. El procesamiento por lotes normalmente se produce según un calendario


recurrente; por ejemplo, cada semana o cada mes. Los archivos de datos de la partición y las
estructuras de datos como las tablas, basadas en períodos temporales que coinciden con el calendario
de procesamiento. Esto simplifica la ingestión de datos y la programación de trabajos y facilita la
solución de errores. Además, la partición de tablas que se utilizan en Hive, U-SQL o consultas SQL
puede mejorar significativamente el rendimiento de las consultas.

●● Aplicación de la semántica de esquema de lectura. El uso de un data lake permite combinar el


almacenamiento de archivos en múltiples formatos, ya sean estructurados, semiestructurados o no
estructurados. Utilice la semántica de esquema de lectura, que proyecta un esquema sobre los datos
cuando se están procesando los datos, no cuando se almacenan. Esto aporta flexibilidad a la solución
y evita los cuellos de botella durante la ingestión de datos provocados por la validación de datos y la
comprobación de tipos.

●● Proceso de datos local. Las soluciones de BI tradicionales suelen utilizan un proceso de extracción,
transformación y carga (ETL) para trasladar los datos a un almacén de datos. Con datos de volúmenes
más grandes y una mayor variedad de formatos, las soluciones de Big Data generalmente utilizan
variaciones de ETL, como la transformación, extracción y carga (TEL). Con este enfoque, los datos se
procesan dentro del almacén de datos distribuidos, transformándolos en la estructura requerida antes
de transferir los datos transformados a un almacén de datos analíticos.

●● Equilibrio y costes temporales. Para los trabajos de procesamiento por lotes, es importante
considerar dos factores: el coste por unidad de los nodos de proceso y el coste por minuto del uso de
esos nodos para completar el trabajo. Por ejemplo, un trabajo por lotes puede tardar ocho horas con
cuatro nodos de clúster. Sin embargo, podría ser que el trabajo utilizara los cuatro nodos solo durante
las dos primeras horas y que después solo necesitara dos nodos. En ese caso, ejecutar todo el trabajo
en dos nodos aumentaría el tiempo total del trabajo, pero no lo multiplicaría por dos, por lo que el
coste total sería menor. En algunos escenarios empresariales, un tiempo de procesamiento más largo
puede ser preferible al mayor coste del uso de recursos de clúster infrautilizados.

●● Recursos de clústeres independientes. En la implementación de clústeres HDInsight, normalmente


obtendrá mejor rendimiento aprovisionando recursos de clústeres independientes para cada tipo
de carga de trabajo. Por ejemplo, aunque los clústeres Spark incluyen Hive, si necesita realizar un
procesamiento extenso con Hive y Spark, deberá considerar la implementación de clústeres Spark y
Hadoop dedicados independientes. Del mismo modo, si utiliza HBase y Storm para el procesamiento
de flujo de baja latencia e Hive para el procesamiento por lotes, deberá considerar clústeres separados
para Storm, HBase y Hadoop.

●● Organización de la ingestión de datos. En algunos casos, las aplicaciones empresariales existentes


pueden escribir archivos de datos para el procesamiento por lotes directamente en los contenedores
de blobs del almacenamiento de Azure, donde pueden ser consumidos por HDInsight o Azure Data
Lake Analytics. Sin embargo, a menudo tendrá que organizar la ingestión de los datos desde las
propias instalaciones o desde orígenes de datos externos al data lake. Utilice un flujo de datos o
canalización de coordinación, tales como los que ofrecen Azure Data Factory u Oozie, para hacerlo
de una manera predecible que permita la administración central.

●● Limpiar los datos confidenciales al principio. El flujo de trabajo de ingesta de datos debe limpiar
los datos confidenciales al principio del proceso, para evitar almacenarlos en el data lake.

30 CAPÍTULO 1f | Estilo de arquitectura de Big data


1g

Estilo de arquitectura
de Big Compute
El término Big Compute describe cargas de trabajo a gran escala que requieren
una gran cantidad de núcleos, en ocasiones centenares o miles de ellos. Entre
los escenarios se incluyen el procesamiento de imágenes, la dinámica de
fluidos, el modelado de riesgos financieros, la prospección petrolífera, el diseño
de medicamentos y el análisis de estrés de ingeniería, entre otros.

A continuación se presentan algunas de las características típicas de las aplicaciones Big Compute:

●● El trabajo se puede dividir en tareas discretas, que se pueden ejecutar en varios núcleos al mismo tiempo.

●● Cada tarea es finita. Acepta datos de entrada, hace algún procesamiento y produce una salida. Toda
la aplicación se ejecuta durante un periodo de tiempo finito (de minutos a días). Un patrón común
consiste en aprovisionar una gran cantidad de núcleos en un pico de demanda y reducir a cero una
vez que se completa la aplicación.

●● La aplicación no necesita estar activa ininterrumpidamente. Sin embargo, el sistema debe controlar los
errores de los nodos o los bloqueos de las aplicaciones.

●● En el caso de algunas aplicaciones, las tareas son independientes y se pueden ejecutar en paralelo.
En otros casos, las tareas están asociadas entre sí de forma estrecha, lo que significa que deben
interactuar o intercambiar resultados intermedios. En ese caso, considere la posibilidad de usar
tecnologías de red de alta velocidad, como InfiniBand, y el acceso directo a memoria remota (RDMA).

●● En función de la carga de trabajo, puede utilizar tamaños de máquina virtual de uso intensivo de
proceso (H16r, H16mr y A9).

31 CAPÍTULO 1g | Estilo de arquitectura de Big Compute


Cuándo utilizar esta arquitectura
●● Operaciones que hacen un uso intensivo de computación, como la simulación y los cálculos numéricos.

●● Simulaciones que hacen un uso intensivo de computación y se deben dividir en varias CPU en
diferentes equipos (de decenas a miles).

●● Simulaciones que requieren demasiada memoria para un equipo y deben dividirse en varios equipos.

●● Cálculos con tiempos de ejecución largos que tardan demasiado en completarse en un solo equipo.

●● Cálculos más pequeños que se deben ejecutar cientos o miles de veces, como las simulaciones de
Monte Carlo.

Ventajas
●● Alto rendimiento con procesamiento "incómodamente paralelo".

●● Puede aprovechar centenares o miles de núcleos de equipo para resolver más rápidamente los
problemas grandes.

●● Acceso a hardware de alto rendimiento especializado, con redes InfiniBand de alta velocidad dedicadas.

●● Pueden aprovisionar tantas máquinas virtuales como sean necesarias para realizar el trabajo y,
después, quitarlas.

Dificultades
●● Administrar la infraestructura de máquinas virtuales.

●● Administrar el volumen del cálculo numérico.

●● Aprovisionar miles de núcleos de un modo puntual.

●● En el caso de las tareas asociadas entre sí de forma estrecha, la adición de más núcleos puede tener
menos rendimiento. Tal vez deba experimentar para encontrar el número óptimo de núcleos.

32 CAPÍTULO 1g | Estilo de arquitectura de Big Compute


Big Compute con Azure Batch
Azure Batch es un servicio administrado para ejecutar aplicaciones de informática de alto rendimiento
(HPC) a gran escala.

Con Azure Batch, se configura un grupo de máquinas virtuales y se cargan las aplicaciones y los archivos
de datos. A continuación, el servicio Batch aprovisiona las máquinas virtuales, les asigna tareas, ejecuta
las tareas y supervisa el progreso. Batch puede escalar horizontalmente las máquinas virtuales como
respuesta a la carga de trabajo. Batch también proporciona programación de trabajos.

Big Compute en máquinas virtuales


Puede utilizar Microsoft HPC Pack para administrar un clúster de máquinas virtuales, así como para
programar y supervisar los trabajos HPC. Con este enfoque, debe aprovisionar y administrar las máquinas
virtuales y la infraestructura de red. Considere la posibilidad de utilizar este enfoque si tiene cargas de
trabajo HPC y desea trasladar todas o algunas de ellas a Azure. Puede trasladar el clúster HPC completo
a Azure o mantenerlo en sus instalaciones pero usar Azure para la capacidad de picos de demanda. Para
obtener más información, vea las soluciones Batch y HPC soluciones para las grandes cargas de trabajo.

HPC Pack implementado en Azure


En este escenario, el clúster de HPC se crea totalmente en Azure.

El nodo principal proporciona servicios de administración y de programación de trabajos al clúster. En el


caso de las tareas asociadas entre sí de forma estrecha, use una red RDMA que proporcione un ancho
de banda muy alto y comunicación de baja latencia entre las máquinas virtuales. Para obtener más
información, vea Implementar un clúster de HPC Pack 2016 en Azure.

33 CAPÍTULO 1g | Estilo de arquitectura de Big Compute


Expansión de un clúster de HPC en Azure
En este escenario, una organización ejecuta HPC Pack en sus instalaciones y usa las máquinas virtuales
de Azure para la capacidad de picos de demanda. El nodo principal del clúster está en las instalaciones.
ExpressRoute o la puerta de enlace de VPN conecta la red local con Azure VNet.

34 CAPÍTULO 1g | Estilo de arquitectura de Big Compute


2

Elección de las
tecnologías de proceso
y de almacén de datos
Elija las tecnologías adecuadas para las aplicaciones de Azure.
Al diseñar una solución para Azure, existen dos decisiones de tecnología que debe tomar al principio del
proceso de diseño, porque afectan a toda la arquitectura. Se trata de la elección de las tecnologías de
proceso y de almacén de datos.

La opción de proceso consiste en decidir el modelo de hosting para los recursos informáticos en los
que se ejecuta su aplicación. En un sentido más amplio, la decisión se toma entre IaaS (infraestructura
como servicio), PaaS (plataforma como servicio) o FaaS (funciones como servicio), y todas las opciones
intermedias. En Azure actualmente se puede elegir entre siete opciones de proceso principales. Para
elegir una, tenga en cuenta las características adecuadas y las limitaciones del servicio, la disponibilidad
y escalabilidad, el coste y las consideraciones de DevOps. Las tablas de comparación de esta sección le
ayudarán a reducir las opciones.

El almacén de datos incluye cualquier tipo de datos que su aplicación deba administrar, procesar y generar,
o que los usuarios deban crear. Los tipos más habituales son los datos de empresa, la memoria caché,
los datos de IoT, la telemetría y los datos de registro no estructurados, y las aplicaciones suelen contener
más de un tipo de datos. Los distintos tipos de datos tienen requisitos de procesamiento diferentes, y
debe elegir el almacén adecuado para cada tipo con el fin de obtener los mejores resultados. Algunas
tecnologías de almacén de datos admiten varios modelos de almacenamiento. Use la información de
esta sección para elegir primero qué modelo de almacenamiento es el más adecuado a sus requisitos.
Después, plantéese un determinado almacén de datos en esa categoría, en función de factores tales como
el conjunto de características, el coste y la facilidad de administración.

En esta sección de la Guía de arquitectura de aplicaciones se incluyen los temas siguientes:

●● En la información general de las opciones de proceso se presentan algunas consideraciones generales


para elegir un servicio de proceso en Azure.

●● En los criterios para elegir una opción de proceso se comparan servicios de proceso de Azure
específicos en diferentes ejes, incluido el modelo de hosting, DevOps, disponibilidad y escalabilidad.

35 CAPÍTULO 2 | Elección de las tecnologías de proceso y de almacén de datos


●● En la elección del almacén de datos adecuado se describen las categorías principales de las
tecnologías de almacén de datos, incluido RDBMS, almacén de pares claves-valor, base de datos de
documentos, base de datos de gráficos y otros.

●● En los criterios de comparación para elegir un almacén de datos se describen algunos de los factores
que se deben tener en cuenta al elegir un almacén de datos.

Para obtener más información sobre estas opciones de proceso, vaya a: https://docs.microsoft.com/
en-us/azure/#pivot=services.

36
2a

Información general de
las opciones de proceso
El término proceso se refiere al modelo de hosting de los recursos
informáticos en los que se ejecuta la aplicación.
En un extremo del espectro está la infraestructura como servicio (IaaS). Con IaaS, se aprovisionan
las máquinas virtuales necesarias, junto con los componentes de red y de almacenamiento asociados.
Después, se implementan el software y las aplicaciones que se desean en esas máquinas virtuales. Este
modelo es el más parecido a un entorno local tradicional, con la excepción de que Microsoft administra la
infraestructura. El usuario sigue administrando las máquinas virtuales individuales.

La plataforma como servicio (PaaS) proporciona un entorno de hosting administrado, donde puede
implementar su aplicación sin necesidad de administrar máquinas virtuales o recursos de red. Por ejemplo,
en lugar de crear máquinas virtuales individuales, puede especificar un recuento de instancias y el servicio
aprovisionará, configurará y administrará los recursos necesarios. Azure App Service es un ejemplo de
servicio PaaS.

Hay varias alternativas intermedias entre IaaS y PaaS puro. Por ejemplo, las máquinas virtuales de Azure se
pueden escalar automáticamente mediante conjuntos de escalado de máquinas virtuales. Esta capacidad
de escalado automático no es estrictamente PaaS, pero es el tipo de característica de administración que
se podría encontrar en un servicio PaaS.

Las funciones como servicio (FaaS) van más lejos en la despreocupación por el entorno de hosting. En
lugar de crear calcular instancias de proceso e implementar código en ellas, simplemente se implementa
el código y el servicio lo ejecuta automáticamente. No es necesario administrar los recursos de proceso.
Estos servicios utilizan una arquitectura sin servidor y se amplían o reducen sin problemas al nivel que sea
necesario para controlar el tráfico. Azure Functions es un servicio FaaS.

IaaS ofrece más control, flexibilidad y portabilidad. FaaS proporciona simplicidad, escalado elástico
y posibilidad de ahorro, porque solo se paga por el tiempo que se ejecuta el código.

PaaS se encuentra en medio de los dos. En general, cuanta más flexibilidad proporciona un servicio, más
responsabilidad tiene el usuario para configurar y administrar los recursos. Los servicios FaaS administran
de forma automática prácticamente todos los aspectos de la ejecución de una aplicación, mientras
que las soluciones IaaS necesitan que aprovisione, configure y administre las máquinas virtuales y los
componentes de red que ha creado.

A continuación se presentan las opciones de proceso principales que están disponibles en Azure:

●● Virtual Machines es un servicio IaaS, que le permite implementar y administrar las máquinas virtuales
en una red virtual (VNet).

37 CAPÍTULO 2a | Información general de las opciones de proceso


●● App Service es un servicio administrado para el hosting de aplicaciones web, back-end de aplicaciones
móviles, API compatibles con REST o procesos de negocio automatizados.

●● Service Fabric es una plataforma de sistemas distribuidos que se puede ejecutar en numerosos
entornos, incluido Azure o de forma local. Service Fabric es un organizador de microservicios en un
clúster de máquinas.

●● Azure Container Service permite crear, configurar y administrar un clúster de máquinas virtuales que
están preconfiguradas para ejecutar aplicaciones de contenedor.

●● Azure Functions es un servicio FaaS administrado.

●● Azure Batch es un servicio administrado para ejecutar aplicaciones de informática de alto rendimiento
(HPC) en paralelo y a gran escala.

●● Cloud Services es un servicio administrado para ejecutar aplicaciones en el cloud. Usa un modelo de
hosting de PaaS.

Al seleccionar una opción de proceso, le indicamos algunos factores que debe tener en cuenta:

●● Modelo de hosting. ¿Cómo se hospeda el servicio? ¿Qué requisitos y limitaciones impone este
entorno de hosting?

●● DevOps. ¿Está integrada la compatibilidad con las actualizaciones de las aplicaciones? ¿Cuál es el
modelo de implementación?

●● Escalabilidad. ¿Cómo controla el servicio la adición o eliminación de instancias? ¿Se puede escalar
automáticamente según la carga y otras métricas?

●● Disponibilidad. ¿Cuál es el SLA del servicio?

●● Coste. Además del coste del propio servicio, tenga en cuenta el coste operativo para administrar una
solución basada en ese servicio. Por ejemplo, soluciones IaaS podrían tener un mayor coste operativo.

●● ¿Cuáles son las limitaciones generales de cada servicio?

●● ¿Qué tipo de arquitecturas de aplicación son adecuadas para este servicio?

38
2b

Comparación de
procesos
El término "proceso" se refiere al modelo de hosting de los recursos
informáticos sobre los que se ejecutan las aplicaciones. En las tablas siguientes
se comparan los servicios de proceso de Azure en varios ejes. Consúltelas
cuando vaya a seleccionar una opción de proceso para su aplicación.

Modelo de hosting
Azure
Virtual Azure Cloud
Criterios App Service Service Fabric Container Azure Batch
Machines Functions Services
Services

Servicios,
Composición Trabajos
Independiente Aplicaciones ejecutables de Funciones Contenedores Roles
de aplicaciones programados
invitado

Varias
Una
aplicaciones Varios Varios
Varios servicios instancia
por instancia Sin instancias contenedores contenedores
Densidad Independiente por máquina de rol por
mediante dedicadas por máquina por máquina
virtual máquina
planes de virtual virtual
virtual
aplicación

Número 2 3 Sin nodos 4


mínimo de 1 1 5 1 3 2 1
dedicados
nodos

Administración Sin estado o Sin estado o con Sin estado o


Sin estado Sin estado Sin estado Sin estado
de estados con estado estado con estado

Host propio, IIS Integrado


Hosting web Independiente Integrado N/D Independiente No
en contenedores (IIS)

Windows, Windows, Windows, Linux Windows, Windows,


SO N/D Windows
Linux Linux (preview) (preview) Linux Linux

¿Se puede
implementar en
Admitido Admitido Admitido No admitido Admitido Admitido Admitido
una red virtual 6
dedicada?

Conectividad
Admitido Admitido Admitido No admitido Admitido Admitido Admitido
híbrida 8

39 CAPÍTULO 2b | Comparación de procesos


Notas:
1. Si usa un plan de App Service, las funciones se ejecutan en las máquinas virtuales asignadas al
plan de App Service. Para obtener más información, vaya a https://docs.microsoft.com/en-us/
azure/azure-functions/functions-scale.
2. SLA más alto con dos o más instancias.
3. Para entornos de producción.
4. Se puede reducir a cero después de que se complete el trabajo.
5. Requiere el entorno del Servicio de aplicaciones (ASE).
6. Solo red virtual clásica.
7. Requiere ASE o conexiones híbridas de BizTalk.
8. Red virtual clásica o red virtual de administrador de recursos a través de conexiones entre
pares de red virtual.

DevOps
Azure
Virtual Cloud Azure
Criterios App Service Service Fabric Azure Functions Container
Machines Services Batch
Services

Tiempo de
Depuración IIS Express, Clúster de CLI de Azure ejecución de Emulador No
Independiente
local otros nodos locales Functions contenedor local admitido
local
Ejecutable
Aplicación web,
de invitado, Aplicación
trabajos web
Modelo de modelo de Funciones con Rol web, rol de la
Independiente para las tareas Independiente
programación servicio, modelo desencadenadores de trabajo línea de
en segundo
de actores, comandos
plano
contenedores

Administrador 2
Admitido Admitido Admitido Admitido Admitido Limitado Admitido
de recursos

Sin Actualización VIP Swap o


Actualización Ranuras de Sin compatibilidad Depende del
compatibilidad gradual (por actualización N/D
de aplicación implementación integrada organizador.
integrada servicio) gradual

Notas:

1. Entre las opciones se incluyen IIS Express para ASP.NET o node.js (iisnode), servidor web PHP,
kit de herramientas de Azure para IntelliJ y kit de herramientas de Azure para Eclipse. App
Service también admite la depuración remota de la aplicación web implementada.

2. Para obtener información, vaya a https://docs.microsoft.com/en-us/azure/


azure-resource-manager/resource-manager-supported-services.

40 CAPÍTULO 2b | Comparación de procesos


Escalabilidad
Azure
Service Azure Cloud
Criterios Virtual Machines App Service Container Azure Batch
Fabric Functions Services
Services
Conjuntos Conjuntos
Escalado de escalado Servicio de escalado Servicio Servicio
No admitido N/D
automático de máquinas integrado de máquinas integrado integrado
virtuales virtuales

Equilibrador Equilibrador
Equilibrador Equilibrador de Equilibrador de
Integrado de carga de Integrado de carga de Integrado
de carga carga de Azure carga de Azure
Azure Azure

Límite de 20
Imagen de
20 núcleos de forma
plataforma:
instancias, Sin límite predeterminada.
1000 nodos por
Límite de 50 con el 100 nodos 1 definido, Póngase en
VMSS, imagen Infinito 100
escala Entorno del por VMSS 200 máximo contacto con el
personalizada:
Servicio de recomendado servicio al cliente
100 nodos por
aplicaciones para solicitar un
VMSS
aumento.

Notas:

1. Para obtener más información, vaya a https://docs.microsoft.com/en-us/azure/azure-functions/


functions-scale.

Disponibilidad
Azure
Virtual Azure Cloud
Criterios App Service Service Fabric Container Azure Batch
Machines Functions Services
Services

SLA para SLA para Azure SLA para


SLA para App SLA para SLA para SLA para
SLA Virtual Container Cloud
Service Service Fabric Functions Azure Batch
Machines Service Services

Conmutación Administrador
por error Administrador Administrador de tráfico, No Administrador Administrador
No admitido
de varias de tráfico de tráfico clúster de varias admitido de tráfico de tráfico
regiones regiones

Notas:


1. Para obtener más información sobre SLA específicos, vaya a https://azure.microsoft.com/
en-us/support/legal/sla/.

41 CAPÍTULO 2b | Comparación de procesos


Seguridad
Azure
Virtual Service Azure Cloud Azure
Criterios App Service Container
Machines Fabric Functions Services Batch
Services

Configurado Configurado
SSL en la máquina Admitido Admitido Admitido en la máquina Admitido Admitido
virtual virtual

RBAC Admitido Admitido Admitido Admitido Admitido No admitido Admitido

Otros
Azure
Virtual Service Cloud Azure
Criterios App Service Azure Functions Container
Machines Fabric Services Batch
Services

Precios de
Precios de
Windows, Precios de Precios de Azure Precios de
Coste Service Admitido
Linux App service Azure Functions Container Cloud Services
Fabric
Service
Estilos
de Microservicios, Web-Queue Big
Admitido Admitido Admitido Admitido
arquitectura EDA Worker Compute
adecuados

Notas:

1. Para obtener información sobre el coste específico, vaya a https://azure.microsoft.com/


pricing/details/.

42 CAPÍTULO 2b | Comparación de procesos


2c

Información general
de almacén de datos
Elegir el almacén de datos correcto.
Los sistemas empresariales modernos administran volúmenes de datos cada vez más grandes. Los datos
se pueden obtener de servicios externos, los puede generar el sistema o los pueden crear los usuarios.
Estos conjuntos de datos pueden tener características y requisitos de procesamiento extremadamente
variados. Las empresas usan datos para evaluar tendencias, desencadenar procesos de negocio, auditar
sus operaciones, analizar el comportamiento de los clientes y mucho más.

Esta heterogeneidad implica que un solo almacén de datos normalmente no es el mejor planteamiento. En
su lugar, suele ser mejor guardar los diferentes tipos de datos en almacenes de datos distintos, cada uno
centrado en una carga de trabajo o patrón de uso específicos. El término "persistencia políglota" se usa
para describir las soluciones que usan una combinación de tecnologías de almacén de datos.

La selección del almacén de datos adecuado a los requisitos es una decisión de diseño clave. Hay
literalmente cientos de implementaciones para elegir entre las bases de datos SQL y NoSQL. Los
almacenes de datos se suelen clasificar por el modo en que estructuran los datos y los tipos de
operaciones que admiten. En este artículo se describen varios de los modelos de almacenamiento más
habituales. Tenga en cuenta que una determinada tecnología de almacén de datos puede admitir varios
modelos de almacenamiento. Por ejemplo, un sistema de administración de bases de datos relacionales
(RDBMS) también puede admitir un almacenamiento de pares clave-valor o gráfico. De hecho, existe una
tendencia general para la compatibilidad multimodelo, en la que un solo sistema de base de datos admite
varios modelos. Pero sigue siendo útil conocer los diferentes modelos a grandes rasgos.

No todos los almacenes de datos de una categoría determinada proporcionan el mismo conjunto de
características. La mayoría de los almacenes de datos proporcionan funcionalidad del lado servidor
para consultar y procesar los datos. En ocasiones, esta funcionalidad está integrada en el motor
de almacenamiento de datos. En otros casos, el almacenamiento de datos y las capacidades de
procesamiento están separadas, y puede haber varias opciones de procesamiento y de análisis.
Los almacenes de datos también admiten diferentes interfaces de programación y administración.

Por lo general, debe empezar por analizar qué modelo de almacenamiento resulta más adecuado según
sus requisitos. Después, plantéese un determinado almacén de datos en esa categoría, en función de
factores tales como el conjunto de características, el coste y la facilidad de administración.

43 CAPÍTULO 2c | Información general de almacén de datos


Sistemas de administración de bases de datos relacionales
Las bases de datos relacionales organizan los datos como una serie de tablas bidimensionales con filas y
columnas. Cada tabla tiene sus propias columnas y cada fila de una tabla tiene el mismo conjunto de columnas.
Este modelo tiene base matemática y la mayoría de los proveedores ofrecen un dialecto del lenguaje de
consulta estructurado (SQL) para recuperar y administrar los datos. Un RDBMS normalmente implementa un
mecanismo de coherencia transaccional que cumple el modelo ACID (del inglés atomic, consistent, isolated
y durable, que significa atómico, coherente, aislado y duradero) para actualizar la información.

Un RDBMS normalmente admiten un modelo de esquema de escritura, por el que la estructura de datos
se define con anterioridad y todas las operaciones de lectura o escritura deben usar el esquema. Por el
contrario, en la mayoría de los almacenes de datos NoSQL, en concreto los de tipo de pares clave-valor, el
modelo de esquema de lectura supone que el cliente impondrá su propio esquema de interpretación en
los datos procedentes de la base de datos y es independiente del formato de datos que se va a escribir.

Un RDBMS es muy útil cuando es importante la garantía de una coherencia sólida, en la que todos los
cambios son atómicos y las transacciones siempre dejan los datos en estado coherente. Sin embargo,
las estructuras subyacentes no se prestan al escalado mediante la distribución del almacenamiento
y el procesamiento en varios equipos. Además, la información almacenada en un RDBMS debe tener
una estructura relacional según el proceso de normalización. Aunque este proceso se entiende bien,
puede provocar ineficiencias debido a la necesidad de separar las entidades lógicas en filas de tablas
independientes y, a continuación, volver a combinar los datos al ejecutar consultas.

Servicio relevante de Azure:

●● Azure SQL Database. Para obtener información, vaya a https://azure.microsoft.com/services/sql-database.

●● Azure Database for MySQL. Para obtener información, vaya a https://azure.microsoft.com/services/mysql.

●● Azure Database for PostgreSQL. Para obtener información, vaya a https://azure.microsoft.com/services/


postgresql.

Almacenes de pares clave-valor


Un almacén de pares clave-valor es esencialmente una tabla hash grande. Cada valor de datos se asocia
a una clave única y el almacén de pares clave-valor usa esta clave para almacenar los datos mediante una
función de hash adecuada. La función de hash se selecciona para ofrecer una distribución uniforme de las
claves con hash en el almacenamiento de datos.

La mayoría de los almacenes de pares clave-valor solo admiten las operaciones simples de consulta,
inserción y eliminación. Para modificar un valor, tanto parcial como completamente, la aplicación debe
sobrescribir los datos existentes de todo el valor. En la mayoría de las implementaciones, la lectura o la
escritura de un solo valor es una operación atómica. Si el valor es grande, la escritura puede tardar cierto
tiempo.

Una aplicación puede almacenar datos arbitrarios como un conjunto de valores, aunque algunos almacenes
de pares clave-valor imponen límites en el tamaño máximo de los valores. Los valores almacenados son
opacos para el software de sistema de almacenamiento. La aplicación debe proporcionar e interpretar la
información de esquema. Esencialmente, los valores son blobs y el almacén de pares clave-valor se limita
a recuperar o almacenar el valor por clave.

44 CAPÍTULO 2c | Información general de almacén de datos


Los almacenes de pares clave-valor están muy optimizados para efectuar búsquedas simples, pero resultan
menos adecuados para sistemas que deban consultar datos en diferentes almacenes de pares clave-valor.
Los almacenes de pares clave-valor no están optimizados para los escenarios en los que la consulta por
valor es importante, en vez de realizar búsquedas basadas solo en las claves. Por ejemplo, con una base
de datos relacional se puede encontrar un registro con una cláusula WHERE, pero los almacenes de pares
clave-valor no suelen tener este tipo de capacidad de búsqueda para valores.

Un almacén de pares clave-valor puede ser muy escalable, ya que puede distribuir fácilmente los datos en
varios nodos en máquinas independientes.

Servicios relevantes de Azure:

●● Cosmos DB. Para obtener información, vaya a https://azure.microsoft.com/services/cosmos-db.


●● Azure Redis Cache. Para obtener información, vaya a https://azure.microsoft.com/services/cache.

Bases de datos de documentos


El concepto de una base de datos de documentos es similar al de un almacén de pares clave-valor, con la
excepción de que almacena una colección de campos y datos con nombre (denominados documentos),
cada uno de los cuales pueden ser simples elementos escalares o elementos compuestos, como listas y
colecciones secundarias. Los datos de los campos de un documento se pueden codificar de varias formas,
incluido XML, YAML, JSON o BSON, o, incluso, se pueden almacenar como texto sin formato. A diferencia de
los almacenes de clave-valor, los campos de los documentos están expuestos al sistema de administración
de almacenamiento, lo que permite consultar y filtrar los datos por los valores de dichos campos.

Normalmente, un documento contiene todos los datos de una entidad. Los elementos que constituyen
una entidad son específicos de la aplicación. Por ejemplo, una entidad podría contener los datos de un
cliente, un pedido o una combinación de ambos. Un solo documento puede contener información que se
repartiría en varias tablas relacionales en un RDBMS.

Un almacén de documentos no requiere que todos los documentos tengan la misma estructura. Este
enfoque de formato libre proporciona una gran flexibilidad. Las aplicaciones pueden almacenar diferentes
datos en los documentos a medida que cambian los requisitos empresariales.

La aplicación puede recuperar los documentos mediante su clave. Se trata de un identificador único del
documento, al que se suele aplicar el algoritmo hash, como ayuda para distribuir los datos uniformemente.
Algunas bases de datos de documentos crean automáticamente la clave de documento. Otras permiten al
usuario especificar un atributo del documento que se usará como clave. La aplicación también puede consultar
los documentos según el valor de uno o varios campos. Algunas bases de datos de documentos admiten la
indexación para facilitar la búsqueda rápida de documentos en función de uno o varios campos indexados.

45 CAPÍTULO 2c | Información general de almacén de datos


Muchas bases de datos de documentos admiten actualizaciones directas, lo que permite que una
aplicación modifique los valores de campos específicos en un documento sin volver a escribirlo por
completo. Las operaciones de lectura y escritura en varios campos de un solo documento normalmente
son atómicas.
Servicio relevante de Azure: Cosmos DB

Bases de datos de gráficos


Una base de datos de gráficos almacena dos tipos de información: nodos y bordes. Los nodos se pueden
considerar entidades. Los bordes especifican las relaciones entre los nodos. Los nodos y los bordes
pueden tener propiedades que proporcionen información sobre ellos, de un modo similar a las columnas
de una tabla. Los bordes también pueden tener una dirección que indique la naturaleza de la relación.
La finalidad de una base de datos de gráficos es permitir que una aplicación realice de un modo eficiente
consultas que recorran la red de nodos y bordes, y analizar las relaciones entre las entidades. En el diagrama
siguiente se muestra la base de datos de personal de una organización estructurada como un gráfico.
Las entidades son empleados y departamentos, y los bordes indican las relaciones de subordinación y
el departamento en el que trabaja cada empleado. En este gráfico, las flechas de los bordes muestran la
dirección de las relaciones.

46 CAPÍTULO 2c | Información general de almacén de datos


Esta estructura simplifica las consultas del tipo "Buscar todos los empleados subordinados directos o
indirectos de Sara" o "¿Quién trabaja en el mismo departamento que Juan?". En el caso de gráficos con
muchas entidades y relaciones, se pueden efectuar análisis muy complejos con mucha rapidez. Muchas
bases de datos de gráficos proporcionan un lenguaje de consulta que puede utilizar para recorrer una red
de relaciones de un modo eficiente.

Servicio relevante de Azure: Cosmos DB. Para obtener información, vaya a https://azure.microsoft.com/
services/cosmos-db

Bases de datos de familias de columnas


Una base de datos de familias de columnas organiza los datos en filas y columnas. En su forma más
simple, una base de datos de familias de columnas puede parecerse mucho a una base de datos relacional,
al menos conceptualmente. La eficacia real de una base de datos de familias de columnas reside en su
enfoque desnormalizado de estructurar los datos dispersos.

Se puede considerar que una base de datos de familias de columnas contiene datos tabulares con filas
y columnas, pero las columnas están divididas en grupos denominados familias de columnas. Cada una
de estas familias contiene un conjunto de columnas que están relacionadas entre sí de forma lógica y se
suelen recuperar o manipular como una unidad. Otros datos a los que se accede por separado se pueden
almacenar en familias de columnas independientes. En una familia de columnas, las columnas nuevas se
pueden agregar dinámicamente y las filas pueden estar dispersas (es decir, no es necesario que una fila
tenga valores para cada columna).

En el diagrama siguiente se muestra un ejemplo con dos familias de columnas: Identidad e Información
de contacto. Los datos de una sola entidad tienen la misma clave de fila en cada familia de columnas.
Esta estructura, en la que las filas de un determinado objeto de una familia de columnas pueden variar
dinámicamente, es una ventaja importante del enfoque de familia de columnas y convierte a este tipo de
almacén de datos en una forma muy adecuada para almacenar datos volátiles estructurados.
A diferencia de un almacén de clave y valor o una base de datos de documentos, la mayoría de bases de
datos de familias de columnas almacenan los datos en el orden de la clave, en lugar de calcular el código
hash. Muchas implementaciones permiten crear índices para columnas específicas de una familia de columnas.
Los índices le permiten recuperar datos en función del valor de las columnas, en lugar de la clave de fila.

Las operaciones de lectura y escritura para una fila suelen ser atómicas, con una sola familia de columnas,
aunque algunas implementaciones proporcionan atomicidad para toda la fila y abarcan varias familias de
columnas.

Servicio relevante de Azure: HBase en HDInsight. Para obtener información, vaya a


https://azure.microsoft.com/services/cosmos-db

47 CAPÍTULO 2c | Información general de almacén de datos


Análisis de datos
Los almacenes de análisis de datos proporcionan soluciones de procesamiento masivo en paralelo para
ingerir, almacenar y analizar los datos. Estos datos se distribuyen entre varios servidores usando una
arquitectura "share-nothing" para maximizar la escalabilidad y reducir al mínimo las dependencias. Es poco
probable que los datos sean estáticos, por lo que estos almacenes deben ser capaces de manejar grandes
cantidades de información que llegan en formatos diferentes de diversos flujos, sin dejar de procesar
nuevas consultas.

Servicios relevantes de Azure:


●● SQL Data Warehouse
●● Azure Data Lake

Bases de datos de motores de búsqueda


Una base de datos de motores de búsqueda permite buscar información almacenada en almacenes de
datos externos y servicios. Una base de datos de motores de búsqueda puede utilizarse para indexar
grandes volúmenes de datos y proporciona acceso en tiempo casi real a estos índices. Aunque las bases
de datos de motores de búsqueda suelen considerarse un sinónimo de la web, muchos sistemas a gran
escala las usan para proporcionar capacidades de búsqueda estructurada y ad-hoc encima de sus propias
bases de datos.

Las características clave de una base de datos de motores de búsqueda son la capacidad de almacenar e
indexar información muy rápidamente y la capacidad de proporcionar tiempos de respuesta rápidos para
las solicitudes de búsqueda. Los índices pueden ser multidimensionales y muchos admiten las búsquedas
de texto libre en grandes volúmenes de datos de texto. La indexación puede realizarse utilizando un
modelo de extracción (pull), desencadenado por la base de datos del motor de búsqueda, o utilizar un
modelo de inserción (push), iniciado por el código de la aplicación externa.
La búsqueda puede ser exacta o difusa. Una búsqueda parcial encuentra documentos que coinciden
con un conjunto de términos y calcula su semejanza. Algunos motores de búsqueda también admiten el
análisis lingüístico que puede devolver coincidencias basándose en sinónimos, expansiones de género
(por ejemplo, asociar perros con animales domésticos) y derivados (asociar palabras con la misma raíz).

Servicio relevante de Azure: Búsqueda de Azure

Bases de datos de series temporales


Los datos de series temporales son un conjunto de valores organizados por tiempo y una base de datos
de series temporales es una base de datos que está optimizada para este tipo de datos. Las bases de
datos de series temporales deben ser compatibles con un número muy elevado de escrituras, pues por
lo general recogen grandes cantidades de datos en tiempo real de un gran número de orígenes. Las
actualizaciones son raras y las supresiones suelen realizarse como operaciones masivas. Aunque los
registros escritos en una base de datos de series temporales suelen ser pequeños, a menudo hay un gran
número de registros y el tamaño total de datos puede crecer rápidamente.

Las bases de datos de series temporales son adecuadas para almacenar datos de telemetría, como
sensores de IoT o contadores de aplicación/sistema.

Servicio relevante de Azure: Time Series Insights

48 CAPÍTULO 2c | Información general de almacén de datos


Almacenamiento de objetos
El almacenamiento de objetos está optimizado para almacenar y recuperar objetos binarios grandes
(imágenes, archivos, flujos de audio y vídeo, objetos de datos de aplicaciones grandes y documentos,
imágenes de disco de máquinas virtuales). Los objetos de estos tipos de almacén están compuestos por
datos almacenados, algunos metadatos y un ID único para acceder al objeto. Los almacenes de objetos
permiten la administración de cantidades extremadamente grandes de datos no estructurados.

Servicio relevante de Azure: Blob Storage

Archivos compartidos
En ocasiones, utilizar archivos planos simples puede ser el medio más eficaz de almacenar y recuperar
información. El uso compartido de archivos permite acceder a los archivos a través de una red. Con
mecanismos apropiados de seguridad y control de acceso concurrentes, el uso compartido de los datos
de esta manera permite habilitar servicios distribuidos para proporcionar acceso a los datos altamente
escalable para la realización de operaciones básicas y de bajo nivel, como solicitudes de lectura y escritura.

Servicio relevante de Azure: File Storage

49 CAPÍTULO 2c | Información general de almacén de datos


2d

Comparación de
almacenes de datos
Criterios para la elección de un almacén de datos
Azure es compatible con muchos tipos de soluciones de almacenamiento de datos, cada una de ellas
con características y capacidades diferentes. Este artículo describe los criterios de comparación que debe
utilizar al evaluar un almacén de datos. El objetivo es ayudarle a determinar qué tipo de almacenamiento
de datos puede cumplir los requisitos de su solución.

Consideraciones generales
Para iniciar la comparación, recoja tanta información como pueda sobre sus necesidades de datos, según
se indica a continuación. Esta información le ayudará a determinar qué tipos de almacenamiento de datos
se ajustan a sus necesidades.

Requisitos funcionales
●● Formato de datos. ¿Qué tipo de datos pretende almacenar? Habitualmente suelen ser datos
transaccionales, objetos JSON, telemetría, índices de búsqueda o archivos planos.

●● Tamaño de datos. ¿Cuál es el tamaño de las entidades que debe almacenar? ¿Estas entidades
tendrán que mantenerse como un solo documento o pueden dividirse en múltiples documentos,
tablas, colecciones y otros?

●● Escala y estructura. ¿Cuál es la cantidad total de capacidad de almacenamiento que necesita? ¿Prevé
particionar sus datos?

●● Relaciones de datos. ¿Sus datos deben ser compatibles con las relaciones de uno a muchos o de
muchos a muchos? ¿Las propias relaciones son una parte importante de los datos? ¿Necesita unir o
combinar de algún otro modo datos desde el interior del propio conjunto de datos o de conjuntos de
datos externos?

●● Modelo de coherencia. ¿Es importante que las actualizaciones hechas en un nodo aparezcan en
otros nodos antes de proseguir con otros cambios? ¿Puede aceptar la coherencia al cabo del tiempo?
¿Necesita garantías ACID para las transacciones?

●● Flexibilidad de esquema. ¿Qué tipo de esquemas aplicará a sus datos? ¿Utilizará un esquema fijo, un
enfoque de esquema en escritura o de esquema en lectura?

50 CAPÍTULO 2d | Comparación de almacenes de datos


●● Simultaneidad. ¿Qué tipo de mecanismo de simultaneidad quiere utilizar para actualizar y sincronizar
datos? ¿La aplicación lleva a cabo muchas actualizaciones que podrían entrar potencialmente en
conflicto? En caso afirmativo, quizás necesite un bloqueo de registros y un control de simultaneidad
pesimista. O bien, ¿puede admitir controles de simultaneidad optimistas? En ese caso, ¿basta con un
control de simultaneidad simple basado en marcas de tiempo o necesita la funcionalidad adicional de
control de simultaneidad multiversión?

●● Movimiento de datos. ¿La solución deberá realizar tareas de ETL para mover los datos a otros
almacenes o almacenes de datos?

●● Ciclo de vida de los datos. ¿Se trata de datos que se escriben una vez y se leen muchas veces?
¿Pueden trasladarse a un almacenamiento de acceso poco frecuente?

●● Otras características admitidas. ¿Necesita otras características específicas, como validación de


esquema, agregación, indexación, búsqueda de texto completo, MapReduce u otras capacidades de
consulta?

Requisitos no funcionales
●● Rendimiento y escalabilidad. ¿Cuáles son sus requisitos de rendimiento de datos? ¿Tiene requisitos
específicos en cuanto a las tasas de ingestión de datos y de procesamiento de datos? ¿Cuáles son los
tiempos de respuesta aceptables para consulta y agregación de los datos tras su ingestión? ¿A qué tamaño
necesitará escalar el almacén de datos? ¿Su carga de trabajo se basa más en la lectura o en la escritura?

●● Fiabilidad. ¿Qué SLA general necesita que se admita? ¿Qué nivel de tolerancia a errores necesita
proporcionar a los consumidores de datos? ¿Qué tipo de funcionalidad de copias de seguridad y
restauración necesita?

●● Replicación. ¿Sus datos necesitarán distribuirse entre múltiples réplicas o regiones? ¿Qué tipo de
funcionalidad de replicación de datos necesita?

●● Límites. Los límites de un almacén de datos en particular, ¿admitirán sus requisitos de escala, número
de conexiones y rendimiento?

Administración y coste
●● Servicio administrado. Cuando sea posible, utilice un servicio de datos administrados, a menos
que requiera funcionalidades específicas que solo se puedan encontrar en un almacén de datos
hospedados en IaaS.

●● Disponibilidad de regiones. En cuanto a servicios administrados, ¿el servicio está disponible en


todas las regiones de Azure? ¿Necesita su solución hospedarse en determinadas regiones de Azure?

●● Portabilidad. ¿Necesitará migrar los datos a centros de datos locales, centros de datos externos
u otros entornos de hosting en el cloud?

●● Concesión de licencias. ¿Tiene preferencia por un tipo de licencia de propiedad frente a una de OSS?
¿Hay otras restricciones externas sobre el tipo de licencia que puede utilizar?

●● Coste total. ¿Cuál es el coste total del uso del servicio en el marco de su solución? ¿Cuántas instancias
deberán ejecutarse para dar soporte a sus requisitos de disponibilidad y rendimiento? Considere los
costes de las operaciones en este cálculo. Una razón para preferir los servicios administrados es el
menor coste operativo.

●● Rentabilidad. ¿Puede crear particiones de sus datos para almacenarlos de forma más rentable? Por
ejemplo, ¿puede mover objetos grandes de una base de datos relacional cara a un almacén de objetos?

51 CAPÍTULO 2d | Comparación de almacenes de datos


Seguridad
●● Seguridad. ¿Qué tipo de cifrado necesita? ¿Necesita cifrado en reposo? ¿Qué mecanismo de
autenticación desea utilizar para conectarse a los datos?

●● Auditorías. ¿Qué tipo de registro de auditoría necesita generar?

●● Requisitos de redes. ¿Necesita restringir o administrar de otro modo el acceso a los datos desde
otros recursos de red? ¿Es necesario tener acceso a los datos solo desde el interior del entorno
de Azure? ¿Es necesario tener acceso a los datos desde direcciones IP o subredes específicas? ¿Es
necesario tener acceso a ellos desde aplicaciones o servicios hospedados localmente o en otros
centros de datos externos?

DevOps
●● Habilidades. ¿Existen lenguajes de programación, sistemas operativos u otra tecnología en particular
que su equipo domine especialmente? ¿Hay otros con los que sería difícil para su equipo trabajar?

●● Clientes. ¿Existe una buena compatibilidad con clientes de los lenguajes de desarrollo?

En las secciones siguientes se comparan varios modelos de almacenes de datos en términos de perfiles de
cargas de trabajo, tipos de datos y ejemplos de casos de uso.

Sistemas de administración de bases de datos


relacionales (RDBMS)
Carga de trabajo
●● Tanto la creación de nuevos registros como las actualizaciones de los datos existentes se producen
periódicamente.

●● Varias operaciones deben completarse en una sola transacción.

●● Requiere funciones de agregación para realizar la tabulación cruzada.

●● Se requiere una integración sólida con las herramientas de informes.

●● Las relaciones se aplican utilizando restricciones de base de datos.

●● Los índices se utilizan para optimizar el rendimiento de las consultas.

●● Permite tener acceso a subconjuntos específicos de datos.

Tipo de datos
●● Los datos están altamente normalizados.

●● Los esquemas de base de datos son necesarios y de obligado cumplimiento.

●● Relaciones de varios a varios entre las entidades de datos de la base de datos.

●● Las restricciones se definen en el esquema y se imponen a todos los datos de la base de datos.

●● Los datos requieren una elevada integridad. Los índices y las relaciones deben mantenerse con precisión.

52 CAPÍTULO 2d | Comparación de almacenes de datos


●● Los datos requieren una coherencia sólida. Las transacciones funcionan de una manera que garantiza
que todos los datos sean 100 % coherentes para todos los usuarios y procesos.

●● El tamaño de las entradas de datos individuales debe ser pequeño o mediano.

Ejemplos
●● Línea de negocio (gestión del capital humano, gestión de relaciones con clientes, planificación de
recursos empresariales)
●● Administración de inventarios
●● Base de datos de informes
●● Contabilidad
●● Administración de activos
●● Gestión de fondos
●● Gestión de pedidos

Bases de datos de documentos


Carga de trabajo
●● Uso general.

●● Las operaciones de inserción y actualización son comunes. Tanto la creación de nuevos registros como
las actualizaciones de los datos existentes se producen periódicamente.

●● No hay falta de correspondencia de la impedancia relacional de objeto. Los documentos pueden


adaptarse mejor a las estructuras de objetos utilizadas en el código de la aplicación.

●● La simultaneidad optimista se suele utilizar más.

●● Los datos deben modificarse y procesarse por la aplicación de consumo.

●● Los datos requieren índices de varios campos.

●● Los documentos individuales se recuperan y escriben como un solo bloque.

Tipo de datos
●● Los datos pueden administrarse de forma desnormalizada.
●● El tamaño de los datos de los documentos individuales es relativamente pequeño.
●● Cada tipo de documento puede utilizar su propio esquema.
●● Los documentos pueden incluir campos opcionales.
●● Los datos de documento son semiestructurados, lo que significa que los tipos de datos de cada
campo no están definidos de forma estricta.
●● Se admite la agregación de datos.

53 CAPÍTULO 2d | Comparación de almacenes de datos


Ejemplos
●● Catálogo de productos
●● Cuentas de usuario
●● Lista de materiales
●● Personalización
●● Gestión de contenidos
●● Datos de operaciones
●● Administración de inventarios
●● Datos de historial de transacciones
●● Vista materializada de otros almacenes NoSQL. Reemplaza la indexación de archivos/BLOB.

Almacenes de pares clave-valor


Carga de trabajo
●● Los datos se identifican y se obtiene acceso a ellos mediante una clave de id. único, como un
diccionario.
●● Gran escalabilidad.
●● No se requieren combinaciones, bloqueos ni uniones.
●● No se utilizan mecanismos de agregación.
●● No suelen utilizarse índices secundarios.

Tipo de datos
●● El tamaño de los datos tiende a ser grande.
●● Cada clave está asociada con un único valor, que es un BLOB de datos no administrados.
●● No hay ninguna aplicación de esquema.
●● No hay relaciones entre entidades.

Ejemplos
●● Almacenamiento en caché de datos
●● Administración de sesiones
●● Administración de preferencias y perfiles de usuario
●● Recomendación de productos y entrega de publicidad
●● Diccionarios

54 CAPÍTULO 2d | Comparación de almacenes de datos


Bases de datos de gráficos
Carga de trabajo
●● Las relaciones entre los elementos de datos son muy complejas, con muchos saltos entre los
elementos de datos relacionados.
●● Las relaciones entre los elementos de datos son dinámicas y cambian con el tiempo.
●● Las relaciones entre objetos son de primera categoría y no requieren atravesar claves externas ni
combinaciones.

Tipo de datos
●● Los datos se componen de nodos y relaciones.
●● Los nodos son similares a las filas de tabla o a los documentos JSON.
●● Las relaciones son igual de importantes que los nodos y se exponen directamente en el lenguaje de
consulta.
●● Los objetos compuestos, por ejemplo, una persona con varios números de teléfono, tienden a
dividirse en nodos aparte más pequeños, combinados con relaciones transitables.

Ejemplos
●● Organigramas
●● Gráficos sociales
●● Detección de fraudes
●● Análisis
●● Motores de recomendaciones

55 CAPÍTULO 2d | Comparación de almacenes de datos


Bases de datos de familias de columnas
Carga de trabajo
●● La mayoría de las bases de datos de familias de columnas realizan las operaciones de escritura muy
rápidamente.
●● Las operaciones de actualización y eliminación son poco frecuentes.
●● Diseñado para proporcionar un alto rendimiento y un acceso de baja latencia.
●● Es compatible con el acceso fácil de consulta a un conjunto determinado de campos en un registro
mucho más grande.
●● Gran escalabilidad.

Tipo de datos
●● Los datos se almacenan en tablas que constan de una columna de clave y una o más familias de
columnas.
●● Determinadas columnas pueden variar según filas individuales.
●● Se obtiene acceso a las celdas individuales mediante comandos Get y Put
●● Se devuelven varias filas con un comando Scan.

Ejemplos
●● Recomendaciones
●● Personalización
●● Datos de sensores
●● Telemetría
●● Mensajería
●● Análisis de redes sociales
●● Análisis web
●● Supervisión de la actividad
●● El tiempo y otros datos de series temporales

56 CAPÍTULO 2d | Comparación de almacenes de datos


Bases de datos de motores de búsqueda
Carga de trabajo
●● Indexación de datos de varios orígenes y servicios.
●● Las consultas son ad hoc y pueden ser complejas.
●● Requiere agregación.
●● Se requiere la búsqueda de texto completo.
●● Se requiere la consulta ad hoc de autoservicio.
●● Se requiere el análisis de datos con índice en todos los campos.

Tipo de datos
●● Semiestructurados o no estructurados
●● Texto
●● Texto con referencia a datos estructurados

Ejemplos
●● Catálogos de productos
●● Búsqueda de sitios
●● Registro
●● Análisis
●● Sitios de compras

Almacenamiento de datos
Carga de trabajo
●● Análisis de datos
●● BI empresarial

Tipo de datos
●● Datos históricos de varios orígenes.
●● Generalmente desnormalizados en un esquema "estrella" o "copo de nieve" que consiste en tablas de
hechos y dimensiones.
●● Generalmente se cargan con datos nuevos periódicamente.
●● Las tablas de dimensiones a menudo incluyen varias versiones históricas de una entidad, lo que se
denomina dimensión de variación lenta.

Ejemplos
●● Almacén de datos empresariales que proporciona datos para modelos analíticos, informes y paneles.

57 CAPÍTULO 2d | Comparación de almacenes de datos


Bases de datos de series temporales
Carga de trabajo
●● Una abrumadora proporción de las operaciones (95-99 %) son de escritura.
●● Los registros suelen anexarse secuencialmente en orden cronológico.
●● Las actualizaciones son escasas.
●● Las eliminaciones se producen de forma masiva y se realizan en registros o bloques contiguos.
●● Las solicitudes de lectura pueden ser mayores que la memoria disponible.
●● Es común que varias lecturas se produzcan simultáneamente.
●● Los datos se leen secuencialmente ya sea en orden cronológico ascendente o descendente.

Tipo de datos
●● Marca de tiempo que se utiliza como clave principal y mecanismo de ordenación.
●● Medidas de la entrada o descripciones de lo que representa la entrada.
●● Etiquetas que definen la información adicional sobre el tipo, el origen y otra información sobre la
entrada.

Ejemplos
●● Supervisión y telemetría de eventos.
●● Datos de sensores u otros datos de IoT.

Almacenamiento de objetos
Carga de trabajo
●● Identificado por clave.
●● Se puede tener acceso a los objetos de forma pública o privada.
●● El contenido suele ser un activo, como una hoja de cálculo, una imagen o un archivo de vídeo.
●● El contenido debe ser duradero (persistente) y externo a cualquier capa de aplicación o máquina virtual.

Tipo de datos
●● El tamaño de datos es grande.
●● Datos de blob.
●● El valor es opaco.

58 CAPÍTULO 2d | Comparación de almacenes de datos


Ejemplos
●● Imágenes, vídeos, documentos de Office, PDF
●● CSS, scripts, CSV
●● HTML estático, JSON
●● Archivos de registro y auditoría
●● Copias de seguridad de base de datos

Archivos compartidos
Carga de trabajo
●● Migración de aplicaciones existentes que interactúan con el sistema de archivos.
●● Requiere interfaz de SMB.

Tipo de datos
●● Archivos en un conjunto jerárquico de carpetas.
●● Accesible con bibliotecas estándar de E/S.

Ejemplos
●● Archivos heredados.
●● Contenido compartido accesible entre un número de máquinas virtuales o instancias de aplicación.

59 CAPÍTULO 2d | Comparación de almacenes de datos


3

Diseño de la
aplicación Azure:
principios de diseño
Ahora que ha elegido la arquitectura y las tecnologías de cálculo y
almacén de datos, está listo para comenzar a diseñar y construir la
aplicación en el cloud. Esta sección y las dos siguientes proporcionan
orientación y recursos para el diseño óptimo de aplicaciones para el cloud.

Esta sección describe los diez principios de diseño que debe tener en cuenta durante el desarrollo. Seguir
estos principios le ayudará a crear una aplicación más escalable, resistente y administrable.

60 CAPÍTULO 3 | Diseño de la aplicación Azure: principios de diseño


1. Diseñe para la recuperación automática. En un sistema distribuido se producen errores. Diseñe la
aplicación para que se recupere de forma automática cuando se produzcan errores.

2. Hacer que todo sea redundante. Cree redundancia en la aplicación para evitar puntos únicos de
error.

3. Minimice la coordinación. Minimice la coordinación entre los servicios de aplicación para obtener
escalabilidad.

4. Diseñe pensando en la escalabilidad. Diseñe la aplicación para que se pueda escalar


horizontalmente añadiendo o eliminando nuevas instancias según la demanda.

5. Cree particiones para evitar límites. Puede utilizar particiones para evitar los límites de bases de
datos, redes y procesos.

6. Diseñe para el equipo de operaciones. Diseñe la aplicación para que el equipo de operaciones
cuente con las herramientas que necesita.

7. Use servicios administrados. Cuando sea posible, utilice el modelo de plataforma como servicio
(PaaS) en lugar del de infraestructura como servicio (IaaS).

8. Use el mejor almacén de datos para el trabajo. Elija la tecnología de almacenamiento que mejor se
adapte a los datos y la forma en que se utilizarán.

9. Diseñe para permitir la evolución. Todas las aplicaciones de éxito cambian con el tiempo. Un diseño
evolutivo es clave para la innovación continua.

10. Desarrolle según las necesidades de negocio. Cada decisión de diseño debe estar justificada por un
requisito de negocio.

61 CAPÍTULO 3 | Diseño de la aplicación Azure: principios de diseño


3a

Diseño para la
recuperación automática
Diseñe la aplicación para que se recupere de forma automática cuando se
produzcan errores
En un sistema distribuido se producen errores. El hardware puede fallar. La red puede tener errores
transitorios. En contadas ocasiones, todo un servicio o una región puede experimentar interrupciones,
pero incluso estas deben haberse planeado.

Por lo tanto, diseñe la aplicación para que se recupere de forma automática cuando se produzcan errores.
Esto requiere un enfoque triple:

●● Detecte los errores.


●● Responda a los errores con facilidad.
●● Registre y supervise los errores para proporcionar conocimiento operativo.

La forma en que responda a un tipo particular de error puede depender de los requisitos de disponibilidad
de la aplicación. Por ejemplo, si requiere una disponibilidad muy alta, puede conmutar automáticamente
a una región secundaria durante una interrupción regional. Sin embargo, esto supondrá un coste más
elevado que una implementación de una sola región.

Además, no considere solamente los eventos importantes, como las interrupciones regionales, que por
lo general son poco frecuentes. Debe concentrarse tanto o más en gestionar los errores locales de corta
duración, tales como los errores de conectividad de red o los errores de conexiones de base de datos.

Recomendaciones
Reintente las operaciones con error. Para obtener más información, consulte Patrón de reintento y vaya a
https://docs.microsoft.com/en-us/azure/architecture/best-practices/transient-faults.

Proteja los servicios remotos con errores (interruptor de circuito). Es bueno volver a intentarlo
después de un error transitorio, pero si el error persiste, puede terminar con demasiados llamadores
que repiten llamadas a un servicio con error. Esto puede ocasionar errores en cascada a medida que las
solicitudes crean copias de seguridad. Utilice el patrón de interruptor de circuito para que el error se
produzca rápidamente (sin realizar la llamada remota) cuando es probable que una operación genere
errores.

Aislar los recursos críticos (bulkhead). A veces, los errores de un subsistema pueden propagarse. Puede
suceder si un error provoca que algunos recursos, como los subprocesos o los sockets, no se liberen
a tiempo, lo que provoca el agotamiento de los recursos. Para evitarlo, puede crear particiones de un
sistema en grupos aislados, de modo que un error en una partición no haga caer todo el sistema.

62 CAPÍTULO 3a | Diseño para la recuperación automática


Realice la una redistribución de la carga. Las aplicaciones pueden experimentar picos repentinos de
tráfico que pueden sobrecargar los servicios del back-end. Para evitarlo, utilice el patrón de nivelación de
carga basada en cola para que los elementos de trabajo se pongan en cola de forma asincrónica. La cola
actúa como un búfer que alisa los picos de la carga.

Conmutación por error. Si no puede tener acceso a una instancia, realice una conmutación por error a
otra instancia. En el caso de elementos sin estado, como un servidor web, ponga varias instancias detrás
de un equilibrador de carga o administrador de tráfico. En el caso de elementos que guarda el estado,
como una base de datos, utilice réplicas y conmutación por error. Según el almacén de datos y cómo este
se replica, puede ser necesario que la aplicación se ocupe de aspectos de coherencia final.

Compense las transacciones erróneas. En general, evite las transacciones distribuidas, puesto que
requieren la coordinación entre servicios y recursos. En su lugar, forme una operación a partir de
transacciones individuales más pequeñas. Si se producen errores en la operación a medio camino,
utilice transacciones de compensación para deshacer cualquier paso que ya se haya completado.

Cree puntos de control para transacciones de larga duración. Los puntos de control pueden
proporcionar resistencia si una operación de larga duración genera errores. Cuando se reinicia la operación
(por ejemplo, otra máquina virtual la recoge), se puede reanudar desde el último punto de control.

Degrade correctamente. A veces no se puede solucionar un problema, pero se puede proporcionar


una funcionalidad reducida que sigue siendo útil. Pongamos como ejemplo una aplicación que muestra
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. Subsistemas enteros pueden no ser críticos para la
aplicación. Por ejemplo, en un sitio de comercio electrónico, mostrar recomendaciones de productos es
probablemente menos crítico que procesar pedidos.

Limite los clientes. A veces 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 tiempo. Consulte Patrón de limitación.

Bloquee los actores dañinos. El hecho de limitar un cliente no significa que este estuviera actuando
maliciosamente. Solo significa que el cliente ha superado su cuota de servicio. Pero si un cliente supera
constantemente su cuota o presenta otro tipo de mal comportamiento, puede bloquearlo. Defina un
proceso fuera de banda para que el usuario solicite su desbloqueo.

Utilice la elección de líder. Cuando necesite para coordinar una tarea, utilice la elección de líder para
seleccionar un coordinador. Así, el coordinador no será un punto único de error. Si se producen errores
con el coordinador, se selecciona uno nuevo. En lugar de implementar un algoritmo de elección de líder
desde cero, considere una solución comercial como Zookeeper.

Haga pruebas con la introducción de errores. Con demasiada frecuencia, la ruta correcta está bien
probada pero no la ruta que genera errores. Un sistema podría estar funcionando durante mucho tiempo
en un entorno de producción antes de poner en práctica una vía para superar los errores. Utilice la
introducción de errores para probar la resistencia del sistema a los errores, ya sea mediante la activación
de errores reales o simulándolos.

Adopte la ingeniería del caos. La ingeniería del caos amplía la noción de la introducción de errores
introduciendo errores o condiciones anómalas en instancias de producción.

Para ver un enfoque estructurado del modo de recuperar las aplicaciones automáticamente, consulte
Diseño de aplicaciones flexibles para Azure.

63 CAPÍTULO 3a | Diseño para la recuperación automática


3b

Hacer que todo sea


redundante
Cree redundancia en la aplicación para evitar puntos únicos de error
Una aplicación flexible redirige para evitar el error. Identifique las rutas críticas de la aplicación. ¿Hay
redundancia en cada punto de la ruta? Si se producen errores en un subsistema, ¿conmutará la aplicación
a otro elemento?

Recomendaciones
Considere los requisitos de negocio. La cantidad de redundancia integrada en un sistema puede afectar
a los costes y la complejidad. La arquitectura debe fundamentarse en sus requisitos de negocio, por
ejemplo, el objetivo de tiempo de recuperación (RTO). Una implementación de varias regiones es más
cara que la implementación de una sola región, por ejemplo, y es más complejo administrarla. Necesitará
procedimientos operativos para gestionar la conmutación por error y la conmutación por recuperación. El
coste y la complejidad adicionales podrían estar justificados para algunos escenarios empresariales y para
otros no.

Coloque máquinas virtuales detrás de un equilibrador de carga. No utilice una sola máquina virtual
para cargas de trabajo críticas. En su lugar, coloque varias máquinas virtuales detrás de un equilibrador de
carga. Si alguna máquina virtual deja de estar disponible, el equilibrador de carga distribuye el tráfico a
las máquinas virtuales restantes que están en buenas condiciones. Para obtener información sobre cómo
implementar esta configuración, consulte Varias máquinas virtuales para escalabilidad y disponibilidad.

64 CAPÍTULO 3b | Hacer que todo sea redundante


Replique las bases de datos. Azure SQL Database y Cosmos DB replican automáticamente los datos
dentro de una región, y el usuario puede habilitar la replicación geográfica en varias regiones. Si está
utilizando una solución de base de datos de IaaS, elija una que admita la replicación y la conmutación
por error, como los grupos de disponibilidad AlwaysOn de SQL Server. Para obtener información, visite
https://docs.microsoft.com/en-us/sql/database-engine/availability-groups/windows/always-on-
availability-groups-sql-server.

Habilite la replicación geográfica. La replicación geográfica de Azure SQL Database y Cosmos DB


crea réplicas secundarias legibles de los datos en una o más regiones secundarias. En el caso de una
interrupción, la base de datos puede conmutar por error a la región secundaria para las escrituras. Para
obtener más información sobre Azure SQL Database, visite https://docs.microsoft.com/en-us/azure/
sql-database/sql-database-geo-replication-overview. Para obtener más información sobre Cosmos DB,
visite https://docs.microsoft.com/en-us/azure/documentdb/documentdb-distribute-data-globally.

Cree particiones para mayor disponibilidad. La partición de bases de datos suele utilizarse para mejorar la
escalabilidad, pero también puede mejorar la disponibilidad. Si cae una partición, las otras particiones siguen
estando disponibles. Un error en una partición solo interrumpirá un subconjunto del total de las transacciones.

Implemente más de una región. Para conseguir la máxima disponibilidad, implemente la aplicación
en más de una región. Así, en el caso poco habitual en que un problema afecte a toda una región, la
aplicación puede conmutar por error a otra región. El siguiente diagrama muestra una aplicación en varias
regiones que usa Azure Traffic Manager para gestionar la conmutación por error.

Sincronice la conmutación por error de front-end y back-end. Use Azure Traffic Manager para
conmutar por error el front-end. Si el front-end pasa a no estar disponible en una región, Traffic Manager
redirigirá las solicitudes nuevas a la región secundaria. Según la solución de base de datos, deberá
coordinar la conmutación por error de la base de datos.

Utilice la conmutación por error automática pero la conmutación por recuperación manual.
Utilice Traffic Manager para la conmutación por error automática, pero no para la conmutación por
recuperación automática. La conmutación por recuperación automática conlleva el riesgo de conmutar
a la región principal antes de que las condiciones de sean completamente correctas. En su lugar, antes
de la conmutación por recuperación manual, verifique que todos los subsistemas de la aplicación sean
correctos. Asimismo, según la base de datos, puede que necesite comprobar la coherencia de los datos
antes de la conmutación por recuperación.

Incluya redundancia para Traffic Manager. Traffic Manager es un punto de posibles errores. Revise el
acuerdo de nivel de servicio de Traffic Manager y determine si el uso de Traffic Manager solo cumple los
requisitos de negocio para la alta disponibilidad. Si no, considere la adición de otra solución de administración
de tráfico como una conmutación por recuperación. Si el servicio de Azure Traffic Manager presenta errores,
cambie los registros de CNAME en DNS para que haga referencia a otro servicio de administración de tráfico.

65 CAPÍTULO 3b | Hacer que todo sea redundante


3c

Reducción de la
coordinación
Minimice la coordinación entre los servicios de aplicación para obtener
escalabilidad.
La mayoría de las aplicaciones en el cloud constan de varios servicios de aplicación: front-ends web, bases
de datos, procesos de negocio, informes y análisis, etc. Para alcanzar una escalabilidad y fiabilidad, cada
uno de esos servicios debe ejecutarse en varias instancias.

¿Qué sucede cuando dos instancias intentan realizar operaciones simultáneas que afectan algunos estados
compartidos? En algunos casos, debe haber coordinación entre los nodos, por ejemplo, para preservar las
garantías ACID. En este diagrama, Nodo2 espera a que Nodo1 libere un bloqueo de base de datos:

La coordinación limita las ventajas del escalado horizontal y crea cuellos de botella. En este ejemplo, al
ampliar la aplicación y añadir más instancias, verá un aumento de la contención de bloqueos. En el peor
de los casos, las instancias de front-end pasarán la mayor parte de su tiempo esperando en los bloqueos.
Otro origen frecuente de la coordinación es la semántica "exactamente una vez". Por ejemplo, un pedido
debe procesarse exactamente una vez. Dos trabajos están a la espera de nuevos pedidos. El Trabajo1
selecciona un pedido para procesarlo. La aplicación debe garantizar que el Trabajo2 no duplique el trabajo,
pero también que si el Trabajo1 experimenta un bloqueo, el pedido no se pierda.

66 CAPÍTULO 3c | Reducción de la coordinación


Puede utilizar un patrón, por ejemplo, Supervisor de agente programador, para coordinar los trabajos,
pero en este caso la partición del trabajo podría ser un mejor enfoque. A cada trabajo se le asigna un
determinado intervalo de pedidos (por ejemplo, por región de facturación). Si un trabajo experimenta
un bloqueo, una nueva instancia retoma el trabajo donde lo dejó la instancia anterior, pero no hay varias
instancias compitiendo.

Recomendaciones
Adopte la coherencia final. Cuando los datos están distribuidos, se necesita coordinación para cumplir
unas garantías de coherencia sólidas. Supongamos, por ejemplo, que una operación actualiza dos bases
de datos. En vez de colocarlas en el ámbito de una sola transacción, es mejor si el sistema puede tener
en cuenta la coherencia final, quizá utilizando el patrón de transacciones de compensación para poder
revertir de forma lógica después de un error.

Utilice eventos del dominio para sincronizar el estado. Un evento del dominio es un evento que
registra cuando sucede algo que tiene importancia dentro del dominio. Los servicios interesados pueden
escuchar el evento, en lugar de utilizar una transacción global para coordinar varios servicios. Si se utiliza
este enfoque, el sistema debe admitir la coherencia final (consulte el punto anterior).

Considere los patrones como el de CQRS y el de abastecimiento de eventos. Estos dos patrones
pueden ayudar a reducir la contención entre las cargas de trabajo de lectura y las de escritura.

●● El patrón CQRS separa las operaciones de lectura de las operaciones de escritura. En algunas
implementaciones, los datos de lectura están físicamente separados de los datos de escritura.

●● En el patrón de abastecimiento de eventos se registran los cambios de estado como una serie de
eventos para un almacén de datos solo de anexos. Anexar un evento a la secuencia es una operación
atómica que requiere un bloqueo mínimo.

Estos dos patrones se complementan. Si el almacén de solo escritura en CQRS utiliza el abastecimiento de
eventos, el almacén de solo lectura puede escuchar los mismos eventos para crear una instantánea legible
del estado actual, optimizada para consultas. Antes de adoptar el CQRS o el abastecimiento de eventos,
sin embargo, sea consciente de los desafíos que supone este enfoque. Para obtener más información,
consulte Estilo de arquitectura CQRS.

Datos de partición. Evite poner todos los datos en un único esquema de datos compartido en muchos
servicios de aplicación. Una arquitectura de microservicios aplica este principio al hacer que cada servicio
sea responsable de su propio almacén de datos. Dentro de una misma base de datos, la creación de
particiones de los datos puede mejorar la simultaneidad, porque un servicio que escribe en una partición
no afecta al servicio que escribe en otra partición distinta.

Diseñe operaciones idempotentes. Cuando sea posible, diseñe operaciones que sean idempotentes.
Así, se pueden gestionar usando la semántica "al menos una vez". Puede poner, por ejemplo, elementos
de trabajo en una cola. Si un trabajo experimenta un bloqueo en medio de una operación, otro trabajo
simplemente selecciona ese elemento de trabajo.

Utilice un procesamiento paralelo asincrónico. Si una operación requiere varios pasos que se realizan
de forma asincrónica (como las llamadas de servicio remoto), el usuario podría llamarlas en paralelo y, a
continuación, agregar los resultados. Este enfoque supone que ningún paso depende de los resultados del
paso anterior.

Use la simultaneidad optimista cuando sea posible. El control de simultaneidad pesimista utiliza
bloqueos de base de datos para evitar conflictos. Esto puede causar un rendimiento bajo y reducir
la disponibilidad. Con el control de simultaneidad optimista, cada transacción modifica una copia
o instantánea de los datos. Cuando la transacción se confirma, el motor de base de datos valida la
transacción y rechaza todas las transacciones que puedan afectar a la coherencia de la base de datos.

67 CAPÍTULO 3c | Reducción de la coordinación


Azure SQL Database y SQL Server admiten la simultaneidad optimista mediante el aislamiento de
instantánea. Para obtener más información, visite https://docs.microsoft.com/en-us/sql/t-sql/
statements/set-transaction-isolation-level-transact-sql. Algunos servicios de almacenamiento de
Azure son compatibles con la simultaneidad optimista por medio del uso de Etags, incluidos la API de
DocumentDB y Azure Storage. Para obtener más información sobre la API de DocumentDB, visite
https://docs.microsoft.com/en-us/azure/documentdb/documentdb-faq.

Considere MapReduce u otros algoritmos distribuidos paralelos. En función de los datos y el tipo de
trabajo que realizar, puede dividir el trabajo en tareas independientes que se puedan realizar con varios
nodos trabajando en paralelo. Consulte Estilo de arquitectura de Big Compute.

Utilice la elección de líder para la coordinación. En casos en los que deben coordinarse las operaciones,
asegúrese de que el coordinador no se convierta en un punto único de error en la aplicación. Si utiliza el
patrón de elección del líder, una instancia es el líder en cada momento y actúa como coordinador. Si se
produce un error en el líder, se elige una nueva instancia para ser el líder.

68 CAPÍTULO 3c | Reducción de la coordinación


3d

Diseño pensando en
la escalabilidad
Diseñe la aplicación para que pueda escalarse horizontalmente.
Una de las principales ventajas del cloud es el escalado flexible, es decir, la posibilidad de usar tanta
capacidad como se necesite mediante el escalado horizontal a medida que aumenta la carga y la
reducción horizontal cuando no se necesite la capacidad adicional. Diseñe la aplicación para que se
pueda escalar horizontalmente añadiendo o eliminando nuevas instancias según la demanda.

Recomendaciones
Evite la permanencia de la instancia. La permanencia o afinidad de sesión se produce cuando las
solicitudes del mismo cliente se dirigen siempre al mismo servidor. La permanencia limita la capacidad de
escalado horizontal de la aplicación. Por ejemplo, el tráfico procedente de un usuario de alto volumen no
se distribuirá entre las instancias. Entre las causas de la permanencia se encuentran el almacenamiento del
estado de sesión en memoria y el uso de claves específicas del equipo para el cifrado. Asegúrese de que
cualquier instancia pueda gestionar cualquier solicitud.

Identifique los cuellos de botella. El escalado horizontal no es una solución mágica para todos los
problemas de rendimiento. Por ejemplo, si la base de datos de back-end es el cuello de botella, no
ayudará a añadir más servidores web. Identifique y resuelva los cuellos de botella en el sistema en primer
lugar, antes de añadir más situaciones al problema. Las partes con estado de un sistema son la causa más
probable de los cuellos de botella.

Descomponga las cargas de trabajo por requisitos de escalabilidad. Las aplicaciones suelen constar de
varias cargas de trabajo con diferentes requisitos de escalado. Por ejemplo, una aplicación podría tener un
sitio público y un sitio de administración aparte. El sitio público puede experimentar oleadas repentinas de
tráfico, mientras que el sitio de administración tiene una carga más pequeña y predecible.

Descargue las tareas de uso intensivo de recursos. Las tareas que requieren muchos recursos de CPU
o E/S deben moverse a trabajos en segundo plano siempre que sea posible para minimizar la carga en el
front-end que gestiona solicitudes de usuario.

Utilice características de escalado automático integradas. Muchos servicios de proceso de Azure


disponen de compatibilidad integrada con el escalado automático. Si la aplicación tiene una carga de
trabajo previsible y periódica, aplique el escalado horizontal según una programación. Por ejemplo,
aplique el escalado durante el horario comercial. De lo contrario, si la carga de trabajo no es predecible,
utilice indicadores de rendimiento como la CPU o la longitud de la cola de solicitudes para activar el
escalado automático. Para conocer los procedimientos recomendados del escalado automático, consulte
Escalado automático. Para conocer los procedimientos recomendados del escalado automático, visite
https://docs.microsoft.com/en-us/azure/architecture/best-practices/auto-scaling.

69 CAPÍTULO 3d | Diseño pensando en la escalabilidad


Considere un escalado automático intenso para cargas de trabajo críticas. En el caso de las cargas
de trabajo críticas, desea mantenerse por delante de la demanda. Es preferible agregar rápidamente
nuevas instancias en situaciones de carga muy intensa para gestionar el tráfico adicional e ir reduciendo
paulatinamente.

Diseñe pensando en la reducción horizontal. Recuerde que con la escala flexible, la aplicación tendrá
períodos de reducción horizontal, cuando se quiten instancias. La aplicación debe poder gestionar con
facilidad las instancias que se quiten. He aquí algunas maneras de gestionar la reducción horizontal:
●● Escuche eventos de cierre (si están disponibles) y realice los cierres de forma limpia.
●● Los clientes/consumidores de un servicio deberían asistir a un control de errores y un procedimiento
de reintento transitorios.
●● Para tareas de larga duración, piense en dividir el trabajo y en utilizar puntos de control o el patrón
Pipes and Filters.
●● Ponga los elementos de trabajo en una cola, de modo que otra instancia pueda asumir el trabajo si se
elimina una instancia en mitad del procesamiento.

70 CAPÍTULO 3d | Diseño pensando en la escalabilidad


3e

Creación de particiones
para evitar límites
Puede utilizar particiones para evitar los límites de bases de datos, redes
y procesos.
Una de las principales ventajas del cloud es el escalado flexible, es decir, la posibilidad de usar tanta
capacidad como se necesite mediante el escalado horizontal a medida que aumenta la carga y la
reducción horizontal cuando no se necesite la capacidad adicional. Diseñe la aplicación para que se
pueda escalar horizontalmente añadiendo o eliminando nuevas instancias según la demanda.

En el cloud, todos los servicios tienen limitada su capacidad de escalado vertical. Límites de servicio
de Azure están documentados en los límites, las cuotas y las restricciones de suscripción y servicio de
Azure. Entre los elementos limitados se incluyen el número de núcleos, el rendimiento de consultas y el
rendimiento de red. Si su sistema crece lo suficiente, puede que se encuentre con uno o varios de estos
límites. Puede utilizar la creación de particiones para evitarlos.

Hay muchas formas de crear particiones en un sistema, como por ejemplo:


●● Crear una partición de una base de datos para evitar los límites en el tamaño de la base, los datos,
la E/S o el número de sesiones simultáneas.
●● Crear una partición de un bus de mensajes o una cola para evitar los límites en el número de
solicitudes o de conexiones simultáneas.
●● Crear una partición de una aplicación web de App Service para evitar los límites en el número de
instancias por plan de App Service.

En una base de datos se pueden crear particiones horizontalmente, verticalmente, o funcionalmente.

●● En la creación horizontal de particiones, denominada también particionamiento, cada partición


contiene datos de un subconjunto del conjunto de datos total. Las particiones comparten el mismo
esquema de datos. Por ejemplo, los clientes cuyos nombres comienzan por una letra entre la A y la M
van en una partición, y los que comiencen entre la N y la Z, en otra partición.
●● En la creación vertical de particiones, cada partición tiene un subconjunto de los campos de los
elementos que hay en el almacén de datos. Por ejemplo, puede poner los campos a los que se acceda
más frecuentemente en una partición, y aquellos a los que se acceda con menos frecuencia, en otra.
●● En la creación funcional de particiones, los datos se dividen en virtud de cómo se usan en cada
contexto limitado del sistema. Por ejemplo, puede almacenar los datos de facturación en una partición
y los datos de inventario de productos en otra. Los esquemas son independientes.

Para obtener más información, visite https://docs.microsoft.com/en-us/azure/architecture/best-practices/


data-partitioning.

71 CAPÍTULO 3e | Creación de particiones para evitar límites


Recomendaciones
Cree particiones de partes diferentes de la aplicación. Las bases de datos son un candidato obvio para
crear particiones, pero piense también en el almacenamiento, la caché, las colas y las instancias de proceso.

Diseñe la clave de partición para evitar zonas activas. Si crea una partición en una base de datos, pero
una de las particiones sigue recibiendo la mayor parte de las solicitudes, no habrá resuelto el problema. Lo
ideal es que la carga se distribuya uniformemente en todas las particiones. Por ejemplo, cree los hash por
ID de cliente y no por la primera letra del nombre del cliente, ya que algunas letras son más frecuentes. El
mismo principio se aplica al crear una partición de una cola de mensajes. Elija una clave de partición que
provoque a una distribución uniforme de los mensajes en todo el conjunto de colas. Para obtener más
información, consulte Particionamiento.

Cree particiones para evitar los límites de suscripción y servicio de Azure. Los servicios y los
componentes individuales tienen límites, pero también las suscripciones y los grupos de recursos. En el
caso de aplicaciones muy grandes, puede que necesite crear particiones para evitar esos límites.

Cree particiones en diferentes niveles. Piense en un servidor de base de datos implementado en


una VM. La VM tiene un disco duro virtual que está respaldado por Azure Storage. La cuenta de
almacenamiento pertenece a una suscripción de Azure. Observe que cada paso en la jerarquía tiene
límites. El servidor de base de datos podría tener un límite para el grupo de conexiones. Las VM tienen
límites de CPU y red. El almacenamiento tiene límites de E/S por segundo. La suscripción tiene límites en
el número de núcleos de VM. En general, es más fácil crear particiones en niveles inferiores de la jerarquía.
Solo las aplicaciones grandes deberían tener que crear particiones en el nivel de suscripción.

72 CAPÍTULO 3e | Cree particiones para evitar límites


3f

Diseño para el equipo


de operaciones
Diseñe una aplicación para que el equipo de operaciones cuente con las
herramientas que necesita.

El cloud ha cambiado drásticamente el papel del equipo de operaciones. Ya no son responsables de


gestionar el hardware y la infraestructura que alojan la aplicación. Dicho esto, el equipo de operaciones
sigue siendo una parte fundamental en la ejecución correcta de una aplicación en el cloud. Entre algunas
de las funciones más importantes del equipo de operaciones se incluyen:

●● Implementación
●● Supervisión
●● Escalación
●● Respuesta a incidentes
●● Auditoría de seguridad

Un registro y un seguimiento sólidos son especialmente importantes en las aplicaciones en el cloud.


Involucre al equipo de operaciones en el diseño y la planificación para asegurar que la aplicación les
aporte los datos y el conocimiento que necesitan para tener éxito.

Recomendaciones
Haga que todo se pueda observar. Cuando se implementa y se pone en funcionamiento una solución,
los registros y el seguimiento son la fuente de conocimiento principal sobre el sistema. El seguimiento
registra una ruta a través de todo el sistema y resulta útil para localizar cuellos de botella, problemas
de rendimiento y puntos de error. El registro captura eventos individuales, como cambios de estado
de la aplicación, errores y excepciones. Registre la actividad en producción; de lo contrario, perderá
conocimiento cuando más lo necesite.

Dótese de instrumentos de supervisión. La supervisión aporta conocimiento sobre lo bien (o mal) que
funciona una aplicación en términos de disponibilidad, rendimiento y estado del sistema. Por ejemplo,
mediante la supervisión podrá saber si está cumpliendo su SLA. La supervisión se realiza durante el
funcionamiento normal del sistema. Debe realizarse en tiempo real tanto como sea posible, de modo que
el personal de operaciones pueda reaccionar rápidamente ante los problemas. En una situación ideal, la
supervisión puede ayudarle a evitar problemas antes de que causen un error crítico. Para obtener más
información, visite https://docs.microsoft.com/en-us/azure/architecture/best-practices/monitoring.

73 CAPÍTULO 3f | Diseño para el equipo de operaciones


Dótese de instrumentos para el análisis de causa raíz. El análisis de causa raíz es el proceso de
encontrar la causa subyacente de los errores. Se produce después de que haya ocurrido un error.

Utilice seguimiento distribuido. Utilice un sistema de seguimiento distribuido que admita simultaneidad,
asincronía y escala en el cloud. Los seguimientos deben incluir un ID de correlación que se extienda a
través de los límites de servicio. Una sola operación puede incluir llamadas a múltiples servicios de la
aplicación. Si una operación falla, el ID de correlación ayuda a identificar la causa del error.

Estandarice los registros y las métricas. El equipo de operaciones necesitará acumular registros de los
distintos servicios que tenga en la solución. Si cada servicio utiliza su propio formato de registro, se hace
difícil o imposible obtener información útil de ellos. Defina un esquema común que incluya campos como
el ID de correlación, el nombre del evento, la dirección IP del remitente y así sucesivamente. Los servicios
individuales pueden derivar esquemas personalizados que heredan el esquema base y contienen campos
adicionales.

Automatice las tareas de administración. Incluidas el aprovisionamiento, la implementación y la


supervisión. La automatización de una tarea hace que sea repetible y menos propensa a errores humanos.

Trate la configuración como código. Compruebe los archivos de configuración en un sistema de control
de versiones, de modo que pueda hacer un seguimiento y crear versiones de sus cambios y volver atrás si
es necesario.

74 CAPÍTULO 3f | Diseño para el equipo de operaciones


3g

Uso de servicios
administrados
Cuando sea posible, utilice el modelo de plataforma como servicio
(PaaS) en lugar del de infraestructura como servicio (IaaS).

La IaaS es como tener una caja de piezas. Se puede construir cualquier cosa, pero hay que montarla uno
mismo. Los servicios gestionados son fáciles de configurar y administrar. No hay que aprovisionarse de
máquinas virtuales, configurar VNets, administrar parches y actualizaciones ni asumir todos los demás
gastos asociados con la ejecución de software en una máquina virtual.

Por ejemplo, supongamos que su aplicación necesita una cola de mensajes. Podría configurar su propio
servicio de mensajería en una VM, con algo como RabbitMQ. Sin embargo, Azure Service Bus ya ofrece
mensajería fiable como servicio y es más sencillo de configurar. No tiene más que crear un espacio de
nombres de Service Bus (lo que puede hacerse como parte de un script de implementación) y luego
llamar a Service Bus usando el SDK cliente.

Naturalmente, su aplicación puede tener requisitos específicos que hagan que un enfoque de IaaS
resulte más conveniente. Sin embargo, aunque su aplicación se base en IaaS, identifique aquellos lugares
donde puede ser natural incorporar servicios administrados. Entre ellos se incluyen la caché, las colas y el
almacenamiento de datos.

En lugar de ejecutar... Piense en ejecutar...


●● Active Directory ●● Azure Active Directory Domain Services
●● Elasticsearch ●● Búsqueda de Azure
●● Hadoop ●● HDInsight
●● IIS ●● App Service
●● Mongo DBC ●● Cosmos DB
●● Redis ●● Azure Redis Cache
●● SQL Server ●● Azure SQL Database

75 CAPÍTULO 3g | Uso de servicios administrados


3h

Uso del mejor almacén


de datos para el trabajo
Elija la tecnología de almacenamiento que mejor se adapte a los datos
y la forma en que se utilizarán.

La IaaS es como tener una caja de piezas. Se puede construir cualquier cosa, pero hay que montarla uno
mismo. Los servicios gestionados son fáciles de configurar y administrar. No hay que aprovisionarse de
máquinas virtuales, configurar VNets, administrar parches y actualizaciones ni asumir todos los demás
gastos asociados con la ejecución de software en una máquina virtual.

Por ejemplo, supongamos que su aplicación necesita una cola de mensajes. Podría configurar su propio
servicio de mensajería en una VM, con algo como RabbitMQ. Sin embargo, Azure Service Bus ya ofrece
mensajería fiable como servicio y es más sencillo de configurar. No tiene más que crear un espacio de
nombres de Service Bus (lo que puede hacerse como parte de un script de implementación) y luego
llamar a Service Bus usando el SDK cliente.

Naturalmente, su aplicación puede tener requisitos específicos que hagan que un enfoque de IaaS
resulte más conveniente. Sin embargo, aunque su aplicación se base en IaaS, identifique aquellos lugares
donde puede ser natural incorporar servicios administrados. Entre ellos se incluyen la caché, las colas y el
almacenamiento de datos.

Los días en los que se plasmaban todos los datos en una gran base de datos SQL relacional ya son cosa
del pasado. Las bases de datos relacionales son muy buenas en su objetivo, proporcionar garantías ACID
para transacciones sobre datos relacionales. Sin embargo, tienen algunos inconvenientes:

●● Las consultas podrían requerir combinaciones costosas.


●● Los datos se deben normalizar y ajustar a un esquema predefinido (schema on write).
●● La contención de bloqueo puede afectar al rendimiento.

En soluciones grandes, es probable que una única tecnología de almacén de datos no satisfaga todas sus
necesidades. Entre las alternativas a las bases de datos relacionales se incluyen los almacenes de claves/
valores, las bases de datos de documentos, las bases de datos de motores de búsqueda, las bases de
datos de series temporales, las bases de datos de familias de columnas y las bases de datos de gráficos.
Cada una tiene pros y contras, y los diferentes tipos de datos se ajustan más naturalmente a unas u otras.

Por ejemplo, podría almacenar un catálogo de productos en una base de datos de documentos,
como Cosmos DB, que permite un esquema flexible. En ese caso, cada descripción de producto es un
documento autónomo. Para consultas sobre el catálogo completo, puede indexar el catálogo y almacenar
el índice en Búsqueda de Azure. El inventario de productos podría guardarse en una base de datos SQL, ya
que los datos requieren garantías ACID.

76 CAPÍTULO 3h | Uso del mejor almacén de datos para el trabajo


Recuerde que los datos incluyen más que los datos de la aplicación persistentes. También incluyen
registros de aplicaciones, eventos, mensajes y cachés.

Recomendaciones
No use una base de datos relacional para todo. Piense en otros tipos de almacenes de datos si es
conveniente. Vea Elija el almacén de datos correcto.

Adopte la persistencia políglota. En soluciones grandes, es probable que una única tecnología de
almacén de datos no satisfaga todas sus necesidades.

Tenga en cuenta el tipo de datos. Por ejemplo, ponga los datos transaccionales en SQL, ponga los
documentos JSON en una base de datos de documentos, ponga los datos de telemetría en una base de
datos de serie temporal, ponga los registros de la aplicación en Elasticsearch y ponga los blobs en Azure
Blob Storage.

Opte por la disponibilidad en lugar de por la coherencia (fuerte). El teorema del CAP implica que un
sistema distribuido debe realizar compensaciones entre la disponibilidad y la coherencia. (Las particiones
de red, el otro pilar del teorema del CAP, nunca se pueden evitar completamente). A menudo se puede
lograr mayor disponibilidad mediante la adopción de un modelo de coherencia eventual.

Tenga en cuenta el conjunto de habilidades del equipo de desarrollo. El uso de la persistencia


políglota tiene ventajas, pero es posible excederse. La adopción de una nueva tecnología de
almacenamiento de datos requiere un nuevo conjunto de habilidades. El equipo de desarrollo debe
comprender cómo sacar el máximo provecho de la tecnología. Deben entender los patrones de uso
apropiado, cómo optimizar consultas, ajustar para un mejor rendimiento, etc. Tenga esto en cuenta al
pensar en tecnologías de almacenamiento.

Use transacciones compensatorias. Un efecto secundario de la persistencia políglota es que una sola
transacción podría escribir datos en varios almacenes. Si algo falla, use transacciones compensatorias para
deshacer los pasos que ya haya completado.

Analice los contextos limitados. "Contexto limitado" es un término procedente del diseño basado en
dominio. Un contexto limitado es una acotación explícita en torno a un modelo de dominio y define a qué
partes del dominio se aplica el modelo. Idealmente, un contexto limitado se asigna a un subdominio del
dominio de negocio. Los contextos limitados en el sistema son un lugar natural que tener en cuenta para
la persistencia políglota. Por ejemplo, los "productos" pueden aparecer en el subdominio de catálogo de
productos y el subdominio de inventario de productos, pero es muy probable que estos dos subdominios
tengan diferentes requisitos de almacenamiento, actualización y consulta de productos.

77 CAPÍTULO 3h | Use el mejor almacén de datos para el trabajo


3i

Diseño para permitir


la evolución
Un diseño evolutivo es clave para la innovación continua

Todas las aplicaciones que tienen éxito cambian con el tiempo, ya sea para solucionar problemas, agregar
nuevas características, adoptar nuevas tecnologías o hacer que los sistemas existentes sean más escalables
y resistentes. Si todas las partes de una aplicación están emparejadas de forma inflexible, será muy
complicado introducir cambios en el sistema. Un cambio en una parte de la aplicación puede alterar otra
parte o provocar que los cambios se propaguen por todo el código base.

Este problema no se limita a las aplicaciones monolíticas. Una aplicación se puede descomponer en
servicios pero seguir mostrando el tipo de emparejamiento inflexible que hace que el sistema sea rígido
y quebradizo. Sin embargo, cuando los servicios están diseñados para evolucionar, los equipos pueden
innovar y ofrecer continuamente nuevas características.

Los microservicios se están convirtiendo en una forma popular de lograr un diseño evolutivo, porque
responden a muchas de las consideraciones mencionadas anteriormente.

Recomendaciones
Fomente una alta cohesión y un emparejamiento flexible. Un servicio está cohesionado si proporciona
una funcionalidad que se corresponde con él de forma lógica. Los servicios están emparejados de forma
flexible si se puede cambiar un servicio sin cambiar el otro. Una cohesión alta generalmente significa que los
cambios en una función requerirán cambios en otras funciones relacionadas. Si percibe que la actualización de
un servicio requiere actualizaciones coordinadas en otros servicios, puede ser una señal de que sus servicios
no están cohesionados. Uno de los objetivos del diseño basado en dominio (DDD) es identificar esos límites.

Encapsule el conocimiento del dominio. Cuando un cliente consume un servicio, la responsabilidad


de hacer cumplir las reglas de negocio del dominio no debería recaer en el cliente. El servicio debe
encapsular todo el conocimiento del dominio que caiga bajo su responsabilidad. De lo contrario, cada
cliente tiene que hacer cumplir las reglas de negocio y, al final, el conocimiento del dominio acabará
repartido en diferentes partes de la aplicación.

Utilice mensajería asincrónica. La mensajería asincrónica es una forma de desvincular el productor del
mensaje del consumidor. El productor no depende de que el consumidor responda al mensaje o realice
ninguna acción particular. Con una arquitectura de publicaciones y envíos (pub/sub), el productor puede
no saber quién está consumiendo el mensaje. Los nuevos servicios pueden consumir fácilmente los
mensajes sin modificaciones en el productor.

78 CAPÍTULO 3i | Diseño para permitir la evolución


No vuelque el conocimiento del dominio en una puerta de enlace. Las puertas de enlace pueden ser
útiles en una arquitectura de microservicios, para cosas como solicitudes de enrutamiento, traducción
de protocolo, equilibrio de carga o autenticación. Sin embargo, la puerta de enlace debe limitarse a este
tipo de funcionalidad de la infraestructura. No debe implementar ningún conocimiento del dominio, para
evitar que se convierta en una dependencia insalvable.

Exponga interfaces abiertas. Evite la creación de capas de traducción personalizadas que se encuentren
entre servicios. Por el contrario, un servicio debe exponer una API con un contrato de API bien definido.
La API debe estar versionada, de modo que pueda hacer evolucionar la API mientras que mantiene su
compatibilidad con versiones anteriores. Así, puede actualizar un servicio sin coordinar las actualizaciones
de todos los servicios ascendentes que dependen de ella. Los servicios de orientación pública deben
exponer una API RESTful por HTTP. Los servicios de back-end pueden utilizar un protocolo de mensajería
de estilo RPC por motivos de rendimiento.

Realice los diseños y pruebas con los contratos de servicio. Cuando los servicios exponen API bien
definidas, puede realizar el desarrollo y las pruebas con esas API. Así, podrá desarrollar y probar un
servicio individual sin sincronizar todos sus servicios dependientes. (Naturalmente, seguiría teniendo que
realizar las pruebas de integración y carga con los servicios reales).

Abstraiga la infraestructura de la lógica del dominio. No permita que la lógica del dominio se mezcle
con funcionalidades relacionadas con la infraestructura, como la mensajería o la persistencia. De lo
contrario, los cambios en la lógica del dominio requerirán actualizaciones en las capas de infraestructura y
viceversa.

Descargue las cuestiones de transversalidad a un servicio independiente. Por ejemplo, si hay varios
servicios que necesitan autenticar solicitudes, podría desplazar esta funcionalidad a su propio servicio.
Así, podría hacer evolucionar el servicio de autenticación, por ejemplo añadiendo un nuevo flujo de
autenticación, sin tocar ninguno de los servicios que lo utilizan.

Implemente los servicios de forma independiente. Cuando el equipo de DevOps puede implementar
un servicio independientemente de otros servicios de la aplicación, las actualizaciones pueden realizarse
de forma más rápida y segura. Las correcciones de errores y la implementación de nuevas características
se pueden realizar con una cadencia más regular. Diseñe tanto el proceso de la aplicación como el de
publicación para que admitan actualizaciones independientes.

79 CAPÍTULO 3i | Diseño para permitir la evolución


3j

Desarrollo en función de las


necesidades del negocio
Cada decisión de diseño debe estar justificada por un requisito de negocio.

Este principio de diseño podría parecer obvio, pero es esencial tenerlo en cuenta al diseñar una solución.
¿Prevé que tendrá millones de usuarios o solo un puñado de miles? ¿Es aceptable una interrupción de la
aplicación de una hora? ¿Espera que se produzcan grandes picos de tráfico o la carga de trabajo será muy
predecible? Al final, cada decisión de diseño debe estar justificada por una necesidad de negocio.

Recomendaciones
Defina objetivos de negocio. Incluidos el objetivo de tiempo de recuperación (RTO), el objetivo de
punto de recuperación (RPO) y la interrupción máxima tolerable (MTO). Estos números deben determinar
las decisiones acerca de la arquitectura. Por ejemplo, para lograr un RTO bajo, podría implementar una
conmutación por error automatizada a una región secundaria. Sin embargo, si la solución puede tolerar un
RTO mayor, ese grado de redundancia podría ser innecesario.

Documente los acuerdos de nivel de servicio (SLA) y los objetivos de nivel de servicio (SLO).
Incluidas métricas de disponibilidad y rendimiento. Podría desarrollar una solución que ofrezca una
disponibilidad del 99,95 %. ¿Es eso suficiente? La respuesta es una decisión empresarial.

Modele la aplicación en torno al dominio empresarial. Empiece por analizar los requisitos del negocio.
Utilice estos requisitos para modelar la aplicación. Piense en adoptar un enfoque de diseño basado en
dominio (DDD) para crear modelos de dominios que reflejen los procesos de negocio y los casos de uso.

Aborde tanto requisitos funcionales como no funcionales. Los requisitos funcionales le permiten juzgar
si la aplicación hace lo correcto. Los requisitos no funcionales le permiten juzgar si la aplicación hace bien
esas cosas. En particular, asegúrese de que comprende sus necesidades de escalabilidad, disponibilidad y
latencia. Estos requisitos influirán en las decisiones de diseño y la selección de la tecnología.

Descomponga por carga de trabajo. El término "carga de trabajo" en este contexto significa una
capacidad o tarea de proceso discreta, que puede separarse de forma lógica de otras tareas. Las diferentes
cargas de trabajo pueden tener requisitos diferentes de disponibilidad, escalabilidad, coherencia de los
datos y recuperación ante desastres.

Plan de crecimiento. Puede que una solución satisfaga sus necesidades actuales en términos de número
de usuarios, volumen de transacciones, almacenamiento de datos y así sucesivamente. Sin embargo, una
aplicación robusta puede asumir el crecimiento sin grandes cambios arquitectónicos. Consulte Diseño
pensando en la escalabilidad y Creación de particiones para evitar límites. Piense también que su modelo
de negocio y sus requisitos empresariales probablemente cambiarán con el tiempo. Si el modelo de

80 CAPÍTULO 3j | Desarrollo en función de las necesidades del negocio


servicio y los modelos de datos de una aplicación son demasiado rígidos, será difícil hacer evolucionar la
aplicación para nuevos casos de uso y escenarios. Consulte Diseño para permitir la evolución.

Administre los costes. En una aplicación local tradicional, se paga por adelantado el coste del hardware
(gasto de capital). En una aplicación en el cloud, paga por los recursos que consume. Asegúrese de que
comprende el modelo de precios para los servicios que consume. El coste total incluirá el uso del ancho
de banda de red, el almacenamiento, las direcciones IP, el consumo de servicios y otros factores. Consulte
Precios de Azure para obtener más información. Tenga en cuenta también los costes de sus operaciones.
En el cloud, no tendrá que gestionar el hardware ni ninguna otra infraestructura, pero aún necesitará
administrar sus aplicaciones, incluidos DevOps, respuesta a incidentes, recuperación ante desastres y así
sucesivamente.

81 CAPÍTULO 3j | Desarrollo en función de las necesidades del negocio


3k

Diseño de
aplicaciones flexibles
para Azure
En lugar de comprar hardware de alta gama para escalar verticalmente, en un entorno de cloud debe
escalar horizontalmente. Los costes de los entornos de cloud se mantienen al mínimo y el objetivo es
minimizar el efecto de un error.

En un sistema distribuido, se producen errores. El hardware puede fallar. La red puede tener errores
transitorios. En contadas ocasiones, todo un servicio o una región puede experimentar interrupciones,
pero incluso estas deben haberse planeado.

La creación de una aplicación fiable en el cloud es algo diferente a crear una aplicación fiable en un
entorno empresarial. Aunque, históricamente, puede que haya comprado hardware de alta gama para
escalar verticalmente, en un entorno de cloud debe realizar el escalado en horizontal. Los costes de los
entornos de cloud se mantienen bajos con el uso de hardware básico. En lugar de centrarse en evitar
errores y optimizar el "tiempo medio entre errores", en este nuevo entorno el foco cambia al "tiempo
medio de restauración". El objetivo es minimizar el efecto de un error.

Este artículo proporciona información general sobre cómo desarrollar aplicaciones flexibles en
Microsoft Azure. Comienza con una definición del término "resistencia" y sus conceptos relacionados. A
continuación, describe el proceso para conseguir resistencia, utilizando un enfoque estructurado durante
el periodo de vida de una aplicación, desde el diseño y la implementación hasta el despliegue y las
operaciones.

¿Qué entendemos por resistencia?


La resistencia en este contexto es la capacidad de un sistema para recuperarse de errores y seguir
funcionando. No se trata de evitar los errores, sino de responder a ellos de una forma que evite las
interrupciones o la pérdida de datos. El objetivo de la resistencia es devolver la aplicación a un estado de
pleno funcionamiento tras un error.

Dos aspectos importantes de la resistencia son la alta disponibilidad y la recuperación ante desastres.

●● La alta disponibilidad (HA, por sus siglas en inglés) es la capacidad de la aplicación de seguir
funcionando en un estado correcto, sin un tiempo de inactividad significativo. Entendemos por
"estado correcto" aquel en el que la aplicación responde y los usuarios pueden conectarse a la
aplicación e interactuar con ella.

82 CAPÍTULO 3k | Diseño de aplicaciones flexibles para Azure


●● La recuperación ante desastres (DR, por sus siglas en inglés) es la capacidad de recuperarse de
incidentes raros pero importantes: errores no transitorios y a gran escala, como una interrupción de
servicios que afecte a toda una región. La recuperación ante desastres incluye copia de seguridad y
archivado de datos y podría requerir una intervención manual, como restaurar una base de datos a
partir de una copia de seguridad.

Una manera de pensar en la HA frente a la DR es que la DR comienza cuando el impacto de un error


supera la capacidad del diseño de la HA para afrontarlo. Por ejemplo, si se utilizan varias VM tras un
equilibrador de carga, se proporcionará disponibilidad si se produce un error en una VM, pero no si todas
ellas fallan al mismo tiempo.

Al diseñar una aplicación para ser flexible, es necesario entender sus requisitos de disponibilidad.
¿Cuánto tiempo de inactividad es aceptable? Esto depende parcialmente del coste. ¿Cuánto costará el
tiempo de inactividad potencial a su empresa? ¿Cuánto debe invertir en hacer que la aplicación tenga
alta disponibilidad? También tiene que definir qué implica que la aplicación esté disponible. Por ejemplo,
¿la aplicación se considerará "inactiva" si un cliente puede enviar un pedido, pero el sistema no puede
procesarlo dentro del plazo normal? Tenga también en cuenta la probabilidad de que se produzca un
determinado tipo de interrupción y si es rentable una estrategia de mitigación.

Otro término común es la continuidad del negocio (BC, por sus siglas en inglés), que es la capacidad de
realizar las funciones de negocio esenciales durante y después de condiciones adversas, como un desastre
natural o un servicio interrumpido. La BC cubre todo el funcionamiento del negocio, incluyendo las
instalaciones físicas, el personal, las comunicaciones, el transporte y la informática. Este artículo se centra
en las aplicaciones en el cloud, pero la planificación de la resistencia debe realizarse en el contexto de las
necesidades globales de la BC. Para obtener más información, consulte la [Contingency Planning Guide]
[capacity-planning-guide] del Instituto Nacional de Ciencia y Tecnología de EE. UU. (NIST).

Proceso para obtener resistencia


La resistencia no se consigue con complementos. Debe diseñarse en el propio sistema y ponerse en
práctica operativamente. Este es el modelo general que se debe seguir:

1. Defina sus requisitos de disponibilidad, según sus necesidades empresariales.


2. Diseñe la aplicación pensando en la resistencia. Comience con una arquitectura que siga unas prácticas
de probada eficacia y, a continuación, identifique los posibles puntos de error en dicha arquitectura.

3. Implemente estrategias para detectar errores y recuperarse de ellos.

4. Pruebe la implementación simulando errores y desencadenando conmutaciones por error forzadas.

5. Despliegue la aplicación en producción mediante un proceso confiable y repetible.

6. Supervise la aplicación para detectar errores. Al supervisar el sistema, puede calibrar el estado de la
aplicación y responder a incidencias en caso necesario.

7. Responda si hay incidencias que requieran intervenciones manuales.

En el resto de este artículo abordaremos cada uno de estos pasos con más detalle.

Definición de los requisitos de resistencia


La planificación de la resistencia comienza con los requisitos empresariales. A continuación presentamos
algunos enfoques para abordar la resistencia desde esas condiciones.

83 CAPÍTULO 3k | Diseño de aplicaciones flexibles para Azure


Descomponga por carga de trabajo
Muchas soluciones en el cloud consisten en múltiples cargas de trabajo de aplicaciones. El término
"carga de trabajo" en este contexto significa una capacidad o tarea informática discreta, que puede estar
separada mediante lógica de otras tareas, en términos de requisitos de almacenamiento de datos y de
lógica empresarial. Por ejemplo, una aplicación de comercio electrónico puede incluir las siguientes cargas
de trabajo:

●● Explore y realice búsquedas en un catálogo de productos.


●● Cree pedidos y realice un seguimiento de los mismos.
●● Consulte recomendaciones.

Estas cargas de trabajo pueden tener requisitos diferentes de disponibilidad, escalabilidad, coherencia de
los datos, recuperación ante desastres y así sucesivamente. De nuevo, estas son decisiones de negocio.

Piense también en los patrones de uso. ¿Hay ciertos períodos críticos, en los que el sistema debe estar
disponible? Por ejemplo, un servicio de liquidación de impuestos no puede interrumpirse justo antes de
la fecha límite de presentación, un servicio de streaming de vídeo debe estar activo durante un evento
deportivo importante, etcétera. Durante los períodos críticos, podría contar con implementaciones
redundantes en varias regiones, de modo que la aplicación podría conmutar por error si una de las
regiones falla. Sin embargo, una implementación de varias regiones es más cara, por lo que durante
tiempos menos críticos puede ejecutar la aplicación en una sola región.

RTO y RPO
Dos métricas importantes que cabe tener en cuenta son el objetivo de tiempo de recuperación y el
objetivo de punto de recuperación.
●● El objetivo de tiempo de recuperación (RTO) es el tiempo máximo aceptable que una aplicación
puede no estar disponible después de un incidente. Si su RTO es de 90 minutos, debe ser capaz de
restaurar la aplicación a un estado funcional en 90 minutos desde el inicio de un desastre. Si tiene un
RTO muy bajo, puede mantener una segunda implementación funcionando continuamente en modo
de espera para evitar una interrupción regional.

●● El objetivo de punto recuperación (RPO) es la duración máxima de una pérdida de datos aceptable
durante un desastre. Por ejemplo, si almacena datos en una única base de datos, sin replicación en
otras bases de datos, y realiza copias de seguridad cada hora, podría perder hasta una hora de datos.

El RTO y el RPO son requisitos de negocio. La realización de una evaluación de riesgos puede ayudarle a
definir el RTO y el RPO de la aplicación. Otra métrica común es el tiempo medio de recuperación (MTTR, por
sus siglas en inglés), que es el promedio de tiempo que se tarda en restaurar la aplicación después de un error.
El MTTR es un hecho empírico sobre un sistema. Si el MTTR excede el RTO, un error en el sistema causará una
interrupción del negocio inaceptable, ya que no será posible restaurar el sistema en el RTO definido.

SLA
En Azure, el acuerdo de nivel de servicio (SLA, por sus siglas en inglés) describe los compromisos de
Microsoft relativos al tiempo de actividad y la conectividad. Si el SLA para un servicio particular es del
99,9 %, significa que se espera que el servicio esté disponible 99,9 % del tiempo.

Notas:
El SLA de Azure incluye también disposiciones para la obtención de un crédito de servicio si el SLA no se
cumple, además de definiciones específicas de "disponibilidad" para cada servicio. Ese aspecto del SLA
actúa como una política de cumplimiento.

84 CAPÍTULO 3k | Diseño de aplicaciones flexibles para Azure


Tiempo de inactividad a la
SLA Tiempo de inactividad al mes Tiempo de inactividad al año
semana

99% 1,68 horas 7,2 horas 3,65 días

99.9% 10,1 minutos 43,2 minutos 8,76 horas

99.95% 5 minutos 21,6 minutos 4,38 horas

99.99% 1,01 minutos 4,32 minutos 52,56 minutos

99.999% 6 segundos 25,9 segundos 5,26 minutos

Por supuesto, una mayor disponibilidad es mejor en igualdad del resto de condiciones. Sin embargo,
al crecer el número de nueves en los decimales, el coste y la complejidad de alcanzar ese nivel de
disponibilidad también aumentan. Un tiempo de actividad del 99,99 % se traduce en unos 5 minutos de
inactividad total por mes. ¿Merece la pena la complejidad y el coste adicionales para llegar a cinco 9? La
respuesta depende de los requisitos empresariales.

A continuación figuran otras cuestiones que tener en cuenta al definir un SLA:

●● Para lograr cuatro nueves (99,99 %), probablemente no pueda depender de la intervención manual para
recuperarse de errores. La aplicación debe autodiagnosticarse y resolver automáticamente sus problemas.

●● Con más de cuatro nueves es difícil detectar las interrupciones con la suficiente rapidez como para
cumplir con el SLA.

●● Piense en el periodo de tiempo frente al que se mide el SLA. Cuanto menor sea el periodo, más
restrictiva será la tolerancia. Probablemente no tenga sentido definir su SLA en términos de tiempo
de actividad diaria o por hora.

SLA compuestos
Pongamos como ejemplo una aplicación web de App Service que escriba en Azure SQL Database. En el
momento de escribir este artículo, estos servicios de Azure tienen los siguientes SLA:

●● Aplicaciones web de App Service = 99,95 %


●● SQL Database = 99,99 %

¿Cuál es el máximo tiempo de inactividad que cabría esperar para esta aplicación? Si cualquiera de los
servicios falla, falla toda la aplicación. En general, la probabilidad de que se produzca un error en cada
servicio es independiente, así que el SLA compuesto para esta aplicación es de 99,95 % × 99,99 % = 99,94 %.
Es inferior a los SLA individuales, lo que no es sorprendente, porque una aplicación que se
basa en múltiples servicios tiene más puntos de error potenciales.

Por otro lado, puede mejorar el SLA compuesto mediante la creación de rutas de reserva independientes.
Por ejemplo, si SQL Database no está disponible, ponga las transacciones en una cola para procesarlas
posteriormente.

85 CAPÍTULO 3k | Diseño de aplicaciones flexibles para Azure


Con este diseño, la aplicación sigue estando disponible, incluso aunque no pueda conectarse a la base
de datos. Sin embargo, falla si la base de datos y la cola experimentan un error a la vez. El porcentaje
esperado de tiempo para un error simultáneo es de 0,0001 × 0,001, por lo que el SLA compuesto para esta
ruta combinada es:
●● Base de datos O cola = 1,0 − (0,0001 × 0,001) = 99,99999 %

El SLA compuesto total es:


●● Aplicación web Y (base de datos O cola) = 99,95 % × 99,99999 % = ~99,95 %

Sin embargo, este enfoque implica ciertos sacrificios. La lógica de la aplicación es más compleja, está
pagando por la cola y puede haber problemas de coherencia de datos que deberá tener en cuenta.

SLA para implementaciones en varias regiones. Otra técnica de HA es implementar la aplicación en


más de una región y usar Azure Traffic Manager para la conmutación por error si la aplicación falla en una
región. En una implementación de dos regiones, el SLA compuesto se calcula así:

Pongamos que N es el SLA compuesto para la aplicación implementada en una región. La expectativa de
que la aplicación falle en ambas regiones al mismo tiempo es de (1 − N) × (1 − N). Por lo tanto,
●● SLA combinado para ambas regiones = 1 − (1 − N)(1 − N) = N + (1 − N)N

Por último, debe tener en cuenta el SLA para Traffic Manager. En el momento de escribir esto, el SLA de
Traffic Manager es del 99,99 %.

●● SLA compuesto = 99,99 % × (SLA combinado para ambas regiones)

Además, la conmutación por error no es instantánea y puede provocar cierto tiempo de inactividad
durante una conmutación por error. Consulte Supervisión de puntos de conexión y conmutación por
error en Traffic Manager.

El SLA calculado es una base útil, pero no aborda todos los aspectos de la disponibilidad. A menudo, una
aplicación puede degradarse correctamente cuando falla una ruta no crítica. Pongamos como ejemplo una
aplicación que muestra 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. En ese caso, la no obtención de
la imagen no reduce el tiempo de actividad de la aplicación, aunque afecta a la experiencia del usuario.

86 CAPÍTULO 3k | Diseño de aplicaciones flexibles para Azure


Diseño para garantizar la resistencia
Durante la fase de diseño, debe realizar un análisis del modo de error (FMA). El objetivo de un FMA es
identificar posibles puntos de error y definir cómo responderá la aplicación a dichos errores.

●● ¿Cómo detecta la aplicación este tipo de errores?


●● ¿Cómo responderá la aplicación a este tipo de errores?
●● ¿Cómo registrará y supervisará este tipo de errores?

Para obtener más información sobre el proceso del FMA, con recomendaciones específicas para Azure,
consulte la Guía de resistencia de Azure: análisis del modo de error.

Durante la fase de diseño, debe realizar un análisis del modo de error (FMA). El objetivo de un FMA es
identificar posibles puntos de error y definir cómo responderá la aplicación a dichos errores.

●● ¿Cómo detecta la aplicación este tipo de errores?


●● ¿Cómo responderá la aplicación a este tipo de errores?
●● ¿Cómo registrará y supervisará este tipo de errores?

Para obtener más información sobre el proceso del FMA, con recomendaciones específicas para Azure,
consulte la Guía de resistencia de Azure: análisis del modo de error.

Modo de error Estrategia de detección

El servicio no está disponible HTTP 5xx

Limitación HTTP 429 (demasiadas solicitudes)

Autenticación HTTP 401 (no autorizado)

Respuesta lenta Tiempo de espera de solicitud agotado

Estrategias de resistencia
En esta sección se ofrece un estudio de algunas estrategias de resistencia comunes. La mayoría de ellas no
se limitan a una tecnología particular. Las descripciones de esta sección resumen de la idea general que
hay detrás de cada técnica, con enlaces a lecturas adicionales.

Reintente los errores transitorios


Los errores transitorios pueden deberse a la pérdida momentánea de conectividad de red, la caída de una
conexión de base de datos o a un tiempo de espera cuando un servicio está ocupado. A menudo, un error
transitorio puede resolverse simplemente reintentando la solicitud. Para muchos servicios de Azure, el
SDK cliente implementa reintentos automáticos, de forma que es transparente para el autor de la llamada;
consulte Orientación específica para el servicio de reintento.

Cada intento de reintento aumenta la latencia total. Además, muchas solicitudes erróneas pueden
causar un cuello de botella, dado que las solicitudes pendientes se acumulan en la cola. Estas solicitudes
bloqueadas podrían retener recursos críticos del sistema como memoria, subprocesos, conexiones a bases
de datos, etcétera, que pueden causar errores en cascada. Para evitar esto, aumente el retardo entre cada
intento de reintento y limite el número total de solicitudes erróneas.

87 CAPÍTULO 3k | Diseño de aplicaciones flexibles para Azure


Para obtener más información, consulte Patrón de reintento.

Equilibre la carga entre instancias

En términos de escalabilidad, una aplicación en el cloud debe poder escalarse horizontalmente añadiendo
más instancias. Este enfoque también mejora la resistencia, porque las instancias en mal estado pueden
eliminarse de la rotación.

Por ejemplo:
●● Ponga dos o más VM tras un equilibrador de carga. El equilibrador de carga distribuye el tráfico a
todas las VM. Consulte Ejecución de VM con carga equilibrada para escalabilidad y disponibilidad.
●● Escale horizontalmente una aplicación de Azure App Service a varias instancias. App Service equilibra
automáticamente la carga en varias instancias. Consulte Aplicación web básica.
●● Use Azure Traffic Manager para distribuir el tráfico en un conjunto de puntos de conexión.

Replique los datos


La replicación de datos es una estrategia general para la administración de errores no transitorios en un
almacén de datos. Muchas tecnologías de almacenamiento ofrecen replicación integrada, como Azure SQL
Database, Cosmos DB y Apache Cassandra.

Es importante tener en cuenta tanto la ruta de lectura como la de escritura. En función de la tecnología de
almacenamiento, puede tener varias réplicas de escritura o una única réplica de escritura y varias réplicas
de solo lectura.

Para aumentar al máximo la disponibilidad, se pueden colocar réplicas en varias regiones. Sin embargo,
esto aumenta la latencia cuando se replican los datos. Por lo general, la replicación entre regiones se
realiza asincrónicamente, lo que implica un modelo de consistencia eventual y posible pérdida de datos si
se produce un error en una réplica.

Degradar correctamente

Si un servicio falla y no hay ninguna ruta de conmutación por error, la aplicación puede degradarse
correctamente y aun así proporcionar una experiencia de usuario aceptable.
Por ejemplo:

●● Poner un elemento de trabajo en una cola para gestionarlo posteriormente.


●● Devolver un valor estimado.
●● Utilizar datos en la caché local.
●● Mostrar al usuario un mensaje de error. (Esta opción es preferible a que la aplicación deje de
responder a solicitudes).

88 CAPÍTULO 3k | Diseño de aplicaciones flexibles para Azure


Limite a los usuarios de alto volumen
A veces, un número pequeño de usuarios crea una carga excesiva. Eso puede afectar a otros usuarios,
reduciendo la disponibilidad global de su aplicación.

Cuando un único cliente realiza un número excesivo de solicitudes, la aplicación podría limitar al cliente
durante un periodo de tiempo determinado. Durante el período de limitación, la aplicación deniega todas
o algunas de las peticiones del cliente (dependiendo de la estrategia de limitación exacta). El umbral para
la limitación puede depender del nivel de servicio del cliente.

La limitación no implica que el cliente actuara necesariamente con intención maliciosa, sino simplemente
que excede su cuota de servicio. En algunos casos, un consumidor podría exceder constantemente su
cuota o mostrar un mal comportamiento de otra naturaleza. En ese caso, podría dar un paso más y
bloquear al usuario. Por lo general, esto se hace mediante el bloqueo de una clave de la API o un intervalo
de direcciones IP.

Para obtener más información, consulte Patrón de limitación.

Utilice un interruptor de circuito

El patrón de interruptor de circuito puede evitar que una aplicación intente repetidamente una operación
que probablemente fracase. Esto es algo similar a un interruptor de circuito físico, un interruptor que
detiene el flujo de corriente cuando un circuito se sobrecarga.

El interruptor de circuito envuelve las llamadas en un servicio. Tiene tres estados:

●● Closed. Este es el estado normal. El interruptor de circuito envía solicitudes al servicio y un contador
controla el número de errores recientes. Si la cuenta de errores supera un umbral dentro de un
periodo de tiempo determinado, el interruptor de circuito pasa al estado Open.

●● Open. En este estado, el interruptor de circuito hace inmediatamente que fallen todas las peticiones,
sin llamar al servicio. La aplicación debe usar una ruta de mitigación, como leer datos de una réplica o
simplemente devolver un error al usuario. Cuando el interruptor de circuito cambia a abierto, se inicia
un temporizador. Cuando el temporizador expira, el interruptor de circuito cambia al estado Half-
Open.

●● Half-Open. En este estado, el interruptor de circuito permite que un número limitado de solicitudes
llegue al servicio. Si lo consiguen, se supone que el servicio se ha recuperado y el interruptor vuelve al
estado Closed. De lo contrario, vuelve al estado Open. El estado Half-Open impide que un servicio de
recuperación se vea inundado de solicitudes de repente.

Para obtener más información, consulte Patrón de interruptor de circuito.

Utilice la nivelación de carga para suavizar los picos de tráfico


Las aplicaciones pueden sufrir picos repentinos de tráfico, que podrían abrumar a los servicios del back-end.
Si un servicio de back-end no puede responder a las peticiones con la suficiente rapidez, puede hacer que las
solicitudes se acumulen en cola (copia de seguridad) o provoquen que el servicio reduzca el flujo de tráfico
de la aplicación.

Para evitar esto, puede utilizar una cola como búfer. Cuando hay un nuevo elemento de trabajo, en lugar
de llamar al servicio de back-end inmediatamente, la aplicación pone en cola un elemento de trabajo para
que se ejecute de forma asincrónica. La cola actúa como un búfer que alisa los picos de la carga.

Para obtener más información, consulte Patrón de nivelación de carga basada en cola.

89 CAPÍTULO 3k | Diseño de aplicaciones flexibles para Azure


Aísle los recursos críticos
Los errores en un subsistema pueden expandirse en cascada, causando errores en otras partes de la
aplicación. Puede suceder si un error provoca que algunos recursos, como los subprocesos o los sockets,
no se liberen a tiempo, lo que provoca el agotamiento de los recursos.

Para evitar esto, puede crear particiones en un sistema de modo que se organice en grupos aislados, de
modo que un error en una partición no haga caer todo el sistema. Esta técnica se llama a veces patrón
Bulkhead.

Ejemplos:
●● Cree particiones de una base de datos (por ejemplo, por inquilino) y asigne un grupo independiente
de las instancias de servidor web a cada partición.
●● Utilice grupos de subprocesos separados para aislar las llamadas a los distintos servicios. Esto ayuda
a evitar errores en cascada si falla uno de los servicios. Por ejemplo, consulte la biblioteca Netflix
Hystrix.
●● Use contenedores para limitar los recursos disponibles para un subsistema particular.

Aplique transacciones compensatorias

Una transacción compensatoria es una transacción que permite anular los efectos de otra transacción
completada.

En un sistema distribuido, puede ser muy difícil de conseguir una coherencia transaccional sólida. Las
transacciones compensatorias son una forma de lograr coherencia mediante el uso de una serie de
transacciones individuales y más pequeñas que se pueden deshacer en cada paso.

Por ejemplo, para reservar un viaje, un cliente puede reservar un coche, una habitación de hotel y un
vuelo. Si alguno de estos pasos falla, falla toda la operación. En lugar de intentar utilizar una única
transacción distribuida para toda la operación, puede definir una transacción compensatoria para
cada paso. Por ejemplo, para anular una reserva de un coche, cancele la reserva. Para completar toda
la operación, un coordinador ejecuta cada paso. Si alguno de los pasos falla, el coordinador aplica
transacciones compensatorias para deshacer los pasos que se hayan completado.

Para obtener más información, consulte Patrón de transacciones de compensación.

90 CAPÍTULO 3k | Diseño de aplicaciones flexibles para Azure


Comprobaciones de resistencia

Por lo general, no puede comprobar la resistencia de la misma manera que comprueba las funcionalidades
de una aplicación (ejecutando pruebas unitarias, etc.). Debe comprobar cómo se comporta la carga de
trabajo de extremo a extremo en condiciones de error que solo se presentan de forma intermitente.

El proceso de comprobación es iterativo. Compruebe la aplicación, mida los resultados, analice y aborde
los errores resultantes y repita el proceso.

Pruebas de inyección de errores. Puede comprobar la resistencia del sistema durante los errores, ya
sea mediante la activación de errores reales o simulándolos. A continuación figuran algunos escenarios
habituales para comprobar:

●● Cierre las instancias de VM.


●● Interrumpa procesos.
●● Haga expirar los certificados.
●● Cambie las claves de acceso.
●● Cierre el servicio DNS en los controladores de dominio.
●● Limite los recursos disponibles del sistema, como la RAM o el número de subprocesos.
●● Desmonte los discos.
●● Vuelva a implementar una VM.

Mida los tiempos de recuperación y verifique que se cumplan los requisitos de su negocio. Pruebe
también combinaciones de modos de error. Asegúrese de que los errores no se propaguen en cascada y
se administren de forma aislada.

Este es otro motivo de por qué es importante analizar los puntos de error potenciales durante la fase de
diseño. Los resultados de ese análisis deberían ser entradas de su plan de pruebas.

Comprobación de carga. Puede comprobar la carga de la aplicación con herramientas como Visual
Studio Team Services o Apache JMeter. Las pruebas de carga son esenciales para identificar errores que
solo se producen en condiciones de carga, como que la base de datos del back-end se vea sobrepasada o
que el servicio se vea limitado. Compruebe las cargas máximas con datos de producción o sintéticos tan
parecidos a los datos de producción como sea posible. El objetivo es ver cómo se comporta la aplicación
en condiciones reales.

Implementación resistente
Cuando una aplicación se implementa en versión de producción, las actualizaciones son una fuente de
errores potencial. En el peor de los casos, una mala actualización puede causar tiempo de inactividad.
Para evitarlo, el proceso de implementación debe ser predecible y repetible. La implementación incluye el
aprovisionamiento de recursos de Azure, el despliegue de código de aplicación y la aplicación de ajustes
de configuración. Una actualización puede incluir esos tres aspectos o solo un subconjunto.

El punto crucial es que los despliegues manuales son propensos a causar errores. Por lo tanto, se
recomienda tener un proceso idempotente automatizado que pueda ejecutar bajo demanda y volver a
ejecutar si algo falla.

●● Utilice las plantillas de Resource Manager para automatizar el aprovisionamiento de recursos de Azure.
●● Utilice la configuración de estado deseado (DSC) de Azure Automation para configurar máquinas virtuales.
●● Utilice un proceso de implementación automatizado para el código de la aplicación.

91 CAPÍTULO 3k | Diseño de aplicaciones flexibles para Azure


Dos conceptos relacionados con la implementación resistente son la infraestructura como código y la
infraestructura inmutable.
●● La infraestructura como código es la práctica de usar código para proporcionar infraestructura y
configurarla. La infraestructura como código puede utilizar un enfoque declarativo o un enfoque
imperativo (o una combinación de ambos). Las plantillas de Resource Manager son un ejemplo de
enfoque declarativo. Los scripts de PowerShell son un ejemplo de enfoque imperativo.
●● La infraestructura inmutable es el principio basado en que no se debe modificar la infraestructura
después de que esté implementada en producción. De lo contrario, podría llegar a un estado en
el que se hayan aplicado cambios ad hoc, por lo que será complicado saber exactamente qué ha
cambiado y resultará difícil razonar sobre el sistema.

Otra cuestión es cómo llevar a cabo una actualización de la aplicación. Recomendamos técnicas como
la implementación "blue-green" o versiones "canary", que inserta actualizaciones de una forma muy
controlada para minimizar los posibles impactos de una mala implementación.

●● La implementación "blue-green" es una técnica donde se implementa una actualización en un


entorno de producción independiente de la aplicación activa. Después de validar la implementación,
cambie el enrutamiento del tráfico a la versión actualizada. Por ejemplo, Azure App Service Web Apps
permite realiza esto con ranuras de ensayo.
●● Las versiones "Canary" son similares a las implementaciones "blue-green". En vez de cambiar todo el
tráfico a la versión actualizada, lleva la actualización a un pequeño porcentaje de usuarios, enrutando
una parte del tráfico a la nueva implementación. Si hay un problema, puede dar marcha atrás y volver
a la antigua implementación. De lo contrario, enrute más tráfico a la nueva versión, hasta que obtenga
el 100 % del tráfico.

Sea cual sea el enfoque que adopte, asegúrese de que puede dar marcha atrás hasta la última
implementación que sepa que es funcional, en caso de que la nueva versión no sea operativa. Además, si
se producen errores, los registros de aplicación deben indicar qué versión ha causado el error.

Supervisión y diagnóstico
La supervisión y el diagnóstico resultan cruciales para la resistencia. Si se produce algún error, debe saber
qué ha sido lo que ha fallado y necesita conocer la causa del error.

La supervisión de un sistema distribuido a gran escala presenta un desafío significativo. Piense en una
aplicación que se ejecute en unas cuantas máquinas virtuales. No resultará práctico iniciar sesión en cada
una de ellas, una por una, y analizar los archivos de registro, intentando resolver un problema. Además,
probablemente el número de instancias de VM no sea estático. Las VM se añaden y eliminan a medida
que la aplicación se escala o reduce horizontalmente, y hay veces en las que una instancia puede fallar y
necesitar que se vuelva a aprovisionar. Además, una aplicación en el cloud típica podría utilizar múltiples
almacenes de datos (almacenamiento de Azure, SQL Database, Cosmos DB, caché Redis) y una acción de
un solo usuario podría abarcar varios subsistemas.

Puede concebir el proceso de supervisión y diagnóstico como una canalización con varias fases distintas:

92 CAPÍTULO 3k | Diseño de aplicaciones flexibles para Azure


●● Instrumentación. Los datos en bruto para la supervisión y el diagnóstico proceden de varias fuentes,
incluyendo registros de aplicaciones, registros de servidores web, contadores de rendimiento del SO,
registros de bases de datos y diagnóstico integrado en la plataforma Azure. La mayoría de servicios
Azure tienen una característica de diagnóstico que puede utilizar para determinar la causa de los
problemas.
●● Recopilación y almacenamiento. Los datos de instrumentación en bruto se pueden guardar en
distintas ubicaciones y con distintos formatos (por ejemplo, registros de seguimiento de aplicaciones,
registros de IIS, contadores de rendimiento). Estas diferentes fuentes se recopilan, consolidan y
almacenan de forma fiable.
●● Análisis y diagnóstico. Tras consolidar los datos, se pueden analizar para solucionar problemas y
facilitar una visión general del estado de la aplicación.
●● Visualización y alertas. En esta etapa, se presentan los datos de la telemetría de tal manera que un
operador pueda detectar rápidamente problemas o tendencias. Entre los ejemplos se incluyen paneles
o alertas por correo electrónico.

La supervisión no es igual que la detección de errores. Por ejemplo, la aplicación puede detectar un error
transitorio y volver a intentarlo, con lo que dará como resultado la ausencia de tiempo de inactividad. Sin
embargo, también debe registrar la operación de reintento, así que podrá supervisar la tasa de error, con
el fin de obtener un panorama general de la salud de la aplicación.

Los registros de aplicaciones son una fuente importante de los datos de diagnóstico. Entre los
procedimientos recomendados para el registro de aplicaciones se incluyen:
●● Registro en producción. De lo contrario, se pierden conocimientos cuando más se necesitan.
●● Eventos de registro en los límites de servicio. Incluyen un ID de correlación que fluye entre varios
límites de servicio. Si una transacción fluye a través de varios servicios y uno de ellos falla, el ID de
correlación le ayudará a determinar por qué ha fallado la operación.
●● Uso del registro semántico, también conocido como registro estructurado. Los registros no
estructurados hacen que sea difícil automatizar el consumo y el análisis de los datos de registro, lo
que es necesario a escala del cloud.
●● Uso de registro asincrónico. De lo contrario, el sistema de registro en sí mismo puede causar un
error en la aplicación, haciendo que las solicitudes creen una copia de seguridad, ya que se bloquean
mientras esperan para escribir un evento de registro.
●● El registro de aplicaciones no es lo mismo que la auditoría. Una auditoría puede hacerse por razones
reglamentarias o normativas. Por tanto, los registros de auditoría deben ser completos y no es
aceptable omitir ningún aspecto de los mismos al procesar transacciones. Si una aplicación requiere
auditoría, se debe realizar por separado con respecto al registro de diagnóstico.

Para obtener más información sobre supervisión y diagnóstico, consulte Guía de supervisión y diagnóstico.

Respuestas manuales ante errores


Las secciones anteriores se han centrado en estrategias de recuperación automatizadas, que son críticas
para una alta disponibilidad. Sin embargo, a veces es necesaria una intervención manual.
●● Alertas. Supervise la aplicación en busca de señales de advertencia que puedan requerir una
intervención proactiva. Por ejemplo, si ve que SQL Database o Cosmos DB limita constantemente el
tráfico de su aplicación, puede que necesite aumentar la capacidad de su base de datos u optimizar
sus consultas. En este ejemplo, aunque la aplicación puede controlar los errores de limitación de
forma transparente, su telemetría aún debe crear una alerta para que pueda realizar un seguimiento.
●● Conmutación por error manual. Algunos sistemas no podrán realizar automáticamente una
conmutación por error y requieren una conmutación por error manual.

93 CAPÍTULO 3k | Diseño de aplicaciones flexibles para Azure


●● Prueba de disponibilidad operativa. Si su aplicación conmuta por error a una región secundaria,
debe realizar una prueba de disponibilidad operativa antes de revertir a la región primaria. La prueba
debería verificar que la región primaria es correcta y está lista para recibir tráfico otra vez.
●● Comprobación de coherencia de datos. Si se produce un error en un almacén de datos, puede
haber incoherencias de datos cuando la tienda vuelve a estar disponible, especialmente si los datos se
han replicado.
●● Restauración desde una copia de seguridad. Por ejemplo, si SQL Database experimenta una
interrupción regional, puede geo-restaurar la base de datos a partir de la última copia de seguridad.

Documente y compruebe su plan de recuperación ante desastres. Evalúe el impacto empresarial de


los errores en la aplicación. Automatice el proceso tanto como sea posible y documente las medidas
manuales, como la conmutación por error manual o la restauración a partir de copias de seguridad.
Compruebe periódicamente su proceso de recuperación ante desastres para validar y mejorar el plan.

Resumen
En este artículo se ha abordado la resistencia desde una perspectiva completa, destacando algunos de los
desafíos exclusivos del cloud. Entre ellos se incluye la naturaleza distribuida de la computación en el cloud,
el uso de hardware básico y la presencia de errores de red transitorios.

Estos son los principales puntos que se extraen de este artículo:

●● La resistencia conduce a una mayor disponibilidad y a un menor tiempo de recuperación ante errores.

●● Conseguir resistencia en el cloud requiere un conjunto de técnicas diferente al de las soluciones


locales tradicionales.
●● La resistencia no se presenta por accidente. Debe diseñarse e integrarse desde el principio.
●● La resistencia afecta a cada parte del ciclo de vida de la aplicación, desde la planificación y la
codificación hasta las operaciones.
●● Realice comprobaciones y supervisión.

94 CAPÍTULO 3k | Diseño de aplicaciones flexibles para Azure


4

Diseño de la
aplicación Azure:
uso de los pilares
de la calidad
Escalabilidad, disponibilidad, resistencia, administración y seguridad son
los cinco pilares de un software de calidad. Si se centra en estos pilares,
podrá diseñar una aplicación en el cloud satisfactoria. Puede usar las listas
de comprobación de esta guía para revisar su aplicación en relación con
estos pilares.

95 CAPÍTULO 4 | Diseño de la aplicación Azure: uso de los pilares de la calidad


Pilar Descripción
Escalabilidad Diseñe el sistema para que admita escalación horizontal y añadir nuevas
instancias.

Disponibilidad Defina un objetivo de nivel de servicio (SLO) que determine claramente la


disponibilidad prevista y cómo se mide. Utilice la ruta crítica para definirlo.
Un punto porcentual de disponibilidad adicional puede suponer horas o días
adicionales de tiempo de actividad en un año.

Resistencia Las aplicaciones en el cloud tienen errores ocasionales y deben desarrollarse


para poder recuperarse de ellos. Integre mitigaciones de resistencia en su
aplicación en todos los niveles. Céntrese primero en mitigaciones tácticas e
incluya supervisión.

Administración Automatice las implementaciones y haga que sean un proceso rápido y rutinario
para acelerar la publicación de nuevas características o soluciones de errores.
Integre operaciones para avanzar o revertir entre versiones e incluya supervisión
y diagnóstico.

Seguridad Integre administración de identidades, protección de infraestructuras, soberanía


de los datos y cifrado en su aplicación y sus procesos de DevOps.

Escalabilidad
La escalabilidad es la capacidad de un sistema de administrar una carga creciente. Hay dos métodos
principales de escalar una aplicación. El escalado vertical (scaling up) implica aumentar la capacidad de un
recurso, por ejemplo mediante el uso de una VM de mayor tamaño. El escalado horizontal (scaling out)
consiste en añadir nuevas instancias de un recurso, como VM o réplicas de bases de datos.

El escalado horizontal tiene ventajas significativas sobre el escalado vertical:


●● Escala real en el cloud. Las aplicaciones se pueden diseñar para ejecutarse en cientos o incluso miles
de nodos, llegando a escalas que no son posibles en un único nodo.
●● El escalado horizontal es elástico. Puede añadir más instancias si aumenta la carga, o quitarlas durante
períodos más tranquilos.
●● El escalado horizontal se puede provocar automáticamente, de forma programada o como respuesta
a cambios en la carga.
●● El escalado horizontal puede ser más barato que el escalado vertical. Operar con varias VM pequeñas
puede costar menos que operar con una única VM más grande.
●● El escalado horizontal también puede mejorar la resistencia, al agregar redundancia. Si una instancia
se interrumpe, la aplicación sigue funcionando.

Una de las ventajas del escalado vertical es que se puede realizar sin tener que cambiar la aplicación. Sin
embargo, en algún momento se alcanzará el límite y ya no se podrá seguir escalando verticalmente. En
ese momento, cualquier escalado adicional debe ser horizontal.

El escalado horizontal debe estar diseñado en el sistema. Por ejemplo, se pueden escalar horizontalmente
VM colocándolas tras un equilibrador de carga. Sin embargo, cada VM del grupo debe ser capaz de
administrar cualquier solicitud del cliente, por lo que la aplicación no puede tener un estado, o si lo tiene,
almacenarlo externamente (por ejemplo, en una caché distribuida). Los servicios PaaS administrados
suelen tener escalado horizontal y escalado automáticos integrados. La facilidad de escalar estos servicios
es una ventaja principal del uso de servicios PaaS.

96 CAPÍTULO 4 | Diseño de la aplicación Azure: uso de los pilares de la calidad


Sin embargo, añadir más instancias no implica escalar una aplicación. Podría limitarse simplemente a pasar
el cuello de botella a otro lugar. Por ejemplo, si escala el front-end de una web para que se ocupe de más
peticiones de clientes, podría desencadenar contenciones de bloqueos en la base de datos. Entonces sería
necesario pensar en medidas adicionales, como la simultaneidad optimista o el particionamiento de datos,
para permitir más tráfico en la base de datos.

Realice siempre comprobaciones de rendimiento y carga para encontrar estos posibles cuellos de botella.
Las partes con estado de un sistema, como las bases de datos, son la causa más común de cuellos de
botella y requieren un diseño cuidadoso a la hora de escalarlas horizontalmente. Al resolver un cuello de
botella pueden revelarse otros cuellos de botella en otras partes.

Use la Lista de comprobación de escalabilidad para revisar su diseño desde el punto de vista de la
escalabilidad.

Directrices sobre escalabilidad


●● Patrones de diseño para la escalabilidad y el rendimiento
●● Procedimientos recomendados: escalado automático, trabajos en segundo plano, almacenamiento en
caché, CDN, particionamiento de datos.

Procedimientos recomendados
●● https://docs.microsoft.com/en-us/azure/architecture/best-practices/auto-scaling
●● https://docs.microsoft.com/en-us/azure/architecture/best-practices/background-jobs
●● https://docs.microsoft.com/en-us/azure/architecture/best-practices/caching
●● https://docs.microsoft.com/en-us/azure/architecture/best-practices/cdn
●● https://docs.microsoft.com/en-us/azure/architecture/best-practices/data-partitioning

97 CAPÍTULO 4 | Diseño de la aplicación Azure: uso de los pilares de la calidad


Disponibilidad
La disponibilidad es la proporción de tiempo que el sistema es funcional y operativo. Generalmente
se mide como un porcentaje del tiempo de actividad. Los errores de aplicación, los problemas de
infraestructura y la carga del sistema pueden reducir la disponibilidad.

Una aplicación del cloud debe tener un objetivo del nivel de servicio (SLO) que defina claramente la
disponibilidad esperada y cómo se mide la misma. Al definir la disponibilidad, tenga en cuenta la ruta
crítica. El front-end de la web podría ser capaz de dar servicio a las solicitudes del cliente, pero si cada
transacción falla porque no se puede conectar a la base de datos, la aplicación no estará disponible para
los usuarios.

La disponibilidad se describe a menudo en términos de "nueves", por ejemplo, "cuatro nueves" significa
un 99,99 % de tiempo de actividad. En la siguiente tabla se muestra el tiempo de inactividad potencial
acumulado en los distintos niveles de disponibilidad.

Tiempo de inactividad a la
SLA Tiempo de inactividad al mes Tiempo de inactividad al año
semana

99% 1,68 horas 7,2 horas 3,65 días

99.9% 10,1 minutos 43,2 minutos 8,76 horas

99.95% 5 minutos 21,6 minutos 4,38 horas

99.99% 1,01 minutos 4,32 minutos 52,56 minutos

99.999% 6 segundos 25,9 segundos 5,26 minutos

Tenga en cuenta que un tiempo de actividad del 99 % podía traducirse en una interrupción de casi 2 horas
por semana. Para muchas aplicaciones, especialmente las aplicaciones orientadas al consumidor, este no
es un SLO aceptable. Por otra parte, cinco nueves (99,999 %) implica una inactividad menor de 5 minutos
al año. Detectar una interrupción tan rápido ya es todo un reto, por no hablar de resolverla en ese tiempo.
Para obtener una disponibilidad muy alta (99,99 % o superior), no se puede depender de la intervención
manual para recuperarse ante errores. La aplicación debe poder autodiagnosticarse y corregirse
automáticamente, aspectos en los que la resistencia se vuelve crucial.

Directrices sobre escalabilidad


●● Patrones de diseño para la disponibilidad

Procedimientos recomendados
●● https://docs.microsoft.com/en-us/azure/architecture/best-practices/auto-scaling
●● https://docs.microsoft.com/en-us/azure/architecture/best-practices/background-jobs

98 CAPÍTULO 4 | Diseño de la aplicación Azure: uso de los pilares de la calidad


Resistencia
La resistencia es la capacidad de un sistema para recuperarse de errores y seguir funcionando. El objetivo
de la resistencia es devolver la aplicación a un estado de pleno funcionamiento tras un error. La resistencia
está estrechamente vinculada a la disponibilidad.

En el desarrollo de aplicaciones tradicional, ha habido un enfoque en la reducción de tiempo medio


entre errores (MTBF). Se ha dedicado mucho esfuerzo a tratar de evitar los errores en el sistema. En la
computación en el cloud se requiere un enfoque diferente, debido a varios factores:

●● Los sistemas distribuidos son complejos, y un error puntual puede reproducirse por cascada en todo
el sistema.

●● Los costes de entornos en el cloud se mantienen bajos gracias al uso de hardware básico, de modo
que deben esperarse fallos de hardware ocasionales.

●● Las aplicaciones a menudo dependen de servicios externos, que pueden llegar a estar temporalmente
no disponibles o limitar a los usuarios de alto volumen.

●● Los usuarios de hoy en día esperan que una aplicación esté siempre disponible, sin estar nunca offline.

Todos estos factores implican que las aplicaciones en el cloud deben estar diseñadas para anticiparse a
errores ocasionales y recuperarse de ellos. Azul tiene muchas características de resistencia ya incorporadas
en la plataforma. Por ejemplo:

●● Azure Storage, SQL Database y Cosmos DB proporcionan replicación de datos integrada, tanto dentro
de una región como entre regiones.

●● El almacenamiento de Azure Managed Disks se ubica automáticamente en diferentes unidades de


escala de almacenamiento para limitar los efectos de los errores de hardware.

●● Las VM de un conjunto de disponibilidad se reparten entre varios dominios de errores. Un dominio


de errores es un grupo de máquinas virtuales que comparten una fuente de alimentación y un
conmutador de red. El reparto de VM en varios dominios de errores limita el impacto de los errores
físicos en el hardware y las interrupciones de red o de alimentación.

Dicho esto, aún debe integrar la resistencia en su aplicación. Las estrategias de resistencia se pueden
aplicar a todos los niveles de la arquitectura. Algunas mitigaciones tienen una naturaleza más táctica,
por ejemplo, volver a intentar una llamada remota después de un error de red transitorio. Otras
mitigaciones son más estratégicas, como conmutar por error toda la aplicación a una región secundaria.
Las mitigaciones tácticas pueden marcar una gran diferencia. Aunque es raro que toda una región
experimente una interrupción, los problemas transitorios como la congestión de la red son más comunes,
así que primero se deben abordar estos. Contar con los métodos de supervisión y diagnóstico adecuados
también es importante, tanto para detectar errores cuando se producen como para dar con la causa raíz.

Al diseñar una aplicación para ser resistente, es necesario entender sus requisitos de disponibilidad.
¿Cuánto tiempo de inactividad es aceptable? Esto depende parcialmente del coste. ¿Cuánto costará el
tiempo de inactividad potencial a su empresa? ¿Cuánto debe invertir en hacer que la aplicación tenga alta
disponibilidad?

Use la Lista de comprobación de resistencia para revisar su diseño desde el punto de vista de la
resistencia.

99 CAPÍTULO 4 | Diseño de la aplicación Azure: uso de los pilares de la calidad


Guía de resistencia
●● Para obtener información sobre el diseño de aplicaciones resistentes para Azure, visite
https://docs.microsoft.com/en-us/azure/architecture/resiliency/index.
●● Patrones de diseño para la resistencia
●● Procedimientos recomendados: administración de errores transitorios, reintento de orientación para
servicios específicos

Procedimientos recomendados
●● https://docs.microsoft.com/en-us/azure/architecture/best-practices/transient-faults

●● https://docs.microsoft.com/en-us/azure/architecture/best-practices/retry-service-specific

Administración y DevOps
Este pilar abarca los procesos de operaciones que mantienen la ejecución de una aplicación en producción.
Las implementaciones deben ser fiables y predecibles. Deben estar automatizadas para reducir la
posibilidad de error humano. Deben ser un proceso rápido y rutinario, de modo que no retrasen el
lanzamiento de nuevas funciones o correcciones de errores. Igualmente importante, debe poder revertir o
avanzar en los cambios rápidamente si una actualización resulta problemática.

La supervisión y el diagnóstico son cruciales. Para ver procedimientos recomendados sobre supervisión y
diagnóstico, visite https://docs.microsoft.com/en-us/azure/architecture/best-practices/monitoring. Las
aplicaciones en el cloud se ejecutan en un centro de datos remoto en el que no tendrá el control completo
de la infraestructura o, en algunos casos, el sistema operativo. En una aplicación grande, no es práctico
iniciar sesión en varias VM para solucionar un problema o analizar archivos de registro. Con los servicios
PaaS, podría no haber ni siquiera una VM específica en la que iniciar sesión. La supervisión y el diagnóstico
aportan conocimientos sobre el sistema, de modo que podrá saber dónde y cuándo se producen los
errores. Todos los sistemas deben ser observables. Utilice un esquema de registro común y coherente que
le permita correlacionar los eventos en varios sistemas.

El proceso de supervisión y diagnóstico tiene varias fases diferentes:


●● Instrumentación. Generación de los datos en bruto a partir de los registros de aplicación, registros de
servidores web, diagnósticos integrados en la plataforma de Azure y otras fuentes.
●● Recopilación y almacenamiento. Consolidación de los datos en un solo lugar.
●● Análisis y diagnóstico. Para solucionar problemas y analizar el estado general.
●● Visualización y alertas. Uso de datos de telemetría para detectar tendencias o alertar al equipo de
operaciones.

Utilice la lista de comprobación de DevOps para revisar su diseño desde un punto de vista de
administración y DevOps.

Orientación sobre administración y DevOps


●● Patrones de diseño para la administración y la supervisión
●● Procedimientos recomendados: supervisión y diagnóstico

100 CAPÍTULO 4 | Diseño de la aplicación Azure: uso de los pilares de la calidad


Seguridad
Debe pensar en la seguridad a lo largo de todo el ciclo de vida de una aplicación, desde el diseño y la
implementación al despliegue y las operaciones. La plataforma Azure proporciona protección contra una
gran variedad de amenazas, como la intrusión en la red y los ataques DDoS. Aun así, tendrá que integrar
la seguridad en su aplicación y en sus procesos de DevOps.

A continuación presentamos algunos ámbitos de seguridad generales que debería tener en cuenta.

Administración de identidades
Piense en recurrir a Azure Active Directory (Azure AD) para autenticar y autorizar a los usuarios. Azure
AD es un servicio de identidad y gestión de accesos totalmente administrado. Puede utilizarlo para crear
dominios que existan únicamente en Azure, o integrarlo con sus identidades locales de Active Directory.
Azure AD también se integra con Office365, Dynamics CRM Online y muchas aplicaciones SaaS de
terceros. En el caso de las aplicaciones orientadas al consumidor, Azure Active Directory B2C permite
a los usuarios autenticarse con sus cuentas en redes sociales (Facebook, Google o LinkedIn, por ejemplo)
o crear una nueva cuenta de usuario administrada por Azure AD.

Si desea integrar un entorno de Active Directory local con una red Azure, hay varios enfoques posibles,
dependiendo de sus necesidades. Para obtener más información, consulte nuestras arquitecturas de
referencia de administración de identidad.

Protección de su infraestructura
Controle el acceso a los recursos de Azure que implemente. Todas las suscripciones de Azure tienen una
relación de confianza con un inquilino de Azure AD. Utilice el control de acceso basado en roles (RBAC)
para conceder a los usuarios de su organización los permisos correctos para los recursos de Azure. Conceda
accesos asignando un rol de RBAC a usuarios o grupos de un ámbito determinado. Este ámbito puede ser el
de una suscripción, un grupo de recursos o un único recurso. Audite todos los cambios a la infraestructura.
Para obtener más información, visite https://docs.microsoft.com/en-us/azure/active- directory/.

Seguridad de aplicaciones
Por lo general, los procedimientos recomendados de seguridad para desarrollo de aplicaciones siguen
aplicándose al cloud. Entre ellas se incluyen el uso de SSL en todas partes, la protección frente a ataques
CSRF y XSS, la prevención de ataques de inyección por SQL, etc.

Las aplicaciones en el cloud suelen usar servicios administrados con claves de acceso. Nunca las
introduzca en el código fuente. Piense en almacenar los secretos de las aplicaciones en Azure Key Vault.

Cifrado y soberanía de los datos


Asegúrese de que sus datos permanezcan en la zona geopolítica correcta al utilizar la alta disponibilidad
de Azure. El almacenamiento georreplicado de Azure utiliza el concepto de región emparejada en la
misma región geopolítica.

Puede utilizar Key Vault para proteger claves y secretos criptográficos. Al utilizar Key Vault, podrá cifrar
claves y secretos mediante el uso de claves protegidas por módulos de seguridad de hardware (HSM).
Muchos servicios de almacenamiento y bases de datos de Azure admiten el cifrado de datos en reposo,
incluidos Azure Storage, Azure SQL Database, Azure SQL Data Warehouse y Cosmos DB.

101 CAPÍTULO 4 | Diseño de la aplicación Azure: uso de los pilares de la calidad


Para obtener más información, consulte:

●● https://docs.microsoft.com/en-us/azure/storage/storage-service-encryption
●● https://docs.microsoft.com/en-us/azure/sql-database/sql-database-always-encrypted-azure-key-
vault
●● https://docs.microsoft.com/en-us/azure/data-lake-store/data-lake-store-security-overview#data-
protection
●● https://docs.microsoft.com/en-us/azure/cosmos-db/database-security

Recursos de seguridad
●● Azure Security Center {sin enlace} proporciona supervisión de seguridad integrada y administración
de políticas en todas sus suscripciones de Azure. Visite https://azure.microsoft.com/en-us/services/
security-center/.

●● Para obtener información sobre cómo proteger sus aplicaciones en el cloud, consulte https://docs.
microsoft.com/en-us/azure/security/.

102 CAPÍTULO 4 | Diseño de la aplicación Azure: uso de los pilares de la calidad


5

Diseño de la
aplicación Azure:
patrones de diseño
Estos patrones de diseño resultan útiles a la hora de crear aplicaciones
fiables, escalables y seguras en el cloud.
Cada patrón describe el problema que aborda, incluye consideraciones para aplicarlo y un ejemplo basado
en Microsoft Azure. La mayoría de los patrones incluyen ejemplos de código o fragmentos de código que
muestran cómo implementar el patrón en Azure. Sin embargo, la mayoría de los patrones son aptos para
cualquier sistema distribuido, ya se aloje en Azure o en otras plataformas del cloud.

Dificultades en el desarrollo en el cloud


Disponibilidad
La disponibilidad define la proporción de tiempo que el sistema es funcional y operativo. Se ve afectada
por los errores del sistema, los problemas de infraestructura, los ataques malintencionados y la carga del
sistema. Generalmente se mide como un porcentaje del tiempo de actividad. Las aplicaciones en el cloud
normalmente facilitan a los usuarios un acuerdo de nivel de servicio (SLA), lo que implica que deben estar
diseñadas e implementadas de forma que aumenten al máximo su disponibilidad.

Patrón Resumen
Supervisión de punto Implementa comprobaciones funcionales en una aplicación a la que
de conexión de estado pueden acceder herramientas externas mediante puntos de conexión
expuestos a intervalos regulares.

Nivelación de carga basada Utiliza una cola que actúe como búfer entre una tarea y un servicio
en cola que esta invoque para suavizar cargas intensas intermitentes.

Limitación Controla el consumo de recursos que utiliza una instancia de una


aplicación, un inquilino individual o un servicio completo.

103 CAPÍTULO 5 | Diseño de la aplicación Azure: patrones de diseño


Administración de datos
La administración de datos es el elemento clave de las aplicaciones en el cloud e influye en la mayoría de
los atributos de calidad. Normalmente, los datos se alojan en lugares diferentes y en varios servidores por
razones como rendimiento, escalabilidad o la disponibilidad, y esto puede presentar varios desafíos. Por
ejemplo, se debe mantener la coherencia de los datos, y estos normalmente tendrán que sincronizarse
entre las diversas ubicaciones.

Patrón Resumen
Cache-Aside Carga datos bajo demanda en una caché desde un almacén de datos.

CQRS Segrega las operaciones que leen los datos de las operaciones que
actualizan los datos mediante interfaces separadas.

Abastecimiento Utiliza un almacén exclusivamente de anexado para registrar la serie


de eventos completa de eventos que describen las acciones realizadas sobre los datos
en un dominio.

Tabla de índices Crea índices sobre los campos de los almacenes de datos a los que se suele
hacer referencia en las solicitudes.

Vista materializada Genera vistas precompletadas sobre los datos en uno o más almacenes de
datos cuando los datos no tienen el formato ideal para las operaciones de
consulta necesarias.

Particionamiento Divide un almacén de datos en un conjunto de particiones horizontales.

Hosting de contenido Implementa contenido estático en un servicio de almacenamiento en el


estático cloud que puede enviarlo directamente al cliente.

Valet Key Utiliza un token o una clave que proporciona a los clientes acceso directo
restringido a un recurso o servicio específicos.

Diseño e implementación
Un buen diseño abarca factores tales como la consistencia y la coherencia en el diseño y la
implementación de componentes, la capacidad de mantenimiento para simplificar la administración
y el desarrollo y la capacidad de reutilización para permitir que los componentes y los subsistemas se
utilicen en otras aplicaciones y en otros escenarios. Las decisiones tomadas durante la fase de diseño e
implementación tienen un impacto enorme en la calidad y el coste total de propiedad de las aplicaciones
y servicios alojados en el cloud.

Patrón Resumen
Embajador Crea servicios auxiliares que envían solicitudes de red en nombre
de un servicio o una aplicación de consumidor.

Capa para evitar daños Implementa una fachada o capa de adaptación entre una aplicación
moderna y un sistema heredado.

Back-ends for Front-ends Crea servicios back-end separados para que los consuman aplicaciones
o interfaces específicas del front-end.

104 CAPÍTULO 5 | Diseño de la aplicación Azure: patrones de diseño


CQRS Segrega las operaciones que leen los datos de las operaciones que
actualizan los datos mediante interfaces separadas.

Consolidación de Consolida varias tareas u operaciones en una sola unidad computacional.


recursos de proceso

Almacén de Desplaza la información de configuración fuera del paquete de


configuración externa implementación de la aplicación a una ubicación centralizada.

Agregación de puertas Utiliza una puerta de entrada para combinar varias solicitudes individuales
de enlace en una única solicitud.

Descarga de puertas de Descarga funcionalidades de servicio compartidas o especializadas a un


enlace proxy de puerta de enlace.

Enrutamiento de puertas Enruta las solicitudes a varios servicios mediante un único punto de
de enlace conexión.

Elección de líder Coordina las acciones realizadas por una colección de instancias de tareas
colaboración en una aplicación distribuida eligiendo una instancia como
líder que asume la responsabilidad de administrar las demás instancias.

Tuberías y filtros Descompone una tarea que realiza un procesamiento complejo en una
serie de elementos separados que pueden reutilizarse.

Sidecar Implementa los componentes de una aplicación en un proceso o


contenedor separado para proporcionar aislamiento y encapsulamiento.

Hosting de contenido Implementa contenido estático en un servicio de almacenamiento en el


estático cloud que puede enviarlo directamente al cliente.

Strangler Migra progresivamente un sistema heredado reemplazando gradualmente


las partes específicas de funcionalidades con nuevas aplicaciones y servicios.

Mensajería
La naturaleza distribuida de las aplicaciones en el cloud requiere una infraestructura de mensajería
que conecte los componentes y los servicios, idealmente con un emparejamiento flexible, con el fin
de maximizar la escalabilidad. La mensajería asincrónica se utiliza ampliamente y proporciona muchas
ventajas, pero también presenta grandes desafíos, como el orden de los mensajes, la administración
de mensajes dudosos, la idempotencia y mucho más.

Patrón Resumen
Consumidores en Permite que varios consumidores simultáneos procesen los mensajes
competencia recibidos en el mismo canal de mensajería.

Tuberías y filtros Descompone una tarea que realiza un procesamiento complejo en una serie
de elementos separados que pueden reutilizarse.

Cola de prioridad Prioriza las solicitudes enviadas a los servicios de modo que las solicitudes
con una prioridad más alta se reciban y procesen más rápidamente que las
que tienen menor prioridad.

105 CAPÍTULO 5 | Diseño de la aplicación Azure: patrones de diseño


Nivelación de carga Utiliza una cola que actúe como búfer entre una tarea y un servicio que
basada en cola esta invoque para suavizar cargas intensas intermitentes.

Supervisor de agente Coordina un conjunto de acciones a través de un conjunto distribuido


programador de servicios y otros recursos remotos.

Administración y supervisión
Las aplicaciones en el cloud se ejecutan en un centro de datos remoto en el que no tendrá el control
completo de la infraestructura o, en algunos casos, el sistema operativo. Esto puede hacer que la
administración y la supervisión sean más difíciles que una implementación local. Las aplicaciones deben
exponer información de tiempo de ejecución que los administradores y operadores puedan utilizar para
administrar y supervisar el sistema, además de para apoyar los cambiantes requisitos empresariales y la
personalización sin necesidad de que la aplicación se detenga o se reimplemente.

Patrón Resumen
Embajador Crea servicios auxiliares que envían solicitudes de red en nombre de un
servicio o una aplicación de consumidor.

Capa para evitar daños Implementa una fachada o capa de adaptación entre una aplicación
moderna y un sistema heredado.

Almacén de Desplaza la información de configuración fuera del paquete de


configuración externa implementación de la aplicación a una ubicación centralizada.

Agregación de puertas Utiliza una puerta de entrada para combinar varias solicitudes individuales
de enlace en una única solicitud.

Descarga de puertas Descarga funcionalidades de servicio compartidas o especializadas a un


de enlace proxy de puerta de enlace.

Enrutamiento de puertas Enruta las solicitudes a varios servicios mediante un único punto de
de enlace conexión.

Supervisión de punto Implementa comprobaciones funcionales en una aplicación a la que pueden


de conexión de estado acceder herramientas externas mediante puntos de conexión expuestos a
intervalos regulares.

Sidecar Implementa los componentes de una aplicación en un proceso o


contenedor separado para proporcionar aislamiento y encapsulamiento.

Strangler Migra progresivamente un sistema heredado reemplazando gradualmente las


partes específicas de funcionalidades con nuevas aplicaciones y servicios.

106 CAPÍTULO 5 | Diseño de la aplicación Azure: patrones de diseño


Rendimiento y escalabilidad
El rendimiento es un indicativo de la capacidad de respuesta de un sistema para ejecutar cualquier
acción en un intervalo de tiempo dado, mientras que la escalabilidad es la capacidad de un sistema
de afrontar los incrementos de carga sin que se vea afectado el rendimiento o de que los recursos
disponibles aumenten. Las aplicaciones en el cloud suelen enfrentarse a cargas de trabajo variables y
picos de actividad. La predicción de los mismos, especialmente en un escenario con múltiples usuarios,
es prácticamente imposible. Por tanto, las aplicaciones deben poder ampliar su capacidad dentro de
sus límites para satisfacer los picos de demanda y reducir su escala en cuando disminuye la demanda.
La escalabilidad no solo afecta a las instancias de proceso, sino también a otros elementos como el
almacenamiento de datos, la infraestructura de mensajería, y varios más.

Patrón Resumen
Cache-Aside Carga datos bajo demanda en una caché desde un almacén de datos.

CQRS Segrega las operaciones que leen los datos de las operaciones que
actualizan los datos mediante interfaces separadas.

Abastecimiento Utiliza un almacén exclusivamente de anexado para registrar la serie


de eventos completa de eventos que describen las acciones realizadas sobre los datos
en un dominio.

Tabla de índices Crea índices sobre los campos de los almacenes de datos a los que se suele
hacer referencia en las solicitudes.

Vista materializada Genera vistas precompletadas sobre los datos en uno o más almacenes de
datos cuando los datos no tienen el formato ideal para las operaciones de
consulta necesarias.

Cola de prioridad Prioriza las solicitudes enviadas a los servicios de modo que las solicitudes
con una prioridad más alta se reciban y procesen más rápidamente que las
que tienen menor prioridad.

Nivelación de carga Utiliza una cola que actúe como búfer entre una tarea y un servicio que esta
basada en cola invoque para suavizar cargas intensas intermitentes.

Particionamiento Divide un almacén de datos en un conjunto de particiones horizontales.

Hosting de contenido Implementa contenido estático en un servicio de almacenamiento en el


estático cloud que puede enviarlo directamente al cliente.

Limitación Controla el consumo de recursos que utiliza una instancia de una


aplicación, un inquilino individual o un servicio completo.

107 CAPÍTULO 5 | Diseño de la aplicación Azure: patrones de diseño


Resistencia
La resistencia es la capacidad de un sistema de afrontar fácilmente errores y recuperarse de ellos. La
naturaleza del hosting en el cloud, donde las aplicaciones suelen tener varios inquilinos, usar servicios
de plataforma compartidos, competir por los recursos y el ancho de banda, comunicarse por Internet y
ejecutarse en hardware básico, implica que hay una gran probabilidad de que aparezcan errores tanto
transitorios como permanentes. La detección de errores y una recuperación rápida y eficaz son necesarias
para mantener la resistencia.

Patrón Resumen

Bulkhead Aísla los elementos de una aplicación en grupos, de modo que si uno falla,
los demás seguirán funcionando.

Interruptor de circuito Administra los errores que se podría tardar una cantidad de tiempo variable
en arreglar al conectarse a un servicio o recurso remoto.

Transacción de Deshace el trabajo realizado mediante una serie de pasos que, en conjunto,
compensación definen una operación finalmente coherente.

Supervisión de punto Implementa comprobaciones funcionales en una aplicación a la que pueden


de conexión de estado acceder herramientas externas mediante puntos de conexión expuestos a
intervalos regulares.

Elección de líder Coordina las acciones realizadas por una colección de instancias de tareas
colaboración en una aplicación distribuida eligiendo una instancia como
líder que asume la responsabilidad de administrar las demás instancias.

Nivelación de carga Utiliza una cola que actúe como búfer entre una tarea y un servicio que esta
basada en cola invoque para suavizar cargas intensas intermitentes.

Reintento Permite que una aplicación pueda manejar los fallos temporales
esperados cuando intenta conectarse a un servicio o recurso de red
volviendo a intentar de manera transparente una operación que ha fallado
anteriormente.

Supervisor de agente Coordina un conjunto de acciones a través de un conjunto distribuido de


programador servicios y otros recursos remotos.

108 CAPÍTULO 5 | Diseño de la aplicación Azure: patrones de diseño


Seguridad
La seguridad es la capacidad de un sistema para evitar acciones malintencionadas o accidentales fuera
de su uso previsto y para evitar la divulgación o la pérdida de información. Las aplicaciones en el cloud
se exponen en Internet fuera de los límites locales de confianza, suelen estar abiertas al público y pueden
prestar servicio a usuarios no fiables. Las aplicaciones deben diseñarse e implementarse de manera que
queden protegidas de ataques malintencionados, restringe el acceso a solo los usuarios autorizados
y protege los datos confidenciales.

Patrón Resumen
Identidad federada Delega la autenticación a un proveedor de identidad externo.

Gatekeeper Protege las aplicaciones y servicios mediante el uso de una instancia


de host dedicada que actúa como intermediaria entre los clientes y la
aplicación o el servicio, valida y verifica las solicitudes y envía solicitudes
y datos entre ellos.

Valet Key Utiliza un token o una clave que proporciona a los clientes acceso directo
restringido a un recurso o servicio específicos.

109 CAPÍTULO 5 | Diseño de la aplicación Azure: patrones de diseño


6

Catálogo de patrones
Patrón embajador
Crea servicios auxiliares que envían solicitudes de red en nombre de un servicio o una aplicación de
consumidor. Un servicio de embajador puede considerarse como un proxy fuera de proceso colocado
con el cliente.

Este patrón puede ser útil para la descarga de tareas comunes de conectividad del cliente como la
supervisión, el registro, el enrutamiento, la seguridad (como TLS) y los patrones de resistencia de forma
independiente al lenguaje. A menudo se utiliza con aplicaciones heredadas u otras aplicaciones que
resultan difíciles de modificar, con el fin de ampliar sus capacidades de interconexión. También puede
permitir que un equipo especializado implemente esas características.

Contexto y problema
Las aplicaciones resistentes basadas en el cloud requieren características como interrupción de circuito,
enrutamiento, medición y supervisión, además de la capacidad de realizar actualizaciones de configuración
relacionadas con la red. Puede ser difícil o imposible actualizar aplicaciones heredadas
o bibliotecas de código existentes para añadir estas características, porque el código ya no se mantiene
o no puede ser fácilmente modificado por el equipo de desarrollo.

Las llamadas de red también pueden requerir una labor de configuración sustancial para la conexión,
la autenticación y la autorización. Si estas llamadas se utilizan en varias aplicaciones, se crean utilizando
varios lenguajes y marcos, las llamadas deben configurarse para cada una de estas instancias. Además,
la funcionalidad de red y seguridad debe estar administrada por un equipo central dentro de su
organización. Con una base de código grande, puede resultar arriesgado que ese equipo actualice
el código de la aplicación si no está familiarizado con él.

Solución
Ponga los marcos y las bibliotecas del cliente en un proceso externo que actúa como proxy entre su
aplicación y los servicios externos. Implementa el proxy en el mismo entorno de host que su aplicación,
para permitir el control sobre el enrutamiento, la resistencia y las características de seguridad y para evitar
cualquier restricción de acceso relacionada con el host. También puede utilizar el patrón embajador para
estandarizar y ampliar la instrumentación. El proxy puede supervisar las métricas de rendimiento, como la
latencia o el uso de recursos, y esta supervisión se produce en el mismo entorno del host que la aplicación.

110 CAPÍTULO 6 | Catálogo de patrones


Las características que se descargan al embajador pueden administrarse de forma independiente de la
aplicación. Puede actualizar y modificar el embajador sin alterar la funcionalidad heredada de la aplicación.
También permite que equipos independientes y especializados implementen y mantengan características
de seguridad, redes o autenticación que se hayan desplazado al embajador.

Los servicios de embajador se pueden implementar como sidecar para acompañar el ciclo de vida de
una aplicación o servicio de consumo. Alternativamente, si varios procesos independientes comparten un
embajador en un host común, se puede implementar como un daemon o un servicio de Windows. Si el
servicio de consumo está en contenedor, el embajador debe crearse como un contenedor independiente
en el mismo host, con los enlaces apropiados configurados para la comunicación.

Problemas y consideraciones
●● El proxy añade cierta sobrecarga de latencia. Piense si una biblioteca de cliente, que invoca
directamente la aplicación, es un mejor enfoque.

●● Tenga en cuenta el posible impacto de incluir características generalizadas en el proxy. Por ejemplo,
el embajador podría administrar los reintentos, pero quizás eso no sea seguro, a no ser que todas las
operaciones sean idempotentes.

●● Piense en un mecanismo que permita que el cliente pase contexto al proxy y lo devuelva al cliente.
Por ejemplo, incluya encabezados de solicitud HTTP para poder decidir no reintentar o especifique
el número máximo de reintentos.

●● Piense en cómo va a empaquetar e implementar el proxy.

●● Piense en la posibilidad de utilizar una única instancia compartida para todos los clientes o una
instancia para cada cliente.

Cuándo utilizar este patrón


Puede utilizar este patrón en los siguientes casos:

●● Si necesita crear un conjunto común de características de conectividad de cliente para varios lenguajes
o marcos.
●● Si necesita descargar problemas transversales de conectividad del cliente a desarrolladores de
infraestructuras u otros equipos más especializados.
●● Si necesita dar soporte a los requisitos de conectividad del cloud o el clúster en una aplicación
heredada o una aplicación que resulte difícil modificar.

Este patrón puede no ser adecuado:

●● Cuando la latencia de la solicitud de red es crítica. Un proxy introducirá sobrecarga, aunque sea
mínima, y en algunos casos esto podría afectar a la aplicación.

●● Cuando las funciones de conectividad del cliente se consumen en un único lenguaje. En ese caso,
podría ser mejor opción una biblioteca cliente que se distribuye a los equipos de desarrollo como
un paquete.

●● Cuando las funciones de conectividad no se pueden generalizar y requieren una integración más
profunda con la aplicación cliente.

111 CAPÍTULO 6 | Catálogo de patrones


Ejemplo
El siguiente diagrama muestra una aplicación que hace una solicitud a un servicio remoto a través de un
proxy de embajador. El embajador proporciona enrutamiento, interrupción de circuito y registro. Llama al
servicio remoto y devuelve la respuesta a la aplicación cliente:

Patrón de capa para evitar daños


Implemente una capa de fachada o adaptación entre una aplicación moderna y un sistema heredado del
que dependa. Esta capa traduce las peticiones entre la aplicación moderna y el sistema heredado. Utilice
este patrón para garantizar que el diseño de una aplicación no esté limitado por dependencias en sistemas
heredados.

Contexto y problema
La mayoría de las aplicaciones dependen de otros sistemas para obtener algunos datos o funcionalidades.
Por ejemplo, cuando una aplicación heredada se migra a un sistema moderno, podría seguir necesitando
los recursos heredados existentes. Las nuevas características deberían poder llamar al sistema heredado.
Esto se pone de especial relevancia en las migraciones graduales, en las que las diversas características
de una aplicación mayor se van desplazando a un sistema moderno a lo largo del tiempo.

A menudo, estos sistemas heredados experimentan problemas de seguridad, como esquemas de datos
enrevesados o API obsoletas. Las características y tecnologías utilizadas en sistemas heredados pueden
variar enormemente de las de los sistemas más modernos. Para interoperar con el sistema heredado,
la nueva aplicación podría necesitar ser compatible con infraestructura, protocolos, modelos de datos,
API u otras características obsoletas que, de lo contrario, no pondría en una aplicación moderna.

El mantenimiento del acceso entre sistemas nuevos y heredados puede forzar que el nuevo sistema
se ajuste a al menos parte de las API u otros elementos semánticos del sistema heredado. Cuando estas
características heredadas tienen problemas de calidad, la compatibilidad con ellas "corrompe" lo que
de lo contrario podría ser una aplicación moderna con un diseño limpio.

Solución
Aísle los sistemas antiguos y los modernos, colocando una capa para evitar daños entre ellos. Esta capa
traduce las comunicaciones entre los dos sistemas, permitiendo que el sistema heredado permanezca sin
cambios, mientras la aplicación moderna puede evitar poner en riesgo su diseño y su enfoque tecnológico.

112 CAPÍTULO 6 | Catálogo de patrones


La comunicación entre la aplicación moderna y la capa para evitar daños siempre utiliza el modelo de
datos y la arquitectura de la aplicación. Las llamadas procedentes de la capa para evitar daños al sistema
heredado se ajustan al modelo de datos o los métodos de dicho sistema. La capa para evitar daños
contiene todos los elementos lógicos necesarios para la traducción entre los dos sistemas. La capa puede
implementarse como un componente dentro de la aplicación o como un servicio independiente.

Problemas y consideraciones
●● La capa para evitar daños podría añadir latencia a las llamadas realizadas entre los dos sistemas.

●● La capa para evitar daños añade un servicio adicional que debe administrarse y mantenerse.

●● Piense en el escalado potencial de su capa para evitar daños.

●● Piense si necesita más de una capa para evitar daños. Es posible que quiera descomponer la
funcionalidad en varios servicios con distintas tecnologías o lenguajes, o podría haber otros motivos
para crear particiones en la capa para evitar daños.

●● Piense cómo se administrará la capa para evitar daños en relación con sus otras aplicaciones
o servicios. ¿Cómo se integrará en sus procesos de supervisión, publicación y configuración?

●● Asegúrese de que la coherencia de transacciones y datos se mantenga y pueda supervisarse.

●● Compruebe si su capa para evitar daños ha de administrar toda la comunicación entre los sistemas
heredados y modernos, o simplemente un subconjunto de características.

●● Tenga en cuenta si la capa para evitar daños va a ser permanente o se prevé que se retire tras haber
migrado todas las funcionalidades heredadas.

113 CAPÍTULO 6 | Catálogo de patrones


Cuándo utilizar este patrón
Puede utilizar este patrón en los siguientes casos:

●● Si se ha previsto una migración en varias etapas, pero debe mantenerse la integración entre los
sistemas nuevos y los heredados.
●● Si el sistema nuevo y el heredado tienen una semántica diferente, pero siguen teniendo que
comunicarse.

Este patrón puede no resultar conveniente si no hay diferencias semánticas significativas entre los sistemas
nuevos y los heredados.

Patrón Back-ends for Front-ends


Crea servicios back-end separados para que los consuman aplicaciones o interfaces específicas del front-end.
Este patrón resulta útil si quiere evitar la personalización de un único back-end para varias interfaces.

Contexto y problema
Una aplicación podría orientarse inicialmente a una IU web para escritorio. Por lo general, se desarrolla
en paralelo un servicio de back-end que proporciona las características necesarias para esa interfaz de
usuario. A medida que crece la base de usuarios de la aplicación, se desarrolla una aplicación móvil que
debe interactuar con el mismo back-end. El servicio de back-end se convierte en un back-end con fines
generales y que sirve a los requisitos de las interfaces móviles y de escritorio.

Sin embargo, las capacidades de un dispositivo móvil difieren significativamente de las de un navegador de
escritorio, en el tamaño de la pantalla, el rendimiento y las limitaciones de pantalla. Como resultado,
los requisitos para un back-end de una aplicación móvil difieren de la interfaz de usuario web para escritorio.

Estas diferencias resultan en requisitos enfrentados para el back-end. El back-end requiere cambios
regulares y significativos para hacer frente tanto a la IU web como a la aplicación móvil. A menudo,
en cada front-end trabajan equipos de interfaz separados, haciendo que el back-end se convierta en un
cuello de botella en el proceso de desarrollo. Los requisitos de actualización en conflicto y la necesidad
de que el servicio siga trabajando en ambos front-ends pueden dar como resultado una gran inversión
de esfuerzo en un único recurso implementable.

A medida que la actividad de desarrollo se centra en el servicio de back-end, podría quedarse un equipo
independiente para administrar y mantener el back-end. En definitiva, esto resulta en una desconexión
entre los equipos de desarrollo de la interfaz y el back-end, lo que resulta un lastre para el equipo back-end
a la hora de equilibrar los requisitos enfrentados de los diferentes equipos de interfaz de usuario. Cuando
un equipo de interfaz solicita cambios al back-end, esos cambios deben validarse con otros equipos de
interfaz antes de que se puedan integrar en el back-end.

114 CAPÍTULO 6 | Catálogo de patrones


Solución
Cree un back-end por interfaz de usuario. Afine el comportamiento y el rendimiento de cada back-
end para satisfacer mejor las necesidades del entorno front-end, sin preocuparse de que afecte a otras
experiencias del front-end.

Dado que cada back-end es específico para una interfaz, puede optimizarse para dicha interfaz. Como
resultado, será más pequeño, menos complejo y probablemente más rápido que un back-end genérico
que intente satisfacer las necesidades de todas las interfaces. Cada equipo de interfaz tiene autonomía
para controlar su propio back-end y no depende de un equipo de desarrollo de back-end centralizado.
Esto le da al equipo de interfaz flexibilidad en la selección del lenguaje, la cadencia de lanzamiento,
la priorización de la carga de trabajo y la integración de las características en su back-end.

Problemas y consideraciones
●● Tenga en cuenta cuándos back-ends implementar.

●● Si hay diferentes interfaces (por ejemplo, clientes móviles) que realizarán las mismas solicitudes,
piense si es necesario implementar un back-end para cada interfaz o si bastará con un solo back-end.

●● La duplicación de código entre servicios es muy probable al implementar este patrón.

●● Los servicios de back-end centrados en front-end solo deben contener lógica y comportamientos
específicos al cliente. La lógica empresarial general y otras características globales deben
administrarse en otras partes de la aplicación.

●● Piense en cómo podría reflejarse este patrón en las responsabilidades de un equipo de desarrollo.

●● Piense en cuánto tiempo tardaría en implementar este patrón. ¿El esfuerzo de construir los nuevos
back-ends le hará incurrir en una deuda técnica, mientras continúa dando soporte al back-end
genérico existente?

115 CAPÍTULO 6 | Catálogo de patrones


Cuándo utilizar este patrón
Puede utilizar este patrón en los siguientes casos:

●● Debe mantenerse un servicio de back-end de propósito general o compartido con una sobrecarga
de desarrollo significativa.

●● Quiere optimizar el back-end para los requisitos de interfaces de cliente específicas.

●● Las personalizaciones se realizan en un back-end de uso general para dar cabida a varias interfaces.

●● Un lenguaje alternativo resulta más adecuado para el back-end de una interfaz de usuario diferente.

Este patrón puede no ser adecuado:


●● Cuando las interfaces realizan las mismas solicitudes o solicitudes similares al back-end.

●● Cuando se utiliza solamente una interfaz para interactuar con el back-end.

Orientación relacionada
●● Patrón de agregación de puerta de enlace
●● Patrón de descarga de puerta de enlace
●● Patrón de enrutamiento de puerta de enlace

Patrón Bulkhead
Aísla los elementos de una aplicación en grupos, de modo que si uno falla, los demás seguirán
funcionando.

Este patrón se denomina Bulkhead (mamparo) porque se asemeja a la división de las secciones del casco
de un barco. Si existe riesgo para el casco de un barco, solo la sección deteriorada se llenará con agua,
lo que evitará que el barco se hunda.

Contexto y problema
Una aplicación basada en el cloud puede incluir múltiples servicios y cada uno de ellos podría tener uno
o varios consumidores. Una carga excesiva o un error en un servicio afectará a todos los consumidores del
servicio.

Además, un consumidor puede enviar solicitudes a varios servicios simultáneamente, utilizando recursos
para cada solicitud. Cuando el consumidor envía una solicitud a un servicio que está mal configurado
o no responde, los recursos utilizados por la solicitud del cliente no pueden liberarse de forma puntual.
A medida que continúan las solicitudes a dicho servicio, los recursos pueden agotarse. Por ejemplo,
el grupo de conexiones del cliente podría agotarse. En ese momento, las solicitudes que realiza
el consumidor a otros servicios se ven afectadas. Finalmente, el consumidor ya no podrá enviar solicitudes
a ningún servicio, no simplemente al servicio que no respondía originalmente.

El mismo problema de agotamiento de recursos afecta a servicios con varios consumidores. Un gran
número de solicitudes procedentes de un cliente puede agotar los recursos disponibles en el servicio.
Otros consumidores ya no podrán consumir el servicio, causando un efecto de error en cascada.

116 CAPÍTULO 6 | Catálogo de patrones


Solución
Cree particiones de las instancias de servicio y divídalas en grupos, en función de la carga de
consumidores y los requisitos de disponibilidad. Este diseño ayuda a aislar errores y le permite mantener
la funcionalidad del servicio para algunos consumidores, incluso durante un error.

Un consumidor también puede crear particiones de recursos, para garantizar que los recursos utilizados
para llamar a un servicio no afecten a los recursos que utiliza para llamar a otro servicio. Por ejemplo,
a un consumidor que llama a varios servicios se le puede asignar un grupo de conexiones para cada
servicio. Si un servicio empieza a fallar, solo afectará al grupo de conexiones asignado a dicho servicio,
por lo que permitirá al consumidor seguir usando los demás servicios.

Los beneficios de este modelo incluyen:


●● Aísla los consumidores y a los servicios de los errores en cascada. Un problema que afecte a un consumidor
o un servicio se puede aislar en su propio bulkhead, y así se evita que falle la totalidad de la solución.
●● Permite conservar algunas funciones en caso de un error de servicio. Otros servicios y funciones
de la aplicación seguirán funcionando.
●● Le permite implementar servicios que ofrecen calidades de servicio diferentes para las aplicaciones
consumidoras. Se puede configurar un grupo de consumidores de alta prioridad para usar servicios
de alta prioridad.
El siguiente diagrama muestra bulkheads estructurados en torno a grupos de conexiones que llaman a servicios
individuales. Si el servicio A falla o causa algún otro problema, el grupo de conexiones queda aislado, por lo
que solo se verán afectadas las cargas de trabajo que usan el grupo de subprocesos asignados al servicio A. Las
cargas de trabajo que usan los servicios B y C no se ven afectadas y pueden seguir trabajando sin interrupción.

El siguiente diagrama muestra a varios clientes llamando a un único servicio. A cada cliente se le asigna
una instancia de servicio independiente. El cliente 1 ha hecho demasiadas solicitudes y ha sobrecargado
su instancia. Dado que cada instancia de servicio está aislada de las demás, los otros clientes pueden
seguir realizando llamadas.

117 CAPÍTULO 6 | Catálogo de patrones


Problemas y consideraciones
●● Defina las particiones en torno a los requisitos empresariales y técnicos de la aplicación.

●● Cuando cree particiones de servicios o consumidores en bulkheads, tenga en cuenta el nivel de


aislamiento que ofrece la tecnología, además de la sobrecarga en términos de coste, rendimiento
y manejabilidad.

●● Piense en combinar bulkheads con patrones de reintento, interruptor de circuito y limitación para
proporcionar una administración de errores más sofisticada.

●● Al crear particiones en bulkheads de los consumidores, tenga en cuenta el uso de procesos, grupos
de subprocesos y semáforos. Proyectos como Netflix Hystrix y Polly ofrecen un marco para la creación
de bulkheads de consumidores

●● Al crear particiones de servicios en bulkheads, piense en implementarlas en máquinas virtuales,


contenedores o procesos separados. Los contenedores ofrecen un buen equilibrio entre el aislamiento
de recursos y una sobrecarga razonablemente baja.

●● Los servicios que se comunican mediante mensajes asincrónicos pueden aislarse mediante diferentes
conjuntos de colas. Cada cola puede tener un conjunto dedicado de instancias de procesamiento
de mensajes en la cola, o un solo grupo de instancias que utilice un algoritmo para quitar elementos
de la cola y distribuir el procesamiento.

●● Determine el nivel de granularidad de los bulkheads. Por ejemplo, si desea distribuir inquilinos en
varias particiones, podría colocar a cada inquilino en una partición diferente, o a varios inquilinos en
una partición.

●● Supervise el rendimiento y el SLA de cada partición.

Cuándo utilizar este patrón


Puede utilizar este patrón en los siguientes casos:
●● Para aislar recursos que se utilicen para consumir un conjunto de servicios back-end, especialmente
si la aplicación puede facilitar cierto nivel de funcionalidad, incluso si uno de los servicios no responde.
●● Para aislar a los consumidores críticos de los consumidores estándares.
●● Para proteger la aplicación de loe errores en cascada.

Este patrón puede no ser adecuado:


●● Si un uso menos eficiente de los recursos no sería aceptable en el proyecto.
●● Si no es necesario aumentar la complejidad.

118 CAPÍTULO 6 | Catálogo de patrones


Ejemplo
El siguiente archivo de configuración de Kubernetes crea un contenedor aislado para ejecutar un servicio
único, con sus propios recursos y límites de CPU y memoria.

apiVersion: v1
kind: Pod
metadata:
name: drone-management
spec:
containers:
- name: drone-management-container
image: drone-service
resources:
requests:
memory: “64Mi”
cpu: “250m”
limits:
memory: “128Mi”
cpu: “1”

Orientación relacionada

●● Patrón de interruptor de circuito


●● Diseño de aplicaciones flexibles para Azure
●● Patrón de reintento
●● Patrón de limitación

Patrón Cache-Aside
Carga datos bajo demanda en una caché desde un almacén de datos. Esto puede mejorar el rendimiento
y también ayuda a mantener la coherencia entre los datos guardados en la caché y los datos guardados
en el almacén de datos subyacente.

Contexto y problema
Las aplicaciones utilizan una memoria caché para mejorar el acceso repetido a la información guardada
en un almacén de datos. Sin embargo, no resulta práctico esperar que los datos almacenados en
caché siempre sean completamente coherentes con los datos en el almacén de datos. Las aplicaciones
deben implementar una estrategia que ayude a garantizar que los datos en la memoria caché están tan
actualizados como sea posible, pero también puede detectar y gestionar las situaciones derivadas de
cuando los datos en caché se vuelven obsoletos.

Solución
Muchos sistemas comerciales de almacenamiento en caché proporcionan operaciones de lectura
sincrónica y escritura sincrónica y asincrónica. En estos sistemas, una aplicación recupera los datos
mediante una referencia a la memoria caché. Si los datos no están en la caché, se recuperan del almacén
de datos y se añaden a la caché. Cualquier modificación a los datos almacenados en la caché se vuelven
a escribir automáticamente también en el almacén de datos.

119 CAPÍTULO 6 | Catálogo de patrones


Para las cachés que no proporcionan esta funcionalidad, es responsabilidad de las aplicaciones que
utilizan la caché mantener los datos.

Una aplicación puede emular la funcionalidad de almacenamiento en caché de la lectura sincrónica


mediante la implementación de la estrategia cache-aside. Esta estrategia carga datos en la caché bajo
demanda. El gráfico ilustra el uso del patrón cache-aside para almacenar datos en la caché.

Si una aplicación actualiza información, puede seguir la estrategia de escritura sincrónica realizando
la modificación al almacén de datos e invalidando el elemento correspondiente en la memoria caché.

Cuando se vuelve a solicitar el elemento, el uso de la estrategia cache-aside hará que los datos
actualizados se recuperen desde el almacén de datos y se vuelva a añadir a la caché.

Problemas y consideraciones
Tenga en cuenta los puntos siguientes a la hora de decidir cómo implementar este patrón:

Vida útil de los datos almacenados en caché. Muchas memorias caché implementan una política de
caducidad que invalida los datos y los elimina de la memoria si no se accede a ellos durante un periodo
determinado. Para que la cache-aside resulte eficaz, asegúrese de que la política de caducidad coincida
con el patrón de acceso de las aplicaciones que usan los datos. No cree un periodo de caducidad
demasiado corto, ya que esto puede provocar que las aplicaciones recuperen datos continuamente del
almacén de datos y los añadan a la caché. Del mismo modo, no haga que el periodo de caducidad sea tan
largo que los datos en caché acaben estando obsoletos. Recuerde que el almacenamiento en caché es más
eficaz para datos relativamente estáticos o para datos que se leen con frecuencia.

Expulsión de datos. La mayoría de cachés tienen un tamaño limitado en comparación con el almacén de
datos en el que se originan los datos y expulsarán datos si fuera necesario. La mayoría de cachés adoptan
una política de "uso más reciente" para seleccionar los elementos que expulsar, pero este comportamiento
se puede personalizar. Configure la propiedad de caducidad global, otras propiedades de la caché y
la propiedad de caducidad de cada elemento en caché para garantizar que la caché sea eficiente. No
siempre es apropiado aplicar una política global de expulsión a cada elemento de la caché. Por ejemplo,
si un elemento almacenado en caché resulta muy costoso de recuperar desde el almacén de datos, puede
ser beneficioso mantener este elemento en la caché a expensas de elementos a los que se accede con más
frecuencia pero que resultan menos costosos.

120 CAPÍTULO 6 | Catálogo de patrones


Desbloqueo de la caché. Muchas soluciones rellenan previamente la memoria caché con los datos que,
probablemente, necesite una aplicación como parte del proceso de arranque. El patrón cache-aside puede
seguir siendo útil si algunos de estos datos caducan o se expulsan.

Coherencia. La implementación del patrón cache-aside no garantiza la coherencia entre el almacén de


datos y la caché. Un elemento del almacén de datos puede cambiar en cualquier momento mediante
un proceso externo, y este cambio podría no reflejarse en la memoria caché hasta la próxima vez que se
cargue el elemento. En un sistema que replica datos en varios almacenes de datos, este problema puede
llegar a ser grave si la sincronización se produce con frecuencia.

Almacenamiento en caché local (in-memory) Una caché puede ser local en una instancia de la
aplicación y puede almacenarse in-memory. El cache-aside puede resultar útil en este entorno si una
aplicación accede repetidamente a los mismos datos. Sin embargo, una memoria caché local es privada y,
por tanto, las diferentes instancias de la aplicación podrían tener una copia de los mismos datos en caché.
Estos datos podrían resultar rápidamente incoherentes entre cachés, por lo que podría ser necesario que
caduquen los datos almacenados en una caché privada y actualizarlos más a menudo. En estos escenarios,
valore investigar el uso de un mecanismo de almacenamiento en caché compartido o distribuido.

Cuándo utilizar este patrón


Tenga en cuenta los puntos siguientes a la hora de decidir cómo implementar este patrón:

Vida útil de los datos almacenados en caché. Muchas memorias caché implementan una política de
caducidad que invalida los datos y los elimina de la memoria si no se accede a ellos durante un periodo
determinado. Para que la cache-aside resulte eficaz, asegúrese de que la política de caducidad coincida
con el patrón de acceso de las aplicaciones que usan los datos. No cree un periodo de caducidad
demasiado corto, ya que esto puede provocar que las aplicaciones recuperen datos continuamente del
almacén de datos y los añadan a la caché. Del mismo modo, no haga que el periodo de caducidad sea tan
largo que los datos en caché acaben estando obsoletos. Recuerde que el almacenamiento en caché es más
eficaz para datos relativamente estáticos o para datos que se leen con frecuencia.

Expulsión de datos. La mayoría de cachés tienen un tamaño limitado en comparación con el almacén de
datos en el que se originan los datos y expulsarán datos si fuera necesario. La mayoría de cachés adoptan
una política de "uso más reciente" para seleccionar los elementos que expulsar, pero este comportamiento
se puede personalizar. Configure la propiedad de caducidad global, otras propiedades de la caché y
la propiedad de caducidad de cada elemento en caché para garantizar que la caché sea eficiente. No
siempre es apropiado aplicar una política global de expulsión a cada elemento de la caché. Por ejemplo,
si un elemento almacenado en caché resulta muy costoso de recuperar desde el almacén de datos, puede
ser beneficioso mantener este elemento en la caché a expensas de elementos a los que se accede con más
frecuencia pero que resultan menos costosos.

Desbloqueo de la caché. Muchas soluciones rellenan previamente la memoria caché con los datos que,
probablemente, necesite una aplicación como parte del proceso de arranque. El patrón cache-aside puede
seguir siendo útil si algunos de estos datos caducan o se expulsan.

Coherencia. La implementación del patrón cache-aside no garantiza la coherencia entre el almacén de


datos y la caché. Un elemento del almacén de datos puede cambiar en cualquier momento mediante
un proceso externo, y este cambio podría no reflejarse en la memoria caché hasta la próxima vez que se
cargue el elemento. En un sistema que replica datos en varios almacenes de datos, este problema puede
llegar a ser grave si la sincronización se produce con frecuencia.

Almacenamiento en caché local (in-memory). Una caché puede ser local en una instancia de la
aplicación y puede almacenarse in-memory. El cache-aside puede resultar útil en este entorno si una
aplicación accede repetidamente a los mismos datos. Sin embargo, una memoria caché local es privada y,
por tanto, las diferentes instancias de la aplicación podrían tener una copia de los mismos datos en caché.
Estos datos podrían resultar rápidamente incoherentes entre cachés, por lo que podría ser necesario que
caduquen los datos almacenados en una caché privada y actualizarlos más a menudo. En estos escenarios,
valore investigar el uso de un mecanismo de almacenamiento en caché compartido o distribuido.

121 CAPÍTULO 6 | Catálogo de patrones


Cuándo utilizar este patrón
Puede utilizar este patrón en los siguientes casos:
●● Una memoria caché no proporciona operaciones de lectura y escritura sincrónica nativas.

●● La demanda de recursos es impredecible. Este patrón permite que las aplicaciones carguen los datos
bajo demanda. No realiza ninguna suposición sobre qué datos requerirá una aplicación por adelantado.

Este patrón puede no ser adecuado:


●● Cuando el conjunto de datos almacenados en la caché es estático. Si los datos caben en el espacio
de la caché disponible, desbloquee la memoria caché con los datos de arranque y aplique una política
que evite la caducidad de los datos.
●● Para almacenar en caché la información del estado de la sesión en una aplicación web alojada
en una granja de servidores web. En este entorno, se debe evitar introducir dependencias basadas
en la afinidad cliente/servidor.

Ejemplo
En Microsoft Azure, puede utilizar Azure Redis Cache para crear una memoria caché distribuida que
pueden compartir varias instancias de una aplicación.

Para conectarse a una instancia de Azure Redis Cache, llame al método Connect estático y pase la cadena
de conexión. El método devuelve un ConnectionMultiplexer que representa la conexión. Una forma de
compartir una instancia ConnectionMultiplexer en su aplicación es tener una propiedad estática que
devuelva una instancia conectada, similar al ejemplo siguiente. Este enfoque proporciona una manera
segura para los subprocesos de inicializar solamente una única instancia conectada.

private static ConnectionMultiplexer Connection;

// Redis Connection string info


private static Lazy<ConnectionMultiplexer> lazyConnection = new Lazy<ConnectionMultiplexer>(() =>
{
string cacheConnection = ConfigurationManager.AppSettings[“CacheConnection”].ToString();
return ConnectionMultiplexer.Connect(cacheConnection);
});

public static ConnectionMultiplexer Connection => lazyConnection.Value;

El método GetMyEntityAsync en el ejemplo de código siguiente muestra una implementación del patrón
cache-aside basado en Azure Redis Cache. Este método recupera un objeto de la caché utilizando el
método de lectura sincrónica.

Un objeto se identifica mediante un identificador entero como clave. El método GetMyEntityAsync intenta
recuperar un elemento con esta clave de la memoria caché. Si se encuentra un elemento coincidente,
lo devuelve. Si no hay ninguna coincidencia en la caché, el método GetMyEntityAsync recupera el objeto
de un almacén de datos, lo agrega a la caché y luego lo devuelve. El código que lee realmente los datos
desde el almacén de datos no se muestra aquí, porque depende del almacén de datos. Tenga en cuenta
que el elemento almacenado en caché se configura para caducar, con el fin de evitar que se vuelva
obsoleto si se actualiza en otro lugar.

122 CAPÍTULO 6 | Catálogo de patrones


// Set five minute expiration as a default
private const double DefaultExpirationTimeInMinutes = 5.0;

public async Task<MyEntity> GetMyEntityAsync(int id)


{
// Define a unique key for this method and its parameters.
var key = $”MyEntity:{id}”;
var cache = Connection.GetDatabase();

// Try to get the entity from the cache.


var json = await cache.StringGetAsync(key).ConfigureAwait(false);
var value = string.IsNullOrWhiteSpace(json)
? default(MyEntity)
: JsonConvert.DeserializeObject<MyEntity>(json);

if (value == null) // Cache miss


{
// If there’s a cache miss, get the entity from the original store and cache it.
// Code has been omitted because it’s data store dependent.
value = ...;

// Avoid caching a null value.


if (value != null)
{
// Put the item in the cache with a custom expiration time that
// depends on how critical it is to have stale data.
await cache.StringSetAsync(key, JsonConvert.SerializeObject(value)).ConfigureAwait(false);
await cache.KeyExpireAsync(key, TimeSpan.FromMinutes(DefaultExpirationTimeInMinutes)).
ConfigureAwait(false);
}
}

return value;
}

Los ejemplos utilizan la API de Azure Redis Cache para acceder al almacén y recuperar la información de
la caché. Para obtener más información, consulte Uso de Microsoft Azure Redis Cache y Cómo crear una
aplicación web con Redis Cache.

El método UpdateEntityAsync que se muestra a continuación muestra cómo invalidar un objeto en la


caché cuando la aplicación cambia el valor. El código actualiza el almacén de datos original y luego elimina
el elemento en caché de la caché.

public async Task UpdateEntityAsync(MyEntity entity)


{
// Update the object in the original data store.
await this.store.UpdateEntityAsync(entity).ConfigureAwait(false);

// Invalidate the current cache object.


var cache = Connection.GetDatabase();
var id = entity.Id;
var key = $”MyEntity:{id}”; // The key for the cached object.
await cache.KeyDeleteAsync(key).ConfigureAwait(false); // Delete this key from the cache.
}

Nota:
El orden de los pasos es importante. Actualice el almacén de datos antes de quitar el elemento de la caché. Si quita el
elemento en caché en primer lugar, hay un pequeño periodo en el que un cliente podría recuperar el elemento antes
de que el almacén de datos se actualice. Esto provocará un error de caché (porque el elemento se eliminó de la caché),
por lo que la versión anterior del elemento tendrá que recuperarse del almacén de datos y volver a añadirse a la caché.
Como resultado, habrá datos obsoletos en la caché.

123 CAPÍTULO 6 | Catálogo de patrones


Orientación relacionada
La siguiente información puede ser relevante al implementar este patrón:

●● Orientación sobre almacenamiento en caché. Proporciona información adicional sobre cómo


puede almacenar datos en caché en una solución de cloud y los temas que debe tener en cuenta
al implementar una memoria caché.

●● Introducción a la coherencia de datos. Las aplicaciones en el cloud normalmente utilizan datos que
se encuentran en varios almacenes de datos. La administración y el mantenimiento de la coherencia
de datos en este entorno es un aspecto esencial del sistema, especialmente los problemas de
simultaneidad y disponibilidad que pueden surgir. En esta introducción se describen problemas
relacionados con la coherencia de datos distribuidos y se resume cómo puede implementar una
aplicación coherencia eventual para mantener la disponibilidad de los datos.

Patrón de interruptor de circuito


Administra los errores de los que se podría tardar una cantidad de tiempo variable en recuperarse al
conectarse a un servicio o recurso remoto. Esto puede mejorar la estabilidad y la resistencia de una aplicación.

Contexto y problema
En un entorno distribuido, las llamadas a servicios y recursos remotos pueden fallar debido a errores
transitorios, como conexiones de red lentas, tiempos de espera o una sobrexplotación o indisponibilidad
temporal de los recursos. Estos errores se suelen corregir solos tras un breve periodo de tiempo, y una
aplicación sólida en el cloud debería estar preparada para afrontarlos mediante una estrategia como el
patrón de reintento.

Sin embargo, también puede haber situaciones en las que los errores se deben a acontecimientos
imprevistos y su corrección podría llevar mucho más tiempo. La gravedad de estos errores puede variar,
desde una pérdida parcial de conectividad hasta el colapso completo de un servicio. En estas situaciones
podría ser inútil que una aplicación reintente continuamente una operación que es poco probable que
tenga éxito. En cambio, la aplicación debe aceptar rápidamente que la operación ha fallado y afrontar
ese error en consecuencia.

Además, si un servicio está muy ocupado, un fallo en una parte del sistema puede provocar errores en
cascada. Por ejemplo, una operación que invoca un servicio puede configurarse para implementar un
tiempo de espera y responder con un mensaje de error si el servicio no responde dentro de este período.
Sin embargo, esta estrategia podría provocar muchas solicitudes simultáneas a la misma operación, de
modo que quede bloqueada hasta que venza el tiempo de espera. Estas solicitudes bloqueadas podrían
retener recursos críticos del sistema como memoria, subprocesos, conexiones de bases de datos, etcétera.
En consecuencia, estos recursos podrían agotarse, lo que causaría un error en otras partes del sistema,
posiblemente no relacionadas, que tengan que usar los mismos recursos. En estas situaciones, sería
preferible que la operación fallara inmediatamente y solo intentara invocar el servicio si es probable que
tenga éxito. Tenga en cuenta que establecer un tiempo de espera más corto podría ayudar a resolver este
problema, pero el tiempo de espera no debería ser tan corto que la operación fallara la mayor parte del
tiempo, incluso aunque la solicitud al servicio tuviera éxito finalmente.

124 CAPÍTULO 6 | Catálogo de patrones


Solución
El patrón de interruptor de circuito puede evitar que una aplicación intente ejecutar repetidamente una
operación que probablemente fracase. Permite que continúe sin esperar a que se solucione el error o
que se malgasten ciclos de la CPU mientras determina que el error es duradero. El patrón de interruptor
de circuito también permite que una aplicación detecte si se ha resuelto el error. Si el problema parece
haberse resuelto, la aplicación puede intentar invocar la operación.

El objetivo del patrón de interruptor de circuito es diferente al del patrón de reintento. El patrón de
reintento permite que una aplicación vuelva a intentar una operación con la expectativa de que tenga
éxito. El patrón de interruptor de circuito impide que una aplicación realice una operación que es probable
que fracase. Una aplicación puede combinar estos dos patrones usando el patrón de reintento para
invocar una operación a través de un interruptor de circuito. Sin embargo, la lógica de reintento debe
ser sensible a las excepciones que devuelve el interruptor de circuito y abandonar los reintentos si el
interruptor indica que el error no es transitorio.

Un interruptor de circuito actúa como un proxy para las operaciones que podrían fallar. El proxy debe
supervisar el número de errores recientes que han ocurrido y utilizar esta información para decidir
si se permite que la operación proceda, o devuelve simplemente una excepción de inmediato.

El proxy puede implementarse como una máquina de estado con los siguientes estados que imitan
la funcionalidad de un interruptor de circuito eléctrico:

●● Closed: la solicitud de la aplicación se enruta a la operación. El proxy mantiene un recuento del


número de errores recientes y, si la llamada a la operación no tiene éxito, el proxy incrementa este
recuento. Si el número de errores recientes supera un umbral especificado dentro de un período
de tiempo determinado, el proxy se adquiere el estado Open. En este punto, el proxy inicia un
temporizador de tiempo de espera y, cuando vence, el proxy se pone en el estado Half-Open.
● El objetivo del temporizador de tiempo de espera es dar al sistema tiempo para solucionar
el problema que haya causado el error antes de permitir que la aplicación intente realizar
la operación nuevamente.

●● Open: la solicitud de la aplicación falla inmediatamente y devuelve una excepción a la aplicación.

●● Half-Open: se permite pasar un número limitado de solicitudes de la aplicación y se invoca la


operación. Si estas solicitudes tienen éxito, se supone que el defecto que estaba causando el error
anteriormente se ha solucionado y el interruptor de circuito cambia al estado Closed (se restablece el
contador de errores). Si falla alguna solicitud, el interruptor de circuito supone que el fallo todavía está
presente, por lo que vuelve al estado Open y reinicia el temporizador de tiempo de espera para dar al
sistema un período adicional para recuperarse del error.

● El estado Half-Open es útil para evitar que un servicio de recuperación se vea desbordado
de peticiones de repente. Cuando un servicio se está recuperando, podría aceptar un
volumen limitado de solicitudes hasta que la recuperación se complete, pero mientras la
recuperación sigue en marcha, el flujo de trabajo puede hacer que el servicio agote el tiempo
de espera o vuelva a fallar.

125 CAPÍTULO 6 | Catálogo de patrones


En el gráfico, el contador de errores que utiliza el estado Closed está basado en tiempo. Se restablece
automáticamente a intervalos periódicos. Esto ayuda a evitar que el interruptor de circuito adquiera el
estado Open si experimenta errores ocasionales. El umbral de error que activa en el interruptor de circuito
el estado Open solo se alcanza cuando ocurre un número especificado de errores durante un intervalo
especificado. El contador utilizado por el estado Half-Open registra el número de intentos correctos de
invocar la operación. El interruptor de circuito vuelve al estado Closed después de un número especificado
de invocaciones consecutivas satisfactorias de la operación. Si cualquier invocación falla, el interruptor de
circuito entra en el estado Open inmediatamente y el contador de éxitos se restablecerá la próxima vez
que adquiera el estado Half-Open.

La forma en que se recupera el sistema se administra externamente, posiblemente restaurando


o reiniciando un componente erróneo o reparando una conexión de red.

El patrón de interruptor de circuito proporciona estabilidad mientras el sistema se recupera de un error


y minimiza el impacto en el rendimiento. Puede ayudar a mantener el tiempo de respuesta del sistema
rechazando rápidamente una solicitud de una operación que probablemente falle, en lugar de esperar a
que la operación agote el tiempo de espera o no devuelva nada. Si el interruptor de circuito provoca un
evento cada vez que cambia de estado, esta información puede utilizarse para supervisar el estado de la
parte del sistema protegida por el interruptor de circuito, o para avisar a un administrador cuando en un
interruptor se dispara el estado Open.

El patrón es personalizable y puede adaptarse según el tipo de error potencial. Por ejemplo, puede
aplicar un temporizador de tiempo de espera ascendente a un interruptor de circuito. Podría colocar
el interruptor de circuito en el estado Open inicialmente durante unos segundos y, si el error no se ha
resuelto, aumentar el tiempo de espera unos minutos y así sucesivamente. En algunos casos, en lugar de
que el estado Open devuelva un error y una excepción, podría ser útil devolver un valor predeterminado
que resulte significativo para la aplicación.

126 CAPÍTULO 6 | Catálogo de patrones


Problemas y consideraciones
Debe considerar los puntos siguientes a la hora de decidir cómo implementar este patrón:

Control de excepciones. Una aplicación que invoca una operación a través de un interruptor de circuito
debe estar preparada para gestionar las excepciones si la operación no está disponible. La forma en que
se gestionan las excepciones es específica a cada aplicación. Por ejemplo, una aplicación podría degradar
temporalmente su funcionalidad, invocar una operación alternativa para tratar de realizar la misma tarea
u obtener los mismos datos o informar de la excepción al usuario y pedirle que lo intente más tarde.

Tipos de excepciones. Una solicitud puede fallar por muchas razones, algunas de las cuales podrían
indicar un tipo de error más grave que otras. Por ejemplo, una solicitud puede fallar debido a la
interrupción de un servicio remoto y tardar varios minutos en recuperarse, o debido a un tiempo de
espera porque el servicio está sobrecargado temporalmente. Un interruptor de circuito podría examinar
los tipos de excepciones que se producen y ajustar su estrategia dependiendo de la naturaleza de estas
excepciones. Por ejemplo, podría requerir un mayor número de excepciones de tiempo de espera para
disparar en el interruptor el estado Open en comparación con el número de errores debidos a que el
servicio esté totalmente no disponible.

Registro. Un interruptor de circuito debe registrar todas las solicitudes erróneas (y posiblemente las
solicitudes correctas) para permitir que un administrador supervise el estado de la operación.

Capacidad de recuperación. Debe configurar el interruptor de circuito para que coincida con el patrón
de recuperación probable de la operación que está protegiendo. Por ejemplo, si el interruptor de circuito
permanece en el estado Open durante un tiempo prolongado, podrían plantearse excepciones aunque la
causa del error se haya resuelto. Del mismo modo, un interruptor de circuito podría fluctuar y reducir los
tiempos de respuesta de las aplicaciones si cambia del estado Open al estado Half-Open demasiado rápido.

Comprobación de operaciones erróneas. En el estado Open, en lugar de usar un temporizador para


determinar cuándo cambiar al estado Half-Open, un interruptor de circuito puede enviar un ping
periódicamente al servicio o recurso remoto para determinar si vuelve a estar disponible. Este ping podría
adoptar la forma de un intento de invocar una operación que anteriormente haya fallado o podría utilizar
una operación especial proporcionada por el servicio remoto específicamente para comprobar el estado
del servicio, según lo descrito por el patrón de supervisión de puntos de conexión de estado.

Invalidación manual. En un sistema en el que el tiempo de recuperación para una operación errónea
es muy variable, es conveniente facilitar una opción de restablecimiento manual que permita a un
administrador cerrar un interruptor de circuito (y reiniciar el contador de errores). Del mismo modo,
un administrador podría forzar en un interruptor de circuito el estado Open (y reiniciar el temporizador
de tiempo de espera) si la operación protegida por el interruptor de circuito está temporalmente no
disponible.

Simultaneidad. Puede haber un gran número de instancias simultáneas de una aplicación accediendo al
mismo interruptor de circuito a la vez. La aplicación no debería bloquear solicitudes simultáneas ni añadir
una sobrecarga excesiva a cada llamada a una operación.

Diferenciación de recursos. Tenga cuidado al usar un único interruptor de circuito para un tipo de recurso
si puede haber varios proveedores independientes subyacentes. Por ejemplo, en un almacén de datos
que contiene múltiples particiones, una partición puede ser completamente accesible mientras que otra
experimenta un problema temporal. Si se combinan las respuestas a errores en estos escenarios, una
aplicación podría intentar acceder a algunas particiones incluso cuando el error es altamente probable,
mientras que el acceso a otras particiones podría quedar bloqueado aunque su probabilidad de éxito sea alta.

Interrupción de circuito acelerada. A veces una respuesta ante un error puede contener suficiente
información para que el interruptor de circuito se dispare inmediatamente y permanezca activo durante un
tiempo mínimo. Por ejemplo, la respuesta a un error desde un recurso compartido que está sobrecargado
podría indicar que no se recomienda un reintento inmediato y que la aplicación debería volver a intentarlo
en unos minutos.

127 CAPÍTULO 6 | Catálogo de patrones


Notas:
Un servicio puede devolver un error HTTP 429 (demasiadas solicitudes) si está limitando el tráfico
en el cliente, o un HTTP 503 (servicio no disponible) si el servicio no está disponible actualmente.
La respuesta puede incluir información adicional, como la duración previsible de la demora.

Reproducción de solicitudes erróneas. En el estado Open, en lugar de simplemente fallar rápidamente,


un interruptor de circuito podría también registrar los detalles de cada petición y organizar que estas
solicitudes se reproduzcan cuando el recurso o servicio remoto esté disponible.

Tiempos de espera inadecuados en servicios externos. Un interruptor de circuito podría no ser


capaz de proteger completamente las aplicaciones ante operaciones que fallen en servicios externos
configurados con un tiempo de espera prolongado. Si el tiempo de espera es demasiado largo, un
subproceso que ejecute un interruptor de circuito se podría bloquear durante un periodo prolongado
antes de que el interruptor de circuito indique que la operación ha fallado. En este tiempo, muchas otras
instancias de aplicaciones podrían intentar también invocar el servicio a través del interruptor de circuito
y atar un número significativo de subprocesos antes de que todos fallen.

Cuándo utilizar este patrón


Utilice este patrón:
●● Para evitar que una aplicación intente invocar un servicio remoto o acceder a un recurso compartido
si es muy probable que la operación falle.

Este patrón no se recomienda:


●● Para administrar el acceso a los recursos privados locales en una aplicación, como estructuras de datos
in-memory. En este entorno, el uso de un interruptor de circuito aumentaría la sobrecarga del sistema.
●● Como sustitutivo para gestionar excepciones en la lógica empresarial de sus aplicaciones.

Ejemplo
En una aplicación web, varias páginas están rellenas con los datos recuperados de un servicio externo. Si el
sistema implementa un almacenamiento en caché mínimo, la mayoría de visitas a estas páginas causarán un
flujo de ida y vuelta al servicio. Las conexiones desde la aplicación web hasta el servicio podrían configurarse
con un período de tiempo de espera (normalmente 60 segundos) y, si el servicio no responde en este
tiempo, la lógica en cada página supondrá que el servicio no está disponible y presentará una excepción.

Sin embargo, si el servicio falla y el sistema está muy ocupado, los usuarios podrían tener que esperar
hasta 60 segundos antes de que se produzca una excepción. Al final, los recursos como la memoria, las
conexiones y los subprocesos podrían agotarse, con lo que se evitaría que otros usuarios se conectaran
al sistema, incluso aunque no accedan a páginas que recuperen datos del servicio.
Si se escala el sistema añadiendo más servidores web e implementando un equilibrio de carga, se podría
retrasar el momento de agotamiento de los recursos, pero no se resolverá el problema, ya que las
solicitudes de usuarios seguirán sin respuesta y, al final, todos los servidores web podrían quedarse sin
recursos.

El ajuste de lógica que conecta con el servicio y recupera los datos en un interruptor de circuito podría
ayudar a solucionar este problema y administrar el error del servicio con más ligereza. Las solicitudes
de usuarios seguirán fallando, pero fallarán más rápido y no bloquearán los recursos.

128 CAPÍTULO 6 | Catálogo de patrones


La clase CircuitBreaker recoge información de estado sobre un interruptor de circuito en un objeto que
implementa la interfaz ICircuitBreakerStateStore que se muestra en el siguiente código.

interface ICircuitBreakerStateStore
{
CircuitBreakerStateEnum State { get; }

Exception LastException { get; }

DateTime LastStateChangedDateUtc { get; }

void Trip(Exception ex);

void Reset();

void HalfOpen();

bool IsClosed { get; }


}

La propiedad State indica el estado actual del interruptor de circuito, y será Open, HalfOpen o Closed,
según defina la enumeración CircuitBreakerStateEnum. La propiedad IsClosed debe ser true si el
interruptor de circuito está cerrado, pero false si está abierto o parcialmente abierto. El método Trip
cambia el estado del interruptor de circuito al estado Open y registra la excepción que haya provocado
el cambio en el estado, junto con la fecha y la hora en que ocurrió la excepción. Las propiedades
LastException y LastStateChangedDateUtc devuelven esta información. El método Reset cierra el
interruptor de circuito, y el método HalfOpen establece el interruptor de circuito en el estado HalfOpen.

La clase InMemoryCircuitBreakerStateStore del ejemplo contiene una implementación de la interfaz


ICircuitBreakerStateStore. La clase CircuitBreaker crea una instancia de esta clase para conservar el estado
del interruptor.
El método ExecuteAction en la clase CircuitBreaker encapsula una operación, especificada como un
delegado Action. Si el interruptor está cerrado, ExecuteAction invoca al delegado Action. Si la operación
falla, un controlador de excepciones llama a TrackException, que establece el estado del interruptor de
circuito como abierto. En el ejemplo de código siguiente se destaca este flujo.

El método ExecuteAction en la clase CircuitBreaker encapsula una operación, especificada como un


delegado Action. Si el interruptor está cerrado, ExecuteAction invoca al delegado Action. Si la operación
falla, un controlador de excepciones llama a TrackException, que establece el estado del interruptor de
circuito como abierto. En el ejemplo de código siguiente se destaca este flujo.

public class CircuitBreaker


{
private readonly ICircuitBreakerStateStore stateStore =
CircuitBreakerStateStoreFactory.GetCircuitBreakerStateStore();

private readonly object halfOpenSyncObject = new object ();


...
public bool IsClosed { get { return stateStore.IsClosed; } }

public bool IsOpen { get { return !IsClosed; } }

public void ExecuteAction(Action action)


{
...
if (IsOpen)
{
// The circuit breaker is Open.
... (see code sample below for details)

129 CAPÍTULO 6 | Catálogo de patrones


}

// The circuit breaker is Closed, execute the action.


try
{
action();
}
catch (Exception ex)
{
// If an exception still occurs here, simply
// retrip the breaker immediately.
this.TrackException(ex);

// Throw the exception so that the caller can tell


// the type of exception that was thrown.
throw;
}
}

private void TrackException(Exception ex)


{
// For simplicity in this example, open the circuit breaker on the first exception.
// In reality this would be more complex. A certain type of exception, such as one
// that indicates a service is offline, might trip the circuit breaker immediately.
// Alternatively it might count exceptions locally or across multiple instances and
// use this value over time, or the exception/success ratio based on the exception
// types, to open the circuit breaker.
this.stateStore.Trip(ex);
}
}

En el ejemplo siguiente, se muestra el código (que se ha omitido en el ejemplo anterior) que se ejecuta
si el interruptor no está cerrado. Primero comprueba si el interruptor ha estado abierto durante más
tiempo del que se especifica en el campo local OpenToHalfOpenWaitTime en la clase CircuitBreaker. Si es
así, el método ExecuteAction define el interruptor en medio abierto y luego intenta realizar la operación
especificada por el delegado Action.

Si la operación se realiza correctamente, el interruptor se restablece al estado cerrado. Si la operación falla,


vuelve a dispararse al estado abierto y la hora a la que se produjo la excepción se actualiza para que el
interruptor espere un período de tiempo adicional antes de intentar volver a realizar la operación.

Si el interruptor solo ha estado abierto durante un breve período de tiempo, menos que lo que indica
el valor de OpenToHalfOpenWaitTime, el método ExecuteAction simplemente produce una excepción
CircuitBreakerOpenException y devuelve el error que hizo que el interruptor pasara al estado abierto.

Asimismo, utiliza un bloqueo para evitar que el interruptor intente realizar llamadas simultáneas a la
operación mientras está medio abierto. Un intento simultáneo de invocar la operación se considerará
como si el interruptor estuviera abierto y fallará con una excepción, como se explica más adelante.

130 CAPÍTULO 6 | Catálogo de patrones


...
if (IsOpen)
{
// The circuit breaker is Open. Check if the Open timeout has expired.
// If it has, set the state to HalfOpen. Another approach might be to
// check for the HalfOpen state that had be set by some other operation.
if (stateStore.LastStateChangedDateUtc + OpenToHalfOpenWaitTime < DateTime.UtcNow)
{
// The Open timeout has expired. Allow one operation to execute. Note that, in
// this example, the circuit breaker is set to HalfOpen after being
// in the Open state for some period of time. An alternative would be to set
// this using some other approach such as a timer, test method, manually, and
// so on, and check the state here to determine how to handle execution
// of the action.
// Limit the number of threads to be executed when the breaker is HalfOpen.
// An alternative would be to use a more complex approach to determine which
// threads or how many are allowed to execute, or to execute a simple test
// method instead.
bool lockTaken = false;
try
{
Monitor.TryEnter(halfOpenSyncObject, ref lockTaken)
if (lockTaken)
{
// Set the circuit breaker state to HalfOpen.
stateStore.HalfOpen();

// Attempt the operation.


action();

// If this action succeeds, reset the state and allow other operations.
// In reality, instead of immediately returning to the Closed state, a counter
// here would record the number of successful operations and return the
// circuit breaker to the Closed state only after a specified number succeed.
this.stateStore.Reset();
return;
}
catch (Exception ex)
{
// If there’s still an exception, trip the breaker again immediately.
this.stateStore.Trip(ex);

// Throw the exception so that the caller knows which exception occurred.
throw;
}
finally
{
if (lockTaken)
{
Monitor.Exit(halfOpenSyncObject);
}
}
}
}
// The Open timeout hasn’t yet expired. Throw a CircuitBreakerOpen exception to
// inform the caller that the call was not actually attempted,
// and return the most recent exception received.
throw new CircuitBreakerOpenException(stateStore.LastException);
}
...

131 CAPÍTULO 6 | Catálogo de patrones


Para utilizar un objeto CircuitBreaker para proteger una operación, una aplicación crea una instancia
de la clase CircuitBreaker e invoca al método ExecuteAction, especificando como parámetro
la operación que se va realizar. La aplicación debe estar preparada para capturar la excepción
CircuitBreakerOpenException si la operación falla porque el interruptor está abierto. El siguiente
código muestra un ejemplo:

var breaker = new CircuitBreaker();

try
{
breaker.ExecuteAction(() =>
{
// Operation protected by the circuit breaker.
...
});
}
catch (CircuitBreakerOpenException ex)
{
// Perform some different action when the breaker is open.
// Last exception details are in the inner exception.
...
}
catch (Exception ex)
{
...
}

Guías y patrones relacionados

Los patrones siguientes también podrían resultar útiles para implementar este patrón:
●● Patrón de reintento. Describe cómo una aplicación puede manejar los fallos temporales previstos
cuando intenta conectarse a un servicio o recurso de red volviendo a intentar de manera transparente
una operación que ha fallado anteriormente.

●● Patrón de supervisión de puntos de conexión de estado. Un interruptor podría ser capaz de probar
el estado de un servicio mediante el envío de una solicitud a un punto de conexión expuesto por
el servicio. El servicio debería devolver información sobre su estado.

Patrón CQRS (Command and Query Responsibility


Segregation)
Segrega las operaciones que leen los datos de las operaciones que actualizan los datos mediante
interfaces separadas. Puede maximizar el rendimiento, la escalabilidad y la seguridad. Permite la evolución
del sistema con el tiempo gracias a una mayor flexibilidad y evita que los comandos de actualización
causen conflictos de combinación en el nivel de dominio.

Contexto y problema
En los sistemas de administración de datos tradicionales, ambos comandos (actualizaciones de los datos)
y las consultas (solicitudes de datos) se ejecutan con respecto al mismo conjunto de entidades en un único
repositorio de datos. Estas entidades pueden ser un subconjunto de las filas de una o más tablas de una
base de datos relacional como SQL Server.

132 CAPÍTULO 6 | Catálogo de patrones


Por lo general, en estos sistemas, todas las operaciones de creación, lectura, actualización y eliminación
(CRUD) se aplican a la misma representación de la entidad. Por ejemplo, la capa de acceso a datos (DAL)
recupera del almacén de datos un objeto de transferencia de datos (DTO) que representa a un cliente y
se muestra en la pantalla. Un usuario actualiza algunos campos del DTO (puede que mediante enlaces de
datos) y luego la capa DAL vuelve a guardar el DTO en el almacén de datos. El mismo DTO se utiliza para
las operaciones de lectura y escritura. La figura muestra una arquitectura tradicional de CRUD.

Los diseños tradicionales de CRUD funcionan bien cuando se aplica únicamente una lógica de negocio
limitada a las operaciones de datos. Los mecanismos de Scaffold que ofrecen las herramientas de
desarrollo pueden crear muy rápidamente código de acceso a datos, que luego se puede personalizar
como sea preciso.

Sin embargo, el enfoque tradicional de CRUD tiene algunas desventajas:


●● A menudo, significa que existe un desajuste entre las representaciones de lectura y escritura de los
datos, como columnas o propiedades adicionales que deben actualizarse correctamente a pesar de
que no son necesarias como parte de una operación.
●● Supone un riesgo para la contención de datos cuando los registros están bloqueados en el almacén
de datos en un dominio de colaboración, donde varios actores trabajan en paralelo con el mismo
conjunto de datos. O bien hay conflictos de actualización causados por las actualizaciones simultáneas
cuando se utiliza el bloqueo optimista. Estos riesgos aumentan a medida que crece la complejidad
y el rendimiento del sistema. Además, el enfoque tradicional puede tener un efecto negativo en el
rendimiento debido a la carga en el almacén de datos y la capa de acceso a datos y la complejidad
de las consultas necesarias para recuperar la información.
●● Puede dificultar la administración de la seguridad y los permisos, porque cada entidad se somete
a operaciones de lectura y escritura que podrían exponer los datos en el contexto equivocado.

Para conocer en mayor profundidad los límites del enfoque CRUD, consulte CRUD: solo cuando pueda
permitírselo.

Solución
CQRS (Command and Query Responsibility Segregation) es un patrón que segrega las operaciones que
leen datos (consultas) de las operaciones que actualizan datos (comandos) utilizando interfaces separadas.
Esto significa que los modelos de datos que se utilizan para realizar consultas y actualizaciones son
diferentes. Por tanto, los modelos se pueden aislar, como se muestra en la figura siguiente, aunque esto
no sea un requisito absoluto.

133 CAPÍTULO 6 | Catálogo de patrones


En comparación con el modelo de datos único que se utiliza en los sistemas basados en CRUD, el uso
de diferentes modelos de consulta y actualización de los datos en sistemas basados en CQRS simplifica
el diseño y la implementación. Sin embargo, una desventaja es que, a diferencia de los diseños de CRUD,
el código de CQRS no se puede generar automáticamente mediante mecanismos de Scaffold.

El modelo de consulta para leer datos y el modelo de actualización para escribir datos puede acceder al
mismo almacén físico, tal vez mediante el uso de vistas SQL o generando proyecciones sobre la marcha.
Sin embargo, es común separar los datos en diferentes almacenes físicos para maximizar el rendimiento,
la escalabilidad y la seguridad, como se muestra en la siguiente figura.

El almacén de lectura puede ser una réplica de solo lectura del almacén de escritura o bien los almacenes
de lectura y escritura pueden tener una estructura totalmente diferente. El uso de varias réplicas de
solo lectura del almacén de lectura puede aumentar enormemente el rendimiento de las consultas y la
capacidad de respuesta de la IU de la aplicación, especialmente en escenarios distribuidos donde las
réplicas de solo lectura se encuentran cerca de las instancias de aplicación. Algunos sistemas de bases
de datos (SQL Server) ofrecen funciones adicionales, como las réplicas de conmutación por error, para
maximizar la disponibilidad.

La separación de los almacenes de lectura y escritura también permite que cada uno se pueda escalar de
la manera apropiada para ajustarse a la carga. Por ejemplo, normalmente los almacenes de lectura suelen
encontrarse con una carga mucho mayor que los almacenes de escritura.

Cuando el modelo de consulta/lectura contiene datos desnormalizados (véase el patrón de vista


materializada), se maximiza el rendimiento al leer los datos para cada una de las vistas de una aplicación
o al consultar los datos en el sistema.

134 CAPÍTULO 6 | Catálogo de patrones


Problemas y consideraciones
Tenga en cuenta los puntos siguientes a la hora de decidir cómo implementar este patrón:
●● Si se divide el almacén de datos en almacenes físicos separados para las operaciones de lectura y
escritura, puede aumentar el rendimiento y la seguridad de un sistema, pero también podría aumentar
la complejidad en términos de resistencia y coherencia final. El almacén de modelos de lectura debe
actualizarse para reflejar los cambios que se realizan en el almacén de modelos de escritura. Además,
puede ser difícil detectar cuándo un usuario ha publicado una solicitud basándose en datos de lectura
obsoletos, lo que significa que no se puede completar la operación.

●● Para leer una descripción de la coherencia final, consulte Introducción a la coherencia de datos.

●● Piense en la posibilidad de aplicar CQRS en secciones limitadas del sistema, donde sea más valioso.

●● Un enfoque típico para la implementación de la coherencia final consiste en utilizar el abastecimiento


de eventos en conjunción con CQRS para que el modelo de escritura sea un flujo de eventos solo
de anexo impulsado por la ejecución de comandos. Estos eventos se utilizan para actualizar las
vistas materializadas que actúan como modelo de lectura. Para obtener más información, consulte
Abastecimiento de eventos y CQRS.

Cuándo utilizar este patrón


Utilice este patrón en las siguientes situaciones:
●● Dominios de colaboración donde se realizan varias operaciones en paralelo en los mismos datos.
CQRS permite definir comandos con suficiente detalle para minimizar los conflictos de combinación
en el nivel de dominio (los conflictos que surjan se pueden combinar con el comando), incluso al
actualizar lo que parece ser el mismo tipo de datos.

●● Interfaces de usuario basadas en tareas donde se guía a los usuarios a través de un proceso complejo
como una serie de pasos o con modelos de dominio complejos. Además, es útil para los equipos ya
familiarizados con las técnicas de diseño basadas en dominios (DDD). El modelo de escritura tiene una pila
de procesamiento de comandos completa con lógica de negocio, validación de la entrada y validación de
negocio para asegurarse de que todo sea siempre coherente para cada uno de los agregados (cada grupo
de objetos asociados que se trata como una unidad para los cambios de datos) en el modelo de escritura.
El modelo de lectura no tiene ninguna lógica de negocio ni pila de validación y solo devuelve un DTO para
utilizarlo en un modelo de vista. El modelo de lectura tiene coherente final con el modelo de escritura.

●● Situaciones donde el rendimiento de las lecturas de datos se debe ajustar por separado del
rendimiento de las escrituras de datos, especialmente cuando la relación de lectura y escritura
es muy alta y cuando es necesario realizar escalado horizontal. Por ejemplo, en muchos sistemas,
el número de operaciones de lectura es mucho mayor que el número de operaciones de escritura.
Para adaptarse a esto, piense en la posibilidad de escalar el modelo de lectura, pero ejecutando
el modelo de escritura en solo una instancia o en unas pocas. Un pequeño número de instancias
del modelo de escritura también ayuda a minimizar los conflictos de combinación.
●● Situaciones donde un equipo de desarrolladores puede centrarse en el modelo de dominios
complejos que forma parte del modelo de escritura y otro equipo puede centrarse en el modelo
de lectura y las interfaces de usuario.
●● Situaciones donde el sistema debe evolucionar con el tiempo y puede contener varias versiones
del modelo o donde las reglas de negocio cambian periódicamente.
●● Integración con otros sistemas, especialmente en combinación con el abastecimiento de eventos,
donde el fallo temporal de un subsistema no debería afectar a la disponibilidad de los otros.

Este patrón no se recomienda en las siguientes situaciones:


●● Si el dominio o las reglas de negocio son simples.
●● Si es suficiente con una interfaz de usuario de tipo CRUD sencilla y las operaciones de acceso a datos
relacionadas.

135 CAPÍTULO 6 | Catálogo de patrones


●● Para la implementación en todo el sistema. Existen componentes específicos de un escenario general
de administración de datos donde CQRS puede resultar útil, pero puede aumentar la complejidad de
una manera considerable e innecesaria cuando no se necesita.

Abastecimiento de eventos y CQRS


El patrón de CQRS se utiliza a menudo junto con el patrón de abastecimiento de eventos. Los sistemas
basados en CQRS utilizan modelos de datos de lectura y escritura diferentes, cada uno adaptado a las
tareas pertinentes y situados a menudo en almacenes separados físicamente. Cuando se utiliza con el
patrón de abastecimiento de eventos, el almacén de eventos es el modelo de escritura y la fuente oficial
de información. El modelo de lectura de un sistema basado en CQRS proporciona vistas materializadas
de los datos, por lo general como vistas enormemente desnormalizadas. Estas vistas se adaptan
a las interfaces y muestran los requisitos de visualización de la aplicación, lo que ayuda a maximizar
el rendimiento tanto de la visualización como de la consulta.

El uso del flujo de eventos como almacén de escritura en lugar de los datos reales en un punto del tiempo
evita conflictos de actualización en un agregado único y maximiza el rendimiento y la escalabilidad. Los
eventos pueden utilizarse para generar asíncronamente vistas materializadas de los datos que se utilizan
para llenar el almacén de lectura.

Dado que el almacén de eventos es la fuente oficial de información, es posible borrar las vistas
materializadas y reproducir todos los eventos pasados para crear una nueva representación del estado
actual cuando el sistema evoluciona o cuando el modelo de lectura debe cambiar. Las vistas materializadas
son, en efecto, una caché de solo lectura duradera de los datos.

Si utiliza CQRS combinado con el patrón de abastecimiento de eventos, considere lo siguiente:


●● Como en cualquier sistema donde los almacenes de escritura y lectura son independientes, los
sistemas basados en este patrón solo son coherentes al final. Habrá cierto retardo entre la generación
del evento y la actualización del almacén de datos.
●● El patrón aumenta la complejidad porque se debe crear código para iniciar y gestionar eventos
y montar o actualizar las vistas u objetos apropiados que necesitan las consultas o un modelo
de lectura. La complejidad del patrón CQRS cuando se usa con el patrón de abastecimiento de eventos
puede dificultar una implementación correcta y requiere un enfoque diferente para diseñar los sistemas.
Sin embargo, el abastecimiento de eventos puede facilitar el modelado del dominio y hacer que sea
más fácil reconstruir vistas o crear nuevas porque se conserva la intención de los cambios en los datos.
●● La generación de vistas materializadas para utilizarlas en el modelo de lectura o las proyecciones
de los datos reproduciendo y gestionando los eventos para entidades o recopilaciones de entidades
específicas puede requerir un tiempo de procesamiento y un uso de recursos significativo. Esto
es así sobre todo si se requiere la suma o el análisis de valores durante largos períodos de tiempo,
porque puede que haya que examinar todos los eventos asociados. Para resolver esto, implemente
instantáneas de los datos a intervalos programados, como un recuento total de la cantidad de
acciones específicas que se han producido o el estado actual de una entidad.

Ejemplo
El código siguiente muestra algunos extractos de un ejemplo de una implementación de CQRS en la que
se utilizan diferentes definiciones de los modelos de lectura y escritura. Las interfaces del modelo no
determinan las características de los almacenes de datos subyacentes. Pueden evolucionar y se pueden
ajustar de manera independiente porque estas interfaces están separadas. El código siguiente muestra
la definición del modelo de lectura.

136 CAPÍTULO 6 | Catálogo de patrones


// Query interface
namespace ReadModel
{
public interface ProductsDao
{
ProductDisplay FindById(int productId);
ICollection<ProductDisplay> FindByName(string name);
ICollection<ProductInventory> FindOutOfStockProducts();
ICollection<ProductDisplay> FindRelatedProducts(int productId);
}

public class ProductDisplay


{
public int Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public decimal UnitPrice { get; set; }
public bool IsOutOfStock { get; set; }
public double UserRating { get; set; }
}

public class ProductInventory


{
public int Id { get; set; }
public string Name { get; set; }
public int CurrentStock { get; set; }
}
}

El sistema permite a los usuarios calificar los productos. Para ello, el código de la aplicación utiliza
el comando RateProduct que se muestra en el siguiente código.

public interface ICommand


{
Guid Id { get; }
}

public class RateProduct : ICommand


{
public RateProduct()
{
this.Id = Guid.NewGuid();
}
public Guid Id { get; set; }
public int ProductId { get; set; }
public int Rating { get; set; }
public int UserId {get; set; }
}

El sistema utiliza la clase ProductsCommandHandler para gestionar los comandos que


envía la aplicación. Normalmente, los clientes envían comandos al dominio a través de un sistema
de mensajería como una cola. El controlador de comandos acepta estos comandos e invoca los
métodos de la interfaz del dominio. El nivel de detalle de cada comando está diseñado para reducir
la posibilidad de que haya solicitudes conflictivas. El código siguiente muestra una descripción de la clase
ProductsCommandHandler.

137 CAPÍTULO 6 | Catálogo de patrones


public class ProductsCommandHandler :
ICommandHandler<AddNewProduct>,
ICommandHandler<RateProduct>,
ICommandHandler<AddToInventory>,
ICommandHandler<ConfirmItemShipped>,
ICommandHandler<UpdateStockFromInventoryRecount>
{
private readonly IRepository<Product> repository;

public ProductsCommandHandler (IRepository<Product> repository)


{
this.repository = repository;
}

void Handle (AddNewProduct command)


{
...
}

void Handle (RateProduct command)


{
var product = repository.Find(command.ProductId);
if (product != null)
{
product.RateProduct(command.UserId, command.Rating);
repository.Save(product);
}
}

void Handle (AddToInventory command)


{
...
}

void Handle (ConfirmItemsShipped command)


{
...
}

void Handle (UpdateStockFromInventoryRecount command)


{
...
}
}

El código siguiente muestra la interfaz de IProductsDomain del modelo de escritura.

public interface IProductsDomain


{
void AddNewProduct(int id, string name, string description, decimal price);
void RateProduct(int userId, int rating);
void AddToInventory(int productId, int quantity);
void ConfirmItemsShipped(int productId, int quantity);
void UpdateStockFromInventoryRecount(int productId, int updatedQuantity);
}

138 CAPÍTULO 6 | Catálogo de patrones


Observe también cómo la interfaz de IProductsDomain contiene métodos que tienen un significado en el
dominio. Por lo general, en un entorno de CRUD, estos métodos tendrían nombres genéricos tales como
Guardar o Actualizar y un DTO como único argumento. El enfoque CQRS puede diseñarse para satisfacer
las necesidades de estos sistemas de administración del inventario y negocio de la organización.

Guías y patrones relacionados


Los siguientes patrones y guías son útiles cuando se implementa este patrón:
●● Para ver una comparación de CQRS con otros estilos de arquitectura, consulte Estilos arquitectónicos
y Estilo arquitectónico CQRS.

●● Introducción a la coherencia de datos. Explica los problemas que suelen encontrarse debido a la
coherencia final entre los almacenes de datos de lectura y escritura cuando se usa el patrón CQRS
y cómo pueden resolverse.

●● Guía de partición de datos. Describe cómo los almacenes de datos de lectura y escritura que se
utilizan en el patrón de CQRS pueden dividirse en particiones a las que se puede acceder y administrar
por separado para mejorar la escalabilidad, reducir la contención y optimizar el rendimiento.
●● Patrón de abastecimiento de eventos. Describe más detalladamente cómo se puede utilizar
el abastecimiento de eventos con el patrón CQRS para simplificar tareas en dominios complejos
mejorando el rendimiento, la escalabilidad y la capacidad de respuesta. También explica cómo
proporcionar coherencia para los datos transaccionales manteniendo auditorías e historiales
completos que permiten realizar acciones de compensación.
●● Patrón de vistas materializadas. El modelo de lectura de una implementación de CQRS puede
contener vistas materializadas de los datos del modelo de escritura, o bien se puede utilizar el modelo
de lectura para generar vistas materializadas.
●● La guía de prácticas y patrones de CQRS Journey. En particular, la introducción al patrón CQRS
(Command and Query Responsibility Segregation) explora el patrón y cuándo es útil. El epílogo
sobre las lecciones aprendidas le ayuda a comprender algunos de los problemas que surgen cuando
se usa este patrón.
●● En el post CQRS de Martin Fowler, se explican los fundamentos del patrón y se proporcionan enlaces
a otros recursos útiles.
●● Los post de Greg Young exploran muchos aspectos del patrón de CQRS.

Patrón de transacciones de compensación.


Deshaga el trabajo realizado mediante una serie de pasos, que en su conjunto definen una operación con
coherencia final, si uno o varios de los pasos fallan. Las operaciones que siguen el modelo de coherencia
final suelen encontrarse en aplicaciones alojadas en el cloud que implementan flujos de trabajo y procesos
de negocio complejos.

Contexto y problema
Las aplicaciones que se ejecutan en el cloud modifican datos con frecuencia. Estos datos podrían estar
repartidos en diferentes fuentes de datos en distintas ubicaciones geográficas. Para evitar la contención
y mejorar el rendimiento en un entorno distribuido, una aplicación no debería intentar proporcionar una
coherencia transaccional fuerte. Por el contrario, la aplicación debería implementar la coherencia final. En
este modelo, una operación de negocio típica consiste en una serie de pasos distintos. Mientras se realizan
estos pasos, la vista general del estado del sistema podría ser incoherente pero, cuando la operación se ha
completado y se han ejecutado todos los pasos, el sistema debería volver a ser coherente. En Introducción
a la coherencia de datos, se proporciona información acerca de por qué las transacciones distribuidas no
se escalan bien y los principios del modelo de coherencia final.

139 CAPÍTULO 6 | Catálogo de patrones


Uno de los problemas del modelo de coherencia final es la forma de gestionar un paso que ha fallado. En
este caso, podría ser necesario deshacer todo el trabajo realizado en los pasos anteriores de la operación.
Sin embargo, no es posible revertir simplemente los datos porque puede que otras instancias simultáneas
de la aplicación los hayan cambiado. Incluso en casos en los que una instancia simultánea no haya
cambiado los datos, deshacer un paso no es una simple cuestión de restablecer el estado original. Podría
ser necesario aplicar varias reglas específicas del negocio (véase el sitio web de viajes que se describe en
la sección Ejemplo).

Si una operación que implementa coherencia final abarca varios almacenes de datos heterogéneos, para
deshacer los pasos de la operación sería necesario visitar cada almacén de datos de uno en uno. El trabajo
que se ha realizado en cada almacén de datos se debe deshacer de forma fiable para evitar que el sistema
siga siendo incoherente.

No todos los datos afectados por una operación que implementa coherencia final se pueden mantener
en una base de datos. En el entorno de una arquitectura orientada a servicios (SOA), una operación podría
invocar una acción en un servicio y provocar un cambio en el estado que mantiene ese servicio. Para
deshacer la operación, este cambio de estado también se debe deshacer. Esto puede suponer que hay
que volver a invocar el servicio y realizar otra acción que invierta los efectos de la primera.

Solución
La solución es implementar una transacción de compensación. Los pasos de una transacción de
compensación deben deshacer los efectos de los pasos de la operación original. Una transacción de
compensación podría no ser capaz de reemplazar simplemente el estado actual por el estado que tenía
el sistema al principio de la operación, porque este enfoque podría sobrescribir los cambios realizados
por otras instancias simultáneas de una aplicación. En su lugar, debe ser un proceso inteligente que tenga
en cuenta cualquier trabajo realizado por instancias simultáneas. Normalmente, este proceso es específico
de la aplicación y se basa en la naturaleza del trabajo realizado por la operación original.

Un enfoque común consiste en utilizar un flujo de trabajo para implementar una operación con coherencia
final que requiere compensación. A medida que avanza la operación original, el sistema registra la
información sobre cada paso y cómo se puede deshacer el trabajo realizado por ese paso. Si la operación
falla en cualquier punto, el flujo de trabajo vuelve atrás en los pasos que ha completado y realiza el trabajo
que invierte cada paso. Tenga en cuenta que es posible que una transacción de compensación no tenga
que deshacer el trabajo en el orden inverso exacto de la operación original y podría ser posible realizar
algunos de los pasos de deshacer en paralelo.

Este enfoque es similar a la estrategia de Sagas que se explica en el blog de Clemens Vasters.

Una transacción de compensación también es una operación con coherencia final y también podría
fallar. El sistema debe ser capaz de reanudar la transacción de compensación en el punto de error
y continuar. Podría ser necesario repetir un paso que ha fallado, por lo que los pasos de una transacción
de compensación deberían definirse como comandos idempotentes. Para obtener más información,
consulte los patrones de idempotencia en el blog de Jonathan Oliver.

En algunos casos, podría no ser posible recuperarse de un paso que ha fallado excepto mediante
la intervención manual. En estas situaciones, el sistema deberá emitir una alerta y proporcionar toda
la información que sea posible sobre el motivo del error.

140 CAPÍTULO 6 | Catálogo de patrones


Problemas y consideraciones
Tenga en cuenta los puntos siguientes a la hora de decidir cómo implementar este patrón:
No resulta fácil determinar cuándo ha fallado un paso de una operación que implementa coherencia
final. Es posible que un paso no falle inmediatamente, pero se podría bloquear. Podría ser necesario
implementar algún tipo de mecanismo de tiempo de espera.

La lógica de compensación no es fácil de generalizar. Una transacción de compensación es específica


de la aplicación. Confía en que la aplicación tenga información suficiente para poder deshacer los efectos
de cada paso en una operación fallida.

Debería definir los pasos de una transacción de compensación como comandos idempotentes.
Esto permite que los pasos se repitan si la propia transacción de compensación falla.

La infraestructura que controla los pasos de la operación original y la transacción de compensación deben
ser resistentes. No debe perder la información necesaria para compensar un paso fallido y debe ser capaz
de supervisar de una forma fiable el progreso de la lógica de compensación.

Una transacción de compensación no restablece necesariamente el estado que tenían los datos del
sistema al principio de la operación original. En cambio, compensa el trabajo realizado por los pasos
que ha completado correctamente antes del fallo de la operación.

El orden de los pasos de la transacción de compensación no tiene que ser necesariamente el inverso
exacto de los pasos de la operación original. Por ejemplo, un almacén de datos puede ser más sensible
a las incoherencias que otro y, por tanto, los pasos de la transacción de compensación que deshacen los
cambios en este almacén deben realizarse primero.

Si se coloca un bloqueo basado en un tiempo de espera a corto plazo en cada recurso que debe
completar una operación y se obtienen estos recursos por adelantado, podría aumentar la probabilidad
de que la actividad general se realice correctamente. El trabajo se debe realizar únicamente después
de que se hayan adquirido todos los recursos. Todas las acciones deben finalizarse antes de que caduquen
los bloqueos.

Considere el uso de una lógica de reintento que sea más tolerante de lo habitual para minimizar los
errores que desencadenan una transacción de compensación. Si falla un paso de una operación que
implementa coherencia final, intente tratar el fallo como una excepción transitoria y repítalo. Únicamente
detenga la operación e inicie una transacción de compensación si un paso falla repetidas veces o de
manera irreversible.

Muchos de los problemas de la implementación de una transacción de compensación son los mismos
que los que se producen en la implementación de la coherencia final. Consulte la sección sobre
consideraciones para la implementación de la coherencia final en Introducción a la coherencia de datos
para obtener más información.

Cuándo utilizar este patrón


Utilice este patrón solo para las operaciones que se deben deshacer si fallan. Si es posible, diseñe
soluciones que eviten la complejidad de que se requieran transacciones de compensación.

141 CAPÍTULO 6 | Catálogo de patrones


Ejemplo
Un sitio web de viajes permite que los clientes reserven itinerarios. Un itinerario único puede constar de
una serie de vuelos y hoteles. Un cliente que viaja de Seattle a Londres y luego a París podría realizar los
siguientes pasos cuando crea un itinerario:

1. Reservar un asiento en el vuelo F1 de Seattle a Londres.


2. Reservar un asiento en el vuelo F2 de Londres a París.
3. Reservar un asiento en el vuelo F3 de París a Seattle.
4. Reservar una habitación en el hotel H1 en Londres.
5. Reservar una habitación en el hotel H2 en París.

Estos pasos constituyen una operación con coherencia final, aunque cada paso es una acción
independiente. Por lo tanto, además de realizar estos pasos, el sistema también debe registrar las
operaciones de contador necesarias para deshacer cada paso en caso de que el cliente decida cancelar
el itinerario. A continuación, los pasos necesarios para realizar las operaciones de contador se pueden
ejecutar como una transacción de compensación.

Observe que los pasos de la transacción de compensación podrían no ser exactamente los inversos a los
pasos originales y la lógica en cada paso de la transacción de compensación debe tener en cuenta las reglas
específicas del negocio. Por ejemplo, si se anula la reserva de un asiento en un vuelo, eso no da derecho al
cliente a la devolución completa del dinero pagado. La figura muestra la generación de una transacción de
compensación para deshacer una transacción de larga ejecución para reservar un itinerario de viaje.

Podría ser posible que los pasos de la transacción de compensación se realizaran en paralelo,
dependiendo de cómo haya diseñado la lógica de compensación para cada paso.

En muchas soluciones de negocio, el fallo de un solo paso no siempre implica que haya que revertir
el sistema utilizando una transacción de compensación. Por ejemplo, si después de haber reservado
los vuelos F1, F2 y F3 en el ejemplo del sitio web de viajes, el cliente no es capaz de reservar una
habitación en el hotel H1, es preferible ofrecer al cliente una habitación en un hotel diferente de la misma
ciudad, en lugar de cancelar los vuelos. El cliente todavía puede decidir cancelar la reserva (en cuyo caso
la transacción de compensación se ejecuta y deshace las reservas de los vuelos F1, F2 y F3), pero esta
decisión debe tomarla el cliente en lugar del sistema.

142 CAPÍTULO 6 | Catálogo de patrones


Guías y patrones relacionados
Los siguientes patrones y guías también pueden ser relevantes al implementar este patrón:
●● Introducción a la coherencia de datos. El patrón de transacciones de compensación se suele utilizar
para deshacer operaciones que implementan el modelo de coherencia final. Esta introducción
proporciona información sobre las ventajas y desventajas de la coherencia final.
●● Patrón de Programador-Agente-Supervisor. Describe cómo se implementan los sistemas resistentes
que realizan operaciones de negocio que utilizan recursos y servicios distribuidos. A veces, puede ser
necesario deshacer el trabajo que ha realizado una operación mediante el uso de una transacción de
compensación.
●● Patrón de reintento. Puede ser caro ejecutar transacciones de compensación y podría ser posible
reducir al mínimo su uso mediante la implementación de una política eficaz que consiste en reintentar
las operaciones fallidas siguiendo el patrón de reintento.

Patrón de consumidores competitivos


Permite que varios consumidores simultáneos procesen los mensajes recibidos en el mismo canal de
mensajería. Permite a un sistema procesar varios mensajes al mismo tiempo para optimizar el rendimiento,
mejorar la escalabilidad y la disponibilidad y equilibrar la carga de trabajo.

Contexto y problema
Se espera que una aplicación que se ejecuta en el cloud gestione un gran número de solicitudes.
En lugar de procesar cada solicitud de forma sincrónica, se utiliza una técnica común que consiste en
que la aplicación las pasa a través de un sistema de mensajería a otro servicio (un servicio de consumidor)
que las gestiona de forma asincrónica. Esta estrategia ayuda a garantizar que la lógica de negocio de
la aplicación no se bloquee mientras se procesan las solicitudes.

El número de solicitudes puede variar considerablemente con el tiempo por muchas razones. Un aumento
repentino de la actividad del usuario o solicitudes agregadas que proceden de varios inquilinos pueden
causar una carga de trabajo imprevisible. En las horas pico, puede que un sistema tenga que procesar
cientos de solicitudes por segundo, mientras que en otras ocasiones, la cantidad podría ser muy pequeña.
Además, la naturaleza del trabajo realizado para gestionar estas solicitudes puede ser muy variable. Una
sola instancia del servicio al consumidor puede hacer que esa instancia se inunde de solicitudes o podría
sobrecargar el sistema de mensajería debido a una oleada de mensajes provenientes de la aplicación. Para
gestionar estas fluctuaciones de la carga de trabajo, el sistema puede ejecutar varias instancias del servicio
al consumidor. Sin embargo, estos consumidores deben coordinarse para garantizar que cada mensaje se
entregue solamente a un solo consumidor. La carga de trabajo también tiene que equilibrarse entre los
consumidores para evitar que una instancia se convierta en un cuello de botella.

Solución
Utilice una cola de mensajes para implementar el canal de comunicación entre la aplicación y las instancias
de servicio al consumidor. La aplicación publica las solicitudes en forma de mensajes en la cola y las
instancias del servicio al consumidor reciben mensajes de la cola y los procesan. Este enfoque permite que
el mismo conjunto de instancias del servicio al consumidor gestione mensajes de cualquier instancia de la
aplicación. La figura ilustra el uso de una cola de mensajes para distribuir el trabajo a las instancias de un
servicio.

143 CAPÍTULO 6 | Catálogo de patrones


Esta solución tiene los siguientes beneficios:
●● Proporciona un sistema de carga nivelada que puede gestionar amplias variaciones en el volumen
de las solicitudes que envían las instancias de la aplicación. La cola actúa como un búfer entre las
instancias de la aplicación y las instancias del servicio al consumidor. Esto puede ayudar a minimizar
el impacto en la disponibilidad y la capacidad de respuesta para la aplicación y las instancias de
servicio, tal y como se describe en el patrón de nivelación de carga basada en cola. La gestión de un
mensaje que requiere un procesamiento de larga ejecución no impide que otras instancias del servicio
al consumidor gestionen simultáneamente otros mensajes.
●● Mejora la fiabilidad. Si un productor se comunica directamente con un consumidor en lugar de
utilizar este patrón, pero no supervisa al consumidor, hay una alta probabilidad de que los mensajes
se pierdan o no se procesen si el consumidor falla. En este patrón, los mensajes no se envían a una
instancia de servicio específica. Una instancia de servicio fallida no bloquea a un productor y cualquier
instancia de servicio en funcionamiento puede procesar los mensajes.
●● No se requiere una coordinación compleja entre los consumidores o entre el productor y las instancias
de consumidor. La cola de mensajes garantiza que cada mensaje se entregue al menos una vez.
●● Es escalable. El sistema puede aumentar o disminuir dinámicamente el número de instancias
del servicio al consumidor a medida que el volumen de mensajes fluctúa.
●● Puede mejorar la resistencia si la cola de mensajes proporciona operaciones de lectura
transaccionales. Si una instancia del servicio al consumidor lee y procesa el mensaje como parte
de una operación transaccional y la instancia del servicio al consumidor falla, este patrón puede
garantizar que el mensaje se devolverá a la cola para que lo recoja y lo gestione otra instancia
del servicio al consumidor.

Problemas y consideraciones
Tenga en cuenta los puntos siguientes a la hora de decidir cómo implementar este patrón:
●● Orden de los mensajes. El orden en el que las instancias del servicio al consumidor reciben los
mensajes no está garantizado y no refleja necesariamente el orden en el que se crearon los mensajes.
Diseñe el sistema para garantizar que el procesamiento de mensajes sea idempotente, porque esto
ayudará a eliminar cualquier dependencia en el orden en el que se gestionan los mensajes. Para
obtener más información, consulte los patrones de idempotencia en el blog de Jonathan Oliver.

144 CAPÍTULO 6 | Catálogo de patrones


Las colas de Microsoft Azure Service Bus pueden implementar y garantizar el orden de llegada de los
mensajes mediante el uso de sesiones de mensaje. Para obtener más información, consulte Patrones de
mensajes mediante sesiones.

●● Diseño de servicios para resistencia. Si el sistema está diseñado para detectar y reiniciar instancias
de servicio fallidas, podría ser necesario implementar el procesamiento que realizan las instancias
de servicio como operaciones idempotentes para minimizar los efectos de que un único mensaje
se recupere y procese más de una vez.

●● Detección de mensajes dudosos. Un mensaje mal formado o una tarea que requiere acceso a
recursos que no están disponibles pueden causar el fallo de una instancia de servicio. El sistema
debería evitar que esos mensajes se devuelvan a la cola y, en su lugar, capturar y almacenar los
detalles de esos mensajes en otros lugares para poder analizarlos si es necesario.
●● Gestión de los resultados. La instancia de servicio que gestiona un mensaje se desacopla
completamente de la lógica de la aplicación que genera el mensaje y puede que no sea capaz de
comunicarse directamente. Si la instancia de servicio genera resultados que deben pasarse a la lógica
de la aplicación, esta información debe almacenarse en una ubicación que sea accesible a ambos. Para
evitar que la lógica de la aplicación recupere datos incompletos, el sistema debe indicar cuándo se ha
completado el procesamiento.

Si utiliza Azure, un proceso de trabajo puede pasar de nuevo los resultados a la lógica de la aplicación
mediante el uso de una cola de respuesta de mensajes dedicada. La lógica de la aplicación debe ser
capaz de correlacionar estos resultados con el mensaje original. Este escenario se describe con más
detalle en Introducción a la mensajería asincrónica.

●● Escalado del sistema de mensajería. En una solución a gran escala, una sola cola de mensajes podría
saturarse por la gran cantidad de mensajes y convertirse en un cuello de botella en el sistema. En
esta situación, piense en la posibilidad de particionar el sistema de mensajería para enviar mensajes
de productores específicos a una cola en particular o bien utilizar el equilibrio de carga para distribuir
mensajes entre varias colas de mensajes.
●● Garantía de la fiabilidad del sistema de mensajería. Se necesita un sistema de mensajería fiable
para garantizar que, después de que la aplicación ponga en cola un mensaje, este no se perderá.
Esto es esencial para garantizar que todos los mensajes se entreguen al menos una vez.

Cuándo utilizar este patrón

Puede utilizar este patrón en los siguientes casos:


●● La carga de trabajo de una aplicación se divide en tareas que pueden ejecutarse de forma asincrónica.
●● Las tareas son independientes y pueden ejecutarse en paralelo.
●● El volumen de trabajo es muy variable, lo que requiere una solución escalable.
●● La solución debe proporcionar alta disponibilidad y debe ser resistente si falla el procesamiento
de una tarea.

Este patrón podría no ser útil cuando:


●● No es fácil separar la carga de trabajo de la aplicación en tareas discretas o hay un alto grado de
dependencia entre las tareas.
●● Las tareas deben realizarse sincrónicamente y la lógica de la aplicación debe esperar a que una tarea
termine antes de continuar.
●● Las tareas deben realizarse en una secuencia específica.

145 CAPÍTULO 6 | Catálogo de patrones


Algunos sistemas de mensajería admiten sesiones que permiten a un productor agrupar los mensajes
y asegurarse de que todos los gestione el mismo consumidor. Este mecanismo puede utilizarse con los
mensajes priorizados (si son se admiten) para implementar una forma de ordenación de mensajes que
entrega los mensajes en secuencia desde un productor a un consumidor individual.

Ejemplo
Azure proporciona colas de almacenamiento y colas de Service Bus que pueden actuar como un
mecanismo para implementar este patrón. La lógica de la aplicación puede publicar mensajes en una
cola y los consumidores que se han implementado como tareas en uno o más roles pueden recuperar los
mensajes de esta cola y procesarlos. Para conseguir resistencia, una cola de Service Bus permite que un
consumidor utilice el modo PeekLock cuando recupera un mensaje de la cola. En realidad, este modo no
elimina el mensaje, sino que simplemente se lo oculta a otros consumidores. El consumidor original puede
eliminar el mensaje cuando haya terminado de procesarlo. Si el consumidor falla, el tiempo de espera de
PeekLock se agotará y el mensaje volverá a ser visible para que otro consumidor pueda recuperarlo.

Para obtener información detallada sobre el uso de colas de Azure Service Bus, consulte las colas,
los temas y las suscripciones de Service Bus. Para obtener información sobre el uso de colas de
almacenamiento de Azure, consulte Introducción a Azure Queue Storage con .NET.

El siguiente código de la clase QueueManager de la solución CompetingConsumers que está disponible


en GitHub muestra cómo se crea una cola utilizando una instancia de QueueClient en el controlador de
eventos Start en un rol de web o trabajador.

private string queueName = ...;


private string connectionString = ...;
...

public async Task Start()


{
// Check if the queue already exists.
var manager = NamespaceManager.CreateFromConnectionString(this.connectionString);
if (!manager.QueueExists(this.queueName))
{
var queueDescription = new QueueDescription(this.queueName);

// Set the maximum delivery count for messages in the queue. A message
// is automatically dead-lettered after this number of deliveries. The
// default value for dead letter count is 10.
queueDescription.MaxDeliveryCount = 3;

await manager.CreateQueueAsync(queueDescription);
}
...

// Create the queue client. By default the PeekLock method is used.


this.client = QueueClient.CreateFromConnectionString(
this.connectionString, this.queueName);
}

El siguiente fragmento de código muestra cómo una aplicación puede crear y enviar un lote de mensajes
a la cola.

146 CAPÍTULO 6 | Catálogo de patrones


public async Task SendMessagesAsync()
{
// Simulate sending a batch of messages to the queue.
var messages = new List<BrokeredMessage>();

for (int i = 0; i < 10; i++)


{
var message = new BrokeredMessage() { MessageId = Guid.NewGuid().ToString() };
messages.Add(message);
}
await this.client.SendBatchAsync(messages);
}

El código siguiente muestra cómo una instancia del servicio al consumidor puede recibir mensajes
de la cola utilizando un enfoque basado en eventos. El parámetro processMessageTask del método
ReceiveMessages es un delegado que hace referencia al código que se va a ejecutar cuando se recibe
un mensaje. Este código se ejecuta de forma asincrónica.

private ManualResetEvent pauseProcessingEvent;


...

public void ReceiveMessages(Func<BrokeredMessage, Task> processMessageTask)


{
// Set up the options for the message pump.
var options = new OnMessageOptions();

// When AutoComplete is disabled it’s necessary to manually


// complete or abandon the messages and handle any errors.
options.AutoComplete = false;
options.MaxConcurrentCalls = 10;
options.ExceptionReceived += this.OptionsOnExceptionReceived;

// Use of the Service Bus OnMessage message pump.


// The OnMessage method must be called once, otherwise an exception will occur.
this.client.OnMessageAsync(
async (msg) =>
{
// Will block the current thread if Stop is called.
this.pauseProcessingEvent.WaitOne();

// Execute processing task here.


await processMessageTask(msg);
},
options);
}
...

private void OptionsOnExceptionReceived(object sender,


ExceptionReceivedEventArgs exceptionReceivedEventArgs)
{
...
}

147 CAPÍTULO 6 | Catálogo de patrones


Tenga en cuenta que las funciones de escalado automático, como las que están disponibles en Azure,
pueden usarse para iniciar y detener instancias de roles a medida que fluctúa la longitud de la cola. Para
obtener más información, consulte Guía de escalado automático. Además, no es necesario mantener
una correspondencia unívoca entre las instancias de rol y los procesos de trabajo, una única instancia de
rol puede implementar varios procesos de trabajo. Para obtener más información, consulte el patrón de
consolidación de recursos de proceso.

Guías y patrones relacionados


Los siguientes patrones y guías pueden ser relevantes al implementar este patrón:
●● Introducción a la mensajería asincrónica. Las colas de mensajes son un mecanismo de comunicación
asincrónica. Si un servicio al consumidor tiene que enviar una respuesta a una aplicación, podría
ser necesario implementar alguna forma de mensajes de respuesta. La introducción a la mensajería
asincrónica proporciona información sobre cómo implementar mensajes de solicitud/respuesta
utilizando colas de mensajes.
●● Guía de escalado automático. Podría ser posible iniciar y detener las instancias de un servicio
al consumidor, ya que la longitud de los mensajes publicados en las aplicaciones de la cola varía.
El escalado automático puede ayudar a mantener el rendimiento durante los momentos de más
procesamiento.
●● Patrón de consolidación de recursos de proceso. Es posible consolidar varias instancias de un
servicio al consumidor en un solo proceso para reducir los costes y la sobrecarga administrativa. El
patrón de consolidación de recursos de proceso describe las ventajas y desventajas de este enfoque.
●● Patrón de nivelación de carga basada en cola. Cuando se introduce una cola de mensajes, puede
aumentar la resistencia del sistema, lo que permite a las instancias de servicio manejar volúmenes
muy variados de solicitudes de instancias de la aplicación. La cola de mensajes actúa como un búfer,
que nivela la carga. El patrón de nivelación de carga basada en cola describe este escenario con más
detalle.
Este patrón tiene una aplicación de ejemplo asociada.

Patrón de consolidación de recursos de proceso.


Consolide varias tareas u operaciones en una sola unidad computacional. De esta manera, puede
aumentar la utilización de los recursos informáticos y reducir los costes y la sobrecarga de administración
asociada a la realización del procesamiento informático en aplicaciones alojadas en el cloud.

Contexto y problema
Una aplicación de cloud suele implementar una gran variedad de operaciones. En algunas soluciones, lo
mejor es seguir el principio de diseño de la separación inicial de los problemas y dividir estas operaciones
en unidades computacionales diferentes que se alojan e implementan individualmente (por ejemplo, como
aplicaciones web App Service, separadas, máquinas virtuales separadas o roles del servicio en el cloud
separados). Sin embargo, aunque esta estrategia puede ayudar a simplificar el diseño lógico de la solución,
al implementar un gran número de unidades computacionales como parte de la misma aplicación, pueden
aumentar los costes de hosting y la complejidad de la administración del sistema.

148 CAPÍTULO 6 | Catálogo de patrones


Por ejemplo, la figura muestra la estructura simplificada de una solución alojada en el cloud que se ha
implementado con más de una unidad computacional. Cada unidad computacional se ejecuta en su
propio entorno virtual. Cada función se ha implementado como una tarea separada (denominadas de
Tarea A a Tarea E) en su propia unidad computacional.

Cada unidad computacional consume recursos de pago, incluso cuando está inactiva o se utiliza muy
poco. Por lo tanto, esta no siempre es la solución más rentable.

En Azure, este problema se aplica a los roles de un servicio en el cloud, los servicios de aplicaciones y
las máquinas virtuales. Estos elementos se ejecutan en su propio entorno virtual. No se realiza un uso
eficiente de los recursos si se ejecuta una recopilación de roles, sitios web o máquinas virtuales separados
que están diseñados para realizar un conjunto de operaciones bien definidas, pero que tienen que
comunicarse y cooperar como parte de una única solución.

Solución
Para reducir los costes, aumentar la utilización, mejorar la velocidad de la comunicación y reducir la
administración, es posible consolidar varias tareas u operaciones en una sola unidad computacional.

Las tareas pueden agruparse según criterios basados en las características que proporcionan los entornos
y los costes asociados a estas características. Un enfoque común consiste en buscar tareas que tengan
un perfil similar en lo referente a su escalabilidad, duración y requisitos de procesamiento. Al agruparlos,
pueden escalarse como una unidad. La elasticidad que ofrecen muchos entornos de cloud permite iniciar y
detener instancias adicionales de una unidad computacional según la carga de trabajo. Por ejemplo, Azure
proporciona escalado automático que se puede aplicar a los roles de un servicio en el cloud, servicios de
aplicaciones y máquinas virtuales. Para obtener más información, consulte Guía de escalado automático.

Como ejemplo de contador, para demostrar cómo se puede utilizar la escalabilidad para determinar las
operaciones que no deberían agruparse, considere las siguientes dos tareas:
●● La Tarea 1 realiza un sondeo en busca de mensajes poco frecuentes independientes del tiempo que
se envían a una cola.
●● La Tarea 2 maneja ráfagas de alto volumen de tráfico de red.

La segunda tarea requiere elasticidad, lo que puede significar que hay que iniciar y detener un gran
número de instancias de la unidad computacional. Si se aplica el mismo escalado a la primera tarea,
la consecuencia sería simplemente que habría más tareas que recibirían mensajes infrecuentes en la misma
cola y eso sería un desperdicio de los recursos.

En muchos entornos de cloud, es posible especificar los recursos disponibles para una unidad
computacional en lo que se refiere al número de núcleos de CPU, memoria, espacio en disco, etc.

149 CAPÍTULO 6 | Catálogo de patrones


En general, cuantos más recursos se especifiquen, mayor será el coste. Para ahorrar dinero, es importante
maximizar el trabajo que realiza una unidad computacional costosa y no dejar que se esté inactiva durante
un período de tiempo prolongado.

Si hay tareas que requieren una gran cantidad de potencia de CPU en ráfagas cortas, considere la
posibilidad de consolidarlas en una sola unidad computacional que proporcione la potencia necesaria. Sin
embargo, es importante equilibrar esta necesidad para mantener ocupados a los recursos más costosos
con respecto a la contención que se produciría si se sobrecargaran demasiado. Por ejemplo, las tareas de
larga duración y de alta intensidad informática no deben compartir la misma unidad computacional.

Problemas y consideraciones
Al implementar este patrón, tenga en cuenta los siguientes puntos:

Escalabilidad y elasticidad. Muchas soluciones de cloud implementan escalabilidad y elasticidad al nivel


de la unidad computacional iniciando y deteniendo instancias de unidades. Evite tareas de agrupación que
tengan requisitos de escalabilidad contradictorios en la misma unidad computacional.

Duración. La infraestructura de cloud recicla periódicamente el entorno virtual que aloja una unidad
computacional. Cuando hay muchas tareas de larga ejecución dentro de una unidad computacional,
puede que sea necesario configurar la unidad para evitar que se recicle hasta que estas tareas hayan
terminado. Como alternativa, diseñe las tareas utilizando un enfoque dirigido a las comprobaciones que
les permita detenerse limpiamente y continuar en el punto en que se interrumpieron cuando se reinicia
la unidad computacional.

Cadencia de versiones. Si la implementación o la configuración de una tarea cambia con frecuencia,


podría ser necesario detener la unidad computacional que aloja el código actualizado, volver a
configurarla, volver a implementarla y, luego, reiniciarla. Este proceso también requerirá que se
detengan, se vuelvan a implementar y se vuelvan a reiniciar todas las demás tareas de la misma unidad
computacional.

Seguridad. Es posible que las tareas de la misma unidad computacional compartan el mismo contexto
de seguridad y puedan acceder a los mismos recursos. Debe existir un alto grado de confianza entre
las tareas y la seguridad de que una tarea no va a dañar o afectar negativamente a otra. Asimismo,
el creciente número de tareas que se ejecutan en una unidad computacional incrementa la superficie
de ataque de la unidad. La seguridad de cada tarea es la misma que la de la tarea que posee más
vulnerabilidades.

Tolerancia a fallos. Si una de las tareas de una unidad computacional falla o se comporta anormalmente,
eso puede afectar a las demás tareas que se ejecutan en de la misma unidad. Por ejemplo, si una tarea no
se inicia correctamente, puede provocar el fallo de toda la lógica de inicio de la unidad computacional
y evitar que se ejecuten otras tareas de la misma unidad.

Contención. Evite la introducción de contención entre las tareas que compiten por los recursos en la
misma unidad computacional. Lo ideal es que las tareas que comparten la misma unidad computacional
deben tener diferentes características de utilización de los recursos. Por ejemplo, es probable que dos
tareas de alta intensidad informática no deban residir en la misma unidad computacional y tampoco dos
tareas que consuman grandes cantidades de memoria. Sin embargo, mezclar una tarea de alta intensidad
informática con una tarea que requiere una gran cantidad de memoria es una combinación viable.

Complejidad. Combinar varias tareas en una sola unidad computacional aumenta la complejidad del
código de la unidad, lo que posiblemente dificulta las pruebas, la depuración y el mantenimiento.

Arquitectura lógica estable. Diseñe e implemente el código en cada tarea, para que no sea necesario
cambiarlo, incluso si cambia el entorno físico en el que se ejecuta la tarea.

150 CAPÍTULO 6 | Catálogo de patrones


Otras estrategias. La consolidación de recursos informáticos es solo una manera de reducir los costes
asociados a la ejecución simultánea de varias tareas. Requiere una planificación y una supervisión
cuidadosas para asegurarse de que sea un enfoque eficaz. Es posible que haya otras estrategias más
apropiadas, dependiendo de la naturaleza del trabajo y dónde se encuentren los usuarios que ejecutan
estas tareas. Por ejemplo, la descomposición funcional de la carga de trabajo (tal y como se describe en
la Guía de partición de recursos informáticos) podría ser una mejor opción.

Problemas y consideraciones
Utilice este patrón para tareas que no sean rentables si se ejecutan en sus propias unidades
computacionales. Si una tarea pasa gran parte del tiempo inactiva, puede resultar costoso ejecutar
esa tarea en una unidad dedicada.

Este patrón podría no ser adecuado para las tareas que realizan operaciones tolerantes a errores críticas o
tareas que procesan datos altamente sensibles o privados y que requieren su propio contexto de seguridad.
Estas tareas se deben ejecutar en su propio entorno aislado, en una unidad computacional distinta.

Ejemplo

Si crea un servicio de cloud en Azure, es posible consolidar el procesamiento realizado por varias tareas en
un solo rol. Normalmente, se trata de un rol de trabajador que realiza tareas de procesamiento asincrónico
o en segundo plano.

En algunos casos, es posible incluir tareas de procesamiento asincrónico o en segundo plano en el rol
de la web. Esta técnica ayuda a reducir costes y a simplificar la implementación, aunque puede afectar a
la escalabilidad y la capacidad de respuesta de la interfaz pública proporcionada por el rol de la web. En
el artículo Combinación de varios roles de trabajador de Azure en un rol de web de Azure, se ofrece una
descripción detallada de la implementación de tareas de procesamiento asincrónico o en segundo plano
en un rol de web.

El rol es responsable de iniciar y detener las tareas. Cuando el controlador del tejido de Azure carga un rol,
produce el evento Start del rol. Se puede anular el método OnStart de la clase WebRole o WorkerRole para
manejar este evento, tal vez para inicializar los datos y otros recursos de los que dependen las tareas de
este método.

Cuando se completa OnStartmethod, el rol puede empezar a responder a las solicitudes. Encontrará más
información y guías sobre el uso de los métodos OnStart y Run en un rol en la sección Procesos de inicio
de aplicaciones en la guía de patrones y prácticas Traslado de aplicaciones al cloud.

Mantenga el código del método OnStart lo más conciso que sea posible. Azure no impone ningún límite
en el tiempo que tarda en completarse este método, pero el rol no podrá empezar a responder a las
solicitudes de red que recibe hasta que este método termine.

Cuando haya terminado el método OnStart, el rol ejecuta el método Run. En este punto, el controlador
de tejido puede empezar a enviar solicitudes al rol.

Coloque el código que crea las tareas en el método Run. Tenga en cuenta que el método Run define la
duración de la instancia del rol. Cuando este método finaliza, el controlador de tejido se encarga de que
el rol se cierre.

Cuando un rol se cierra o se recicla, el controlador de tejido impide que se reciban más solicitudes del
equilibrador de carga y provoca el evento Stop. Para capturar este evento, sobrescriba el método OnStop
del rol y realice la limpieza necesaria antes de que el rol termine.

151 CAPÍTULO 6 | Catálogo de patrones


Cualquier acción que realice en el método OnStop debe completarse en un plazo de cinco minutos
(o 30 segundos si usa el emulador de Azure en un equipo local). De lo contrario, el controlador de tejido
de Azure da por hecho que el rol se ha paralizado y obligará a que se detenga.
Las tareas se inician por medio del método Run, que espera a que las tareas se completen. Las tareas
implementan la lógica de negocio del servicio de cloud y pueden responder a mensajes enviados al rol
a través del equilibrador de carga de Azure. La figura muestra el ciclo de vida de las tareas y los recursos
en un rol de un servicio de cloud de Azure.

El archivo WorkerRole.cs del proyecto ComputeResourceConsolidation.Worker muestra un ejemplo


de cómo se puede implementar este patrón en un servicio de cloud de Azure.

El proyecto ComputeResourceConsolidation.Worker forma parte de la solución de


ComputeResourceConsolidation que se puede descargar de GitHub.

Los métodos MyWorkerTask1 y MyWorkerTask2 muestran cómo realizar diferentes tareas en el mismo rol
de trabajador. El siguiente código muestra MyWorkerTask1. Es una tarea simple que está inactiva durante
30 segundos y luego produce un mensaje de rastreo Repite este proceso hasta que se cancela la tarea.
El código de MyWorkerTask2 es similar.

152 CAPÍTULO 6 | Catálogo de patrones


// A sample worker role task.
private static async Task MyWorkerTask1(CancellationToken ct)
{
// Fixed interval to wake up and check for work and/or do work.
var interval = TimeSpan.FromSeconds(30);

try
{
while (!ct.IsCancellationRequested)
{
// Wake up and do some background processing if not canceled.
// TASK PROCESSING CODE HERE
Trace.TraceInformation(“Doing Worker Task 1 Work”);

// Go back to sleep for a period of time unless asked to cancel.


// Task.Delay will throw an OperationCanceledException when canceled.
await Task.Delay(interval, ct);
}
}
catch (OperationCanceledException)
{
// Expect this exception to be thrown in normal circumstances or check
// the cancellation token. If the role instances are shutting down, a
// cancellation request will be signaled.
Trace.TraceInformation(“Stopping service, cancellation requested”);

// Rethrow the exception.


throw;
}
}

El código de ejemplo muestra una implementación común de un proceso en segundo plano. En una
aplicación real, puede seguir esta misma estructura, excepto en que deberá colocar su propia lógica
de procesamiento en el cuerpo del bucle que espera a la solicitud de cancelación.

153 CAPÍTULO 6 | Catálogo de patrones


Después de que el rol del trabajador haya inicializado los recursos que utiliza, el método Run inicia las dos
tareas simultáneamente, como se muestra aquí.

/// <summary>
/// The cancellation token source use to cooperatively cancel running tasks
/// </summary>
private readonly CancellationTokenSource cts = new CancellationTokenSource();

/// <summary>
/// List of running tasks on the role instance
/// </summary>
private readonly List<Task> tasks = new List<Task>();

// RoleEntry Run() is called after OnStart().


// Returning from Run() will cause a role instance to recycle.
public override void Run()
{
// Start worker tasks and add to the task list
tasks.Add(MyWorkerTask1(cts.Token));
tasks.Add(MyWorkerTask2(cts.Token));

foreach (var worker in this.workerTasks)


{
this.tasks.Add(worker);
}

Trace.TraceInformation(“Worker host tasks started”);


// The assumption is that all tasks should remain running and not return,
// similar to role entry Run() behavior.
try
{
Task.WaitAll(tasks.ToArray());
}
catch (AggregateException ex)
{
Trace.TraceError(ex.Message);

// If any of the inner exceptions in the aggregate exception


// are not cancellation exceptions then re-throw the exception.
ex.Handle(innerEx => (innerEx is OperationCanceledException));
}

// If there wasn’t a cancellation request, stop all tasks and return from Run()
// An alternative to canceling and returning when a task exits would be to
// restart the task.
if (!cts.IsCancellationRequested)
{
Trace.TraceInformation(“Task returned without cancellation request”);
Stop(TimeSpan.FromMinutes(5));
}
}
...

En este ejemplo, el método Run espera a que se completen las tareas. Si se cancela una tarea,
el método Run presupone que el rol se está cerrando y espera a que las tareas restantes se cancelen
antes de terminar (espera un máximo de cinco minutos antes de terminar). Si una tarea falla debido
a una excepción prevista, el método Run cancela la tarea.

154 CAPÍTULO 6 | Catálogo de patrones


Se podrían implementar más estrategias de manejo de excepciones y supervisión completas en el método
Run, como el reinicio de las tareas que han fallado o la inclusión de código que permita que el rol detenga
e inicie tareas individuales.

Se llama al método Stop, que se muestra en el siguiente código, cuando el controlador de tejido cierra
la instancia de rol (se invoca desde el método OnStop). El código detiene cada tarea sin problemas
cancelándola. Si alguna tarea tarda más de cinco minutos en completarse, el procesamiento de la
cancelación del método Stop deja de esperar y el rol finaliza.

// Stop running tasks and wait for tasks to complete before returning
// unless the timeout expires.
private void Stop(TimeSpan timeout)
{
Trace.TraceInformation(“Stop called. Canceling tasks.”);
// Cancel running tasks.
cts.Cancel();

Trace.TraceInformation(“Waiting for canceled tasks to finish and return”);

// Wait for all the tasks to complete before returning. Note that the
// emulator currently allows 30 seconds and Azure allows five
// minutes for processing to complete.
try
{
Task.WaitAll(tasks.ToArray(), timeout);
}
catch (AggregateException ex)
{
Trace.TraceError(ex.Message);

// If any of the inner exceptions in the aggregate exception


// are not cancellation exceptions then rethrow the exception.
ex.Handle(innerEx => (innerEx is OperationCanceledException));
}
}

Guías y patrones relacionados


Los siguientes patrones y guías también pueden ser relevantes al implementar este patrón:
●● Guía de escalado automático. El escalado automático puede utilizarse para iniciar y detener
instancias de recursos informáticos de hosting de servicios, dependiendo de la demanda prevista
del procesamiento.
●● Guía de partición de recursos informáticos. Describe cómo asignar los servicios y los componentes
en un servicio de cloud de una manera que ayude a minimizar los costes manteniendo la
escalabilidad, el rendimiento, la disponibilidad y la seguridad del servicio.
●● Este patrón incluye una aplicación de ejemplo que se puede descargar.

155 CAPÍTULO 6 | Catálogo de patrones


Patrón de abastecimiento de eventos.
En lugar de almacenar solo el estado actual de los datos de un dominio, utilice un almacén solo de
anexos para registrar la serie completa de acciones que se realizan en esos datos. El almacén actúa como
sistema de registro y puede utilizarse para materializar los objetos de dominio. Esto puede simplificar
tareas en dominios complejos, evitando la necesidad de sincronizar el modelo de datos y el dominio del
negocio, a la vez que mejora el rendimiento, la escalabilidad y la capacidad de respuesta. También puede
proporcionar coherencia de los datos transaccionales y mantener auditorías completas y un historial que
permita acciones de compensación.

Contexto y problema
La mayoría de las aplicaciones trabajan con datos. El enfoque típico es que la aplicación mantenga el
estado actual de los datos actualizándolos mientras los usuarios trabajan con ellos. Por ejemplo, en el
modelo tradicional de creación, lectura, actualización y eliminación (CRUD), uno de los procesos de datos
típicos es la lectura de datos del almacén, la realización de algunas modificaciones en él y la actualización
del estado actual de los datos con los nuevos valores, a menudo mediante el uso de transacciones que
bloquean los datos.

El enfoque CRUD tiene algunas limitaciones:


●● Los sistemas CRUD realizan operaciones de actualización directamente en un almacén de datos,
lo que puede ralentizar el rendimiento y la capacidad de respuesta y limitar la escalabilidad debido
a la sobrecarga de procesamiento que supone.
●● En un dominio de colaboración con muchos usuarios simultáneos, es más probable que se produzcan
conflictos de actualización de los datos porque las operaciones de actualización se realizan en un
único elemento de datos.
●● A menos que exista un mecanismo de auditoría adicional que registre los detalles de cada operación
en un registro separado, el historial se pierde.

Para conocer en mayor profundidad los límites del enfoque CRUD, consulte CRUD: solo cuando pueda
permitírselo.

Solución
El patrón de abastecimiento de eventos define un enfoque del manejo de operaciones en datos que está
basado en una secuencia de eventos, cada uno de los cuales se registra en un almacén solo de anexos. El
código de la aplicación envía una serie de eventos que obligatoriamente describen cada acción que se ha
producido en los datos al almacén de eventos donde se conservan. Cada evento representa un conjunto
de cambios en los datos (como AddedItemToOrder).

Los eventos se conservan en un almacén de eventos que actúa como sistema de registro (la fuente de
datos autorizada) sobre el estado actual de los datos. Normalmente, el almacén de eventos publica estos
eventos para poder notificar a los consumidores y puede manejarlos si es necesario. Los consumidores, por
ejemplo, podrían iniciar las tareas que se aplican a las operaciones de los eventos en otros sistemas o realizar
cualquier otra acción asociada que sea necesaria para completar la operación. Observe que el código de la
aplicación que genera los eventos está desacoplado de los sistemas que se suscriben a los eventos.

Los usos habituales de los eventos que publica el almacén de eventos son mantener vistas materializadas
de entidades como acciones si la aplicación las cambia y para la integración con sistemas externos. Por
ejemplo, un sistema puede mantener una vista materializada de todos los pedidos de clientes que se
utiliza para rellenar las partes de la interfaz de usuario. A medida que la aplicación añade nuevos pedidos,
añade o elimina artículos del pedido y añade información de envío, los eventos que describen estos
cambios se pueden manejar y utilizar para actualizar la vista materializada.

156 CAPÍTULO 6 | Catálogo de patrones


Asimismo las aplicaciones pueden leer el historial de eventos en cualquier punto y utilizarlo para
materializar el estado actual de una entidad mediante la reproducción y el consumo de todos los eventos
relacionados con dicha entidad. Esto se puede producir a demanda para materializar un objeto de dominio
al gestionar una solicitud o por medio de una tarea programada para que el estado de la entidad se pueda
almacenar como una vista materializada para ayudar a la capa de presentación.

La figura muestra información general del patrón e incluye algunas de las opciones para usar el flujo
de eventos, como crear una vista materializada, integrar eventos con sistemas y aplicaciones externas
y reproducir eventos para crear proyecciones del estado actual de entidades específicas.

El patrón de abastecimiento de eventos tiene las siguientes ventajas:


Los eventos son inmutables y se pueden almacenar utilizando una operación solo de anexo. La interfaz de
usuario, el flujo de trabajo o el proceso que inician un evento pueden continuar y las tareas que gestionan
los eventos se pueden ejecutar en segundo plano. Esto, combinado con el hecho de que no hay ninguna
contención durante el procesamiento de las transacciones, puede mejorar enormemente el rendimiento
y la escalabilidad de las aplicaciones, especialmente para el nivel de presentación o la interfaz de usuario.

Los eventos son objetos simples que describen una acción que se ha producido junto con los datos
asociados necesarios para describir la acción que representa el evento. Los eventos no actualizan
directamente un almacén de datos. Simplemente se registran para gestionarlos en el momento adecuado.
Esto puede simplificar la implementación y la administración.

Normalmente, los eventos tienen significado para un experto en el dominio, mientras que la falta de
correspondencia de la impedancia objeto-relacional puede hacer que las tablas de bases de datos
complejas sean difíciles de entender. Las tablas son construcciones artificiales que representan el estado
actual del sistema, no los eventos que se han producido.

El abastecimiento de eventos puede ayudar a evitar que las actualizaciones simultáneas causen conflictos,
porque evita la necesidad de actualizar directamente los objetos en el almacén de datos. Sin embargo, aún
hay que diseñar el modelo de dominio para protegerse de las solicitudes que podrían producir un estado
incoherente.

157 CAPÍTULO 6 | Catálogo de patrones


El almacenamiento de los eventos de solo anexo proporciona una auditoría que puede utilizarse para
supervisar las acciones que se realizan en un almacén de datos, regenerar el estado actual como vistas
materializadas o proyecciones reproduciendo los eventos en cualquier momento y ayudar a probar
y depurar el sistema. Asimismo, el requisito de utilizar eventos de compensación para cancelar cambios
proporciona un historial de los cambios que se han revertido, lo cual no podría realizarse si el modelo
simplemente almacenara el estado actual. La lista de eventos también puede utilizarse para analizar
el rendimiento de las aplicaciones y detectar tendencias de comportamiento del usuario o para obtener
otra información empresarial útil.

El almacén de eventos genera eventos y las tareas realizan operaciones en respuesta a esos eventos. Este
desacoplamiento de las tareas de los eventos ofrece flexibilidad y capacidad de ampliación. Las tareas
conocen el tipo de evento y los datos del evento, pero no la operación que ha activado el evento. Además,
cada evento puede gestionarlo varias tareas. Esto permite una integración sencilla con otros servicios
y sistemas que solo reciben los eventos nuevos que produce el almacén de eventos. Sin embargo, los
eventos de abastecimiento de eventos suelen ser de muy bajo nivel y podría ser necesario generar en
su lugar eventos de integración específicos.

Normalmente, el abastecimiento de eventos se combina con el patrón CQRS realizando las tareas de
administración de datos en respuesta a los eventos y materializando vistas desde los eventos almacenados.

Problemas y consideraciones
Tenga en cuenta los puntos siguientes a la hora de decidir cómo implementar este patrón:
El sistema solo tendrá coherencia final si se crean vistas materializadas o se generan proyecciones de
datos mediante la reproducción de eventos. Hay cierto retardo entre el momento en que una aplicación
añade eventos al almacén de eventos como resultado de la gestión de una solicitud, la publicación de los
eventos y el manejo de estos por parte de los consumidores. Durante este período, es posible que hayan
llegado al almacén de eventos nuevos eventos que describen otros cambios en las entidades.

Notas:
Consulte la Introducción a la coherencia de datos para obtener información acerca de coherencia final.

El almacén de eventos es la fuente permanente de información y, por lo tanto, los datos de eventos
no se deberían actualizar nunca. La única manera de actualizar una entidad para deshacer un cambio es
añadir un evento de compensación al almacén de eventos. Si necesita cambiar el formato (en lugar de
los datos) de los eventos persistentes, quizá durante una migración, puede ser difícil combinar los eventos
existentes del almacén con la nueva versión. Puede ser necesario recorrer en iteración todos los eventos
realizando cambios para que sean compatibles con el nuevo formato o añadir nuevos eventos que usen
el nuevo formato. Considere la posibilidad de usar de una marca de versión en cada versión del esquema
de eventos para mantener los formatos de eventos nuevos y antiguos.

Las aplicaciones multiproceso y las diversas instancias de aplicaciones podrían estar almacenando eventos
en el almacén de eventos. La coherencia de los eventos en el almacén de eventos es esencial, al igual que
el orden de los eventos que afecta a una entidad específica (el orden en que se producen los cambios en una
entidad afecta a su estado actual). Para evitar problemas, añada una marca de tiempo a cada evento. Otra
práctica común es comentar cada evento que se derive de una solicitud con un identificador incremental.
Si dos acciones intentan añadir eventos para la misma entidad al mismo tiempo, el almacén de eventos puede
rechazar un evento que coincida con un identificador de entidad existente y el identificador de eventos.

No hay ningún enfoque estándar ni existe ningún mecanismo, como las consultas SQL, para leer los
eventos para obtener información. Los únicos datos que se pueden extraer son un flujo de eventos
utilizando un identificador de eventos como criterio. Normalmente, el ID de evento se asigna a entidades
individuales. El estado actual de una entidad se puede determinar solamente mediante la reproducción
de todos los eventos que están relacionados con él con respecto al estado original de esa entidad.

158 CAPÍTULO 6 | Catálogo de patrones


La longitud de cada flujo de eventos afecta a la administración y actualización del sistema. Si los flujos
son grandes, considere la posibilidad de crear instantáneas a intervalos específicos, tales como un
determinado número de eventos. El estado actual de la entidad puede obtenerse de la instantánea
y reproduciendo los eventos que se han producido después de ese punto en el tiempo. Para obtener
más información sobre cómo crear instantáneas de datos, consulte Snapshot en el sitio web Enterprise
Application Architecture de Martin Fowler y Master-Subordinate Snapshot Replication.

Aunque el abastecimiento de eventos reduce al mínimo la posibilidad de que haya actualizaciones


conflictivas de los datos, la aplicación aún debe ser capaz de gestionar las incoherencias derivadas de
la coherencia final y la falta de transacciones. Por ejemplo, al almacén de datos podría llegar un evento
que indica una reducción del inventario mientras se está realizando un pedido de ese artículo, lo que
produce un requisito de reconciliar las dos operaciones ya sea advirtiendo al cliente o creando un pedido
pendiente.

La publicación del evento se podría realizar "al menos una vez" y, por tanto, los consumidores de los
eventos deben ser idempotentes. No debe volver a aplicar la actualización que se describe en un evento
si el evento se gestiona más de una vez. Por ejemplo, si varias instancias de un consumidor mantienen
una suma de la propiedad de una entidad, como el número total de pedidos realizados, solo una debe
conseguir incrementar esa suma cuando se produce un evento de realización de un pedido. Aunque esto
no es una característica clave del abastecimiento de eventos, es la decisión de implementación habitual.

Cuándo utilizar este patrón


Utilice este patrón en las siguientes situaciones:
●● Cuando desee capturar la intención, el propósito o el motivo en los datos. Por ejemplo, se pueden
capturar los cambios en una entidad del cliente como una serie de tipos de eventos específicos,
como Cambio de casa, Cuenta cerrada o Defunción.

●● Cuando sea imprescindible minimizar o evitar por completo la aparición de actualizaciones


contradictorias de los datos.

●● Cuando desee registrar los eventos que se producen y poder reproducirlos para restablecer el estado
de un sistema, revertir los cambios o mantener un historial y una auditoría. Por ejemplo, cuando una
tarea consta de varios pasos, puede que necesite ejecutar acciones para revertir las actualizaciones
y luego repetir algunos pasos para devolver los datos a un estado coherente.

●● Cuando el uso de eventos sea una característica natural del funcionamiento de la aplicación y requiera
poco desarrollo adicional o esfuerzo de implementación.

●● Cuando sea necesario desacoplar el proceso de introducción o actualización de los datos de las tareas
necesarias para aplicar estas acciones. Esto podría realizarse para mejorar el rendimiento de la interfaz
de usuario o para distribuir eventos a otros agentes de escucha que actúan cuando se producen
eventos. Por ejemplo, se podría integrar un sistema de pago de nóminas en un sitio web de envío de
gastos, para que los eventos que produce el almacén de eventos en respuesta a las actualizaciones
de datos que se realizan en el sitio web los consuma tanto el sitio web como el sistema de pago de
nóminas.

●● Cuando se desea flexibilidad para poder cambiar el formato de los modelos materializados y los datos
de la entidad si los requisitos cambian o, cuando se utiliza en conjunción con CQRS, es necesario
adaptar un modelo de lectura o las vistas que exponen los datos.

●● Cuando se utiliza junto con CQRS, y la coherencia final es aceptable mientras se actualiza un modelo
de lectura, o el impacto en el rendimiento de la rehidratación de entidades y datos de un flujo de
eventos sea aceptable.

159 CAPÍTULO 6 | Catálogo de patrones


Este patrón podría no ser útil en las siguientes situaciones:
●● Dominios pequeños o simples, sistemas que tienen poca o ninguna lógica de negocio o sistemas sin
dominios que funcionan bien de manera natural con mecanismos tradicionales de administración de
datos CRUD.

●● Sistemas donde se requiere coherencia y actualizaciones en tiempo real de las vistas de los datos.

●● Sistemas que no requieren auditorías, historiales ni la capacidad de revertir y repetir acciones.

●● Sistemas donde la aparición de actualizaciones conflictivas de los datos subyacentes es muy limitada.
Por ejemplo, sistemas que predominantemente añaden datos en lugar de actualizarlos.

Ejemplo
Un sistema de administración de conferencias debe realizar un seguimiento del número de reservas que
se han realizado para una conferencia para comprobar si aún quedan asientos cuando un posible asistente
trata de hacer una reserva. El sistema podría almacenar el número total de reservas para una conferencia
al menos de dos formas diferentes:

● El sistema podría almacenar la información sobre el número total de reservas como una entidad
separada en una base de datos que contiene información de reservas. A medida que se realizan o
cancelan reservas, el sistema podría incrementar o disminuir este número según sea necesario. Este
enfoque es sencillo en teoría, pero puede causar problemas de escalabilidad si hay un gran número
de asistentes intentando realizar reservas durante un breve período de tiempo. Por ejemplo, el último
día antes de que se cierre el período de reserva.

● El sistema podría almacenar información sobre las reservas y cancelaciones como eventos que se
mantienen en el almacén de eventos. Luego, podría calcular el número de asientos disponibles
reproduciendo estos eventos. Este enfoque puede ser más escalable debido a la inmutabilidad de
los eventos. El sistema solo debe ser capaz de leer datos del almacén de eventos o anexar datos al
almacén de eventos. La información de los eventos sobre reservas y cancelaciones no se modifica
nunca.

El siguiente diagrama muestra cómo se podría implementar el subsistema de reservas de entradas del
sistema de administración de conferencias usando el abastecimiento de eventos.

160 CAPÍTULO 6 | Catálogo de patrones


La secuencia de acciones para la reserva de dos entradas es la siguiente:
1. La interfaz de usuario emite un comando de reserva de entradas para dos asistentes. El comando lo
maneja un controlador de comandos diferente. Un fragmento de lógica que está desacoplado de la
interfaz de usuario y es responsable de gestionar las solicitudes como comandos.

2. Se construye una suma que contiene información sobre todas las reservas de la conferencia
consultando los eventos que describen las reservas y las cancelaciones. Esta suma se llama
SeatAvailability y está dentro de un modelo de dominio que expone métodos para consultar
y modificar los datos en la suma.

Algunas de las optimizaciones cuyo uso debe considerarse son el uso de instantáneas (para que no
sea necesario consultar y reproducir la lista completa de eventos para obtener el estado actual de la
suma) y el mantenimiento de una copia en caché de la suma en memoria.

3. El controlador de comandos invoca un método que expone el modelo de dominio para realizar las
reservas.

4. La suma SeatAvailability registra un evento que contiene el número de entradas que se han reservado.
La próxima vez que la suma aplique eventos, todas las reservas se utilizarán para calcular cuántas
entradas quedan.

5. El sistema añade el nuevo evento a la lista de eventos en el almacén de eventos.

161 CAPÍTULO 6 | Catálogo de patrones


Si el usuario cancela una reserva, el sistema sigue un proceso similar, excepto en que el controlador de
comandos envía un comando que genera un evento de cancelación de entrada y lo anexa al almacén de
eventos.

Además de ampliar el ámbito para mejorar la escalabilidad, el uso de un almacén de eventos proporciona
también un historial completo, o una auditoría, de las reservas y cancelaciones de una conferencia. Los
eventos del almacén de eventos son el registro exacto. No hay necesidad de que las sumas se conserven
de otra manera porque el sistema puede reproducir fácilmente los eventos y restablecer el estado en
cualquier punto en el tiempo.

Encontrará más información sobre este ejemplo en Introducción al abastecimiento de eventos.

Guías y patrones relacionados


Los siguientes patrones y guías también pueden ser relevantes al implementar este patrón:
●● Patrón CQRS (Command and Query Responsibility Segregation). El almacén de escritura que
proporciona la fuente permanente de información para una implementación de CQRS suele estar basado
en una implementación del patrón de abastecimiento de eventos. Describe cómo separar las operaciones
que leen datos en una aplicación de las operaciones que actualizan datos mediante interfaces separadas.

●● Patrón de vistas materializadas. Normalmente, el almacén de datos que se utiliza en un sistema


basado en el abastecimiento de eventos no es adecuado para realizar consultas eficientes. En cambio,
un enfoque común consiste en generar vistas de los datos previamente completadas a intervalos
regulares o cuando cambian los datos. Aquí se muestra cómo se hace esto.

●● Patrón de transacciones de compensación. Los datos existentes en un almacén de abastecimiento


de eventos no se actualizan; en su lugar, se añaden nuevas entradas que sirven de transición
del estado de las entidades a los nuevos valores. Para revertir un cambio, se utilizan entradas de
compensación, ya que no es posible simplemente revertir el cambio anterior. Describe cómo deshacer
el trabajo que ha realizado una operación anterior.

●● Introducción a la coherencia de datos. Al usar el abastecimiento de eventos con un almacén de


lectura o vistas materializadas separadas, los datos de lectura no serán coherentes inmediatamente,
sino que solo serán coherentes al final. Resume las cuestiones relacionadas con el mantenimiento de
la coherencia en datos distribuidos.

●● Guía de partición de datos. Con frecuencia, los datos se particionan cuando se utiliza el abastecimiento
de eventos para mejorar la escalabilidad, reducir la contención y optimizar el rendimiento. Describe
cómo dividir los datos en particiones discretas y los problemas que pueden surgir.

●● Post de Greg Young: Why use Event Sourcing?

Patrón de almacén de configuración externa


Desplaza la información de configuración fuera del paquete de implementación de la aplicación a
una ubicación centralizada. De esta manera, tendrá más oportunidades de facilitar la administración
y el control de datos de los datos de configuración y de compartir los datos de configuración entre
aplicaciones e instancias de aplicación.

Contexto y problema
La mayoría de los entornos de tiempo de ejecución de las aplicaciones incluyen información de
configuración que está contenida en archivos implementados con la aplicación. En algunos casos, es posible
editar estos archivos para cambiar el comportamiento de la aplicación después de que se ha implementado.

162 CAPÍTULO 6 | Catálogo de patrones


Sin embargo, los cambios en la configuración requieren que la aplicación se vuelva a implementar,
lo que a menudo produce un tiempo de inactividad inaceptable y otro tipo de sobrecarga administrativa.
Los archivos de configuración locales también limitan la configuración a una sola aplicación, pero a veces
sería útil compartir los ajustes de configuración entre varias aplicaciones. Los ejemplos incluyen cadenas
de conexión de base de datos, información de temas de la interfaz de usuario o las direcciones URL de
colas y almacenamiento que utiliza un conjunto de aplicaciones relacionado.

Es difícil administrar los cambios en las configuraciones locales en varias instancias en ejecución de la
aplicación, especialmente si están alojadas en el cloud. La consecuencia puede ser que las instancias
utilicen diferentes ajustes de la configuración mientras se está implementando la actualización.

Además, las actualizaciones de las aplicaciones y los componentes pueden requerir cambios en los
esquemas de configuración. Muchos sistemas de configuración no admiten diferentes versiones de
la información de configuración.

Solución
Almacene la información de configuración en un almacenamiento externo y proporcione una interfaz
que pueda utilizarse de una forma rápida y eficaz para leer y actualizar los ajustes de la configuración.
El tipo de almacén externo depende del hosting y el entorno de tiempo de ejecución de la aplicación.
En un hosting de cloud, normalmente se trata de un servicio de almacenamiento basado en cloud, pero
podría ser una base de datos alojada u otro sistema.

El almacén de respaldo que elija para la información de configuración debe tener una interfaz que
proporcione un acceso constante y sencillo. Debe exponer la información en un formato estructurado
y escrito correctamente. Es posible que la implementación también tenga que autorizar el acceso de los
usuarios con el fin de proteger los datos de configuración y ser lo suficientemente flexible como para
permitir el almacenamiento de varias versiones de la configuración (por ejemplo, desarrollo, prueba o
producción, incluyendo las diversas versiones de cada una).

Muchos sistemas de configuración integrados leen los datos cuando se inicia la aplicación y almacenan en
caché los datos en memoria para proporcionar un acceso rápido y minimizar el impacto en el rendimiento
de la aplicación. Dependiendo del tipo de almacén de respaldo que se utilice y la latencia de este almacén,
podría ser útil implementar un mecanismo de almacenamiento en caché en el almacén de configuración
externo. Para obtener más información, consulte la Guía de almacenamiento en caché. La figura muestra
información general del patrón de almacén de configuración externa con una memoria caché local
opcional.

163 CAPÍTULO 6 | Catálogo de patrones


Problemas y consideraciones

Tenga en cuenta los puntos siguientes a la hora de decidir cómo implementar este patrón:
Elija un almacén de respaldo que ofrezca un rendimiento aceptable, alta disponibilidad y robustez
y del que, además, se pueda realizar una copia de seguridad como parte del proceso de mantenimiento
y administración de las aplicaciones. En una aplicación alojada en cloud, una buena opción es utilizar
un mecanismo de almacenamiento en cloud para satisfacer estos requisitos.

Diseñe el esquema del almacén de respaldo para permitir flexibilidad en los tipos de información que
puede contener. Asegúrese de que permite cumplir todos los requisitos de configuración, tales como
datos con tipo, recopilaciones de ajustes, varias versiones de ajustes y cualquier otra característica que
necesiten las aplicaciones que los utilizan. El esquema debería ser fácil de extender para permitir ajustes
adicionales a medida que cambian los requisitos.

Piense en las capacidades físicas del almacén de respaldo, cómo se relaciona con la forma en que
se almacena la información de la configuración y los efectos sobre el rendimiento. Por ejemplo, para
guardar un documento XML que contiene información de configuración, será necesario que la interfaz
de configuración o la aplicación analicen el documento con el fin de leer los ajustes individuales. Hará
que sea más difícil actualizar un ajuste, aunque el almacenamiento en caché de los ajustes puede ayudar
a compensar el menor rendimiento de lectura.

Piense en cómo la interfaz de configuración permitirá controlar el ámbito y la herencia de los ajustes de la
configuración. Por ejemplo, podría ser un requisito determinar el ámbito de los ajustes de la configuración al
nivel de la organización, la aplicación y la máquina. Podría ser necesario permitir la delegación del control de
acceso a diferentes ámbitos e impedir o permitir que aplicaciones individuales anulen los ajustes.

Asegúrese de que la interfaz de configuración puede exponer los datos de configuración en los formatos
requeridos como valores con tipo, recopilaciones, pares clave/valor o bolsas de propiedades.

Piense en cómo se comportará la interfaz del almacén de configuración cuando los ajustes contengan
errores o no existan en el almacén de respaldo. Podría ser adecuado devolver los ajustes predeterminados
y registrar los errores. También debe pensar en otros aspectos, como la detección de mayúsculas y
minúsculas en las claves o nombres de la configuración, el almacenamiento y manejo de datos binarios
y las formas en que se manejan valores nulos o vacíos.

Piense en cómo proteger los datos de configuración para permitir el acceso solo a los usuarios y
las aplicaciones apropiados. Probablemente, esta es una característica de la interfaz del almacén de
configuración, pero también es necesario asegurarse de que no se pueda acceder directamente a los datos
del almacén de respaldo sin el permiso correspondiente. Asegúrese de que haya una separación estricta
entre los permisos necesarios para leer y escribir datos de configuración. Piense también si necesita cifrar
todos o algunos de los ajustes de la configuración y cómo se implementará esto en la interfaz del almacén
de configuración.

Las configuraciones almacenadas centralmente, que cambian el comportamiento de la aplicación en


tiempo de ejecución, tienen una importancia crítica y se deben implementar, actualizar y administrar
utilizando los mismos mecanismos que cuando se implementa el código de la aplicación. Por ejemplo,
los cambios que pueden afectar a más de una aplicación se deben realizar mediante un enfoque de
implementación de pruebas completo para asegurarse de que el cambio sea apropiado para todas las
aplicaciones que utilizan esta configuración. Si un administrador modifica un ajuste para actualizar una
aplicación, esto podría afectar negativamente a otras aplicaciones que utilizan el mismo ajuste.

Si una aplicación almacena en caché la información de configuración, hay que avisar a la aplicación si
cambia la configuración. Podría ser posible implementar una política de caducidad en los datos de la
configuración almacenados en caché para que esta información se actualice automáticamente de manera
periódica y se recojan todos los cambios (además de realizar las acciones correspondientes).

164 CAPÍTULO 6 | Catálogo de patrones


Cuándo utilizar este patrón
Este patrón es útil para:

●● Los ajustes de configuración que se comparten entre varias aplicaciones e instancias de aplicación
o si hay que aplicar una configuración estándar en varias aplicaciones e instancias de aplicación.

●● Un sistema de configuración estándar que no es compatible con todos los ajustes de la configuración
requeridos, como almacenar imágenes o tipos de datos complejos.

●● Como un almacén complementario para algunos ajustes de las aplicaciones, tal vez para permitir que
las aplicaciones anulen algunos o todos los ajustes almacenados centralmente.

●● Como una manera de simplificar la administración de múltiples aplicaciones y, opcionalmente, para


supervisar el uso de los ajustes de configuración registrando algunos o todos los tipos de acceso al
almacén de configuración.

Ejemplo

En una aplicación alojada de Microsoft Azure, una opción típica para almacenar información de
configuración de manera externa es utilizar el almacenamiento de Azure. Es resistente, ofrece alto
rendimiento y se replica tres veces con conmutación por error automático para ofrecer alta disponibilidad.
El almacenamiento de tablas de Azure proporciona un almacén de clave/valor con la capacidad de utilizar
un esquema flexible para los valores. El almacenamiento de blobs de Azure proporciona un almacén
jerárquico basado en contenedores que puede contener cualquier tipo de datos en blobs con nombres
individuales.

En el ejemplo siguiente, se muestra cómo se puede implementar un almacén de configuración en


el almacenamiento de blobs para almacenar y exponer la información de configuración. La clase
BlobSettingsStore abstrae el almacenamiento de blobs para guardar información de configuración
e implementa la interfaz ISettingsStore que se muestra en el siguiente código.

public interface ISettingsStore


{
Task<string> GetVersionAsync();

Task<Dictionary<string, string>> FindAllAsync();


}

Esta interfaz define métodos para recuperar y actualizar los ajustes de configuración que se guardan en el
almacén de configuración e incluye un número de versión que se puede utilizar para detectar si los ajustes
de configuración se han modificado recientemente. La clase BlobSettingsStore utiliza la propiedad ETag
del blob para implementar el control de versiones. La propiedad de ETag se actualiza automáticamente
cada vez que se escribe el blob.

Por su diseño, esta sencilla solución expone todos los ajustes de configuración como valores de cadena
en lugar de valores con tipo.

La clase ExternalConfigurationManager proporciona un contenedor alrededor de un objeto


BlobSettingsStore. Una aplicación puede utilizar esta clase para almacenar y recuperar información de
configuración. Esta clase utiliza la biblioteca de extensiones reactivas de Microsoft para exponer los
cambios realizados en la configuración a través de una implementación de la interfaz IObservable. Si se
modifica un ajuste llamando al método SetAppSetting, se provoca el evento Changed y se notifica a todos
los suscriptores a este evento.

165 CAPÍTULO 6 | Catálogo de patrones


Tenga en cuenta que los ajustes también se almacenan en caché en un objeto Dictionary, dentro de la
clase ExternalConfigurationManager, para poder acceder a ellos rápidamente. El método GetSetting que
se utiliza para recuperar un ajuste de configuración lee los datos de la caché. Si el ajuste no se encuentra
en la caché, se recupera del objeto BlobSettingsStore.

El método GetSettings invoca al método CheckForConfigurationChanges para detectar si ha cambiado la


información de configuración en el almacenamiento de blob. Para ello, examina el número de versión y lo
compara con el número de versión actual que guarda el objeto ExternalConfigurationManager. Si se han
producido uno o más cambios, se produce el evento Changed y se actualizan los ajustes de configuración
almacenados en caché en el objeto Dictionary. Esta es una aplicación del patrón Cache-Aside.

El siguiente código de ejemplo muestra cómo se implementa el evento Changed, el método GetSettings
y el método CheckForConfigurationChanges:

public class ExternalConfigurationManager : IDisposable


{
// An abstraction of the configuration store.
private readonly ISettingsStore settings;
private readonly ISubject<KeyValuePair<string, string>> changed;
...
private readonly ReaderWriterLockSlim settingsCacheLock = new ReaderWriterLockSlim();
private readonly SemaphoreSlim syncCacheSemaphore = new SemaphoreSlim(1);
...
private Dictionary<string, string> settingsCache;
private string currentVersion;
...
public ExternalConfigurationManager(ISettingsStore settings, ...)
{
this.settings = settings;
...
}
...
public IObservable<KeyValuePair<string, string>> Changed => this.changed.AsObservable();
...

public string GetAppSetting(string key)


{
...
// Try to get the value from the settings cache.
// If there’s a cache miss, get the setting from the settings store and refresh the settings
cache.

string value;
try
{
this.settingsCacheLock.EnterReadLock();

this.settingsCache.TryGetValue(key, out value);


}
finally
{
this.settingsCacheLock.ExitReadLock();
}

return value;
}
...
private void CheckForConfigurationChanges()
{
try
{
// It is assumed that updates are infrequent.

166 CAPÍTULO 6 | Catálogo de patrones


// To avoid race conditions in refreshing the cache, synchronize access to the in-memory
cache.
await this.syncCacheSemaphore.WaitAsync();

var latestVersion = await this.settings.GetVersionAsync();

// If the versions are the same, nothing has changed in the configuration.
if (this.currentVersion == latestVersion) return;

// Get the latest settings from the settings store and publish changes.
var latestSettings = await this.settings.FindAllAsync();

// Refresh the settings cache.


try
{
this.settingsCacheLock.EnterWriteLock();

if (this.settingsCache != null)
{
//Notify settings changed
latestSettings.Except(this.settingsCache).ToList().ForEach(kv => this.changed.
OnNext(kv));
}
this.settingsCache = latestSettings;
}
finally
{
this.settingsCacheLock.ExitWriteLock();
}

// Update the current version.


this.currentVersion = latestVersion;
}
catch (Exception ex)
{
this.changed.OnError(ex);
}
finally
{
this.syncCacheSemaphore.Release();
}
}
}

La clase ExternalConfigurationManager también proporciona una propiedad denominada Environment.


Esta propiedad admite diferentes configuraciones para la ejecución de una aplicación en diferentes
entornos, como los de prueba y producción.

Un objeto ExternalConfigurationManager también puede consultar el objeto BlobSettingsStore


periódicamente para comprobar si hay cambios. En el siguiente código, el método StartMonitor llama
a CheckForConfigurationChanges en un intervalo para detectar cualquier cambio y produce el evento
Changed, como se ha descrito anteriormente.

167 CAPÍTULO 6 | Catálogo de patrones


public class ExternalConfigurationManager : IDisposable
{
...
private readonly ISubject<KeyValuePair<string, string>> changed;
private Dictionary<string, string> settingsCache;
private readonly CancellationTokenSource cts = new CancellationTokenSource();
private Task monitoringTask;
private readonly TimeSpan interval;

private readonly SemaphoreSlim timerSemaphore = new SemaphoreSlim(1);


...
public ExternalConfigurationManager(string environment) : this(new
BlobSettingsStore(environment), TimeSpan.FromSeconds(15), environment)
{
}

public ExternalConfigurationManager(ISettingsStore settings, TimeSpan interval, string


environment)
{
this.settings = settings;
this.interval = interval;
this.CheckForConfigurationChangesAsync().Wait();
this.changed = new Subject<KeyValuePair<string, string>>();
this.Environment = environment;
}
...
/// <summary>
/// Check to see if the current instance is monitoring for changes
/// </summary>
public bool IsMonitoring => this.monitoringTask != null && !this.monitoringTask.IsCompleted;

/// <summary>
/// Start the background monitoring for configuration changes in the central store
/// </summary>
public void StartMonitor()
{
if (this.IsMonitoring)
return;

try
{
this.timerSemaphore.Wait();

// Check again to make sure we are not already running.


if (this.IsMonitoring)
return;

// Start running our task loop.


this.monitoringTask = ConfigChangeMonitor();
}
finally
{
this.timerSemaphore.Release();
}
}

/// <summary>
/// Loop that monitors for configuration changes
/// </summary>
/// <returns></returns>
public async Task ConfigChangeMonitor()
{
while (!cts.Token.IsCancellationRequested)
{

168 CAPÍTULO 6 | Catálogo de patrones


await this.CheckForConfigurationChangesAsync();
await Task.Delay(this.interval, cts.Token);
}
}

/// <summary>
/// Stop monitoring for configuration changes
/// </summary>
public void StopMonitor()
{
try
{
this.timerSemaphore.Wait();

// Signal the task to stop.


this.cts.Cancel();

// Wait for the loop to stop.


this.monitoringTask.Wait();

this.monitoringTask = null;
}
finally
{
this.timerSemaphore.Release();
}
}

public void Dispose()


{
this.cts.Cancel();
}
...
}

La clase ExternalConfiguration crea una instancia de la clase ExternalConfigurationManager como una


instancia individual, tal y como se muestra a continuación.

public static class ExternalConfiguration


{
private static readonly Lazy<ExternalConfigurationManager> configuredInstance = new
Lazy<ExternalConfigurationManager>(
() =>
{
var environment = CloudConfigurationManager.GetSetting(“environment”);
return new ExternalConfigurationManager(environment);
});

public static ExternalConfigurationManager Instance => configuredInstance.Value;


}

El código siguiente se ha tomado de la clase WorkerRole en el proyecto de ExternalConfigurationStore.


Cloud. Muestra cómo la aplicación utiliza la clase ExternalConfiguration para leer un ajuste.

169 CAPÍTULO 6 | Catálogo de patrones


public override void Run()
{
// Start monitoring configuration changes.
ExternalConfiguration.Instance.StartMonitor();

// Get a setting.
var setting = ExternalConfiguration.Instance.GetAppSetting(“setting1”);
Trace.TraceInformation(“Worker Role: Get setting1, value: “ + setting);

this.completeEvent.WaitOne();
}

El código siguiente, también de la clase WorkerRole, muestra cómo la aplicación se suscribe a los eventos
de configuración.

public override bool OnStart()


{
...
// Subscribe to the event.
ExternalConfiguration.Instance.Changed.Subscribe(
m => Trace.TraceInformation(“Configuration has changed. Key:{0} Value:{1}”,
m.Key, m.Value),
ex => Trace.TraceError(“Error detected: “ + ex.Message));
...
}

Guías y patrones relacionados


●● En GitHub se ofrece un ejemplo donde se ilustra cómo utilizar este patrón.

Patrón de identidad federada


Delega la autenticación a un proveedor de identidad externo. Esto puede simplificar el desarrollo, reducir
al mínimo la necesidad de administración de usuarios y mejorar la experiencia del usuario de la aplicación.

Contexto y problema
Normalmente, los usuarios necesitan trabajar con varias aplicaciones que proporcionan y alojan diferentes
organizaciones con las que tienen relaciones comerciales. Estos usuarios podrían tener que utilizar
credenciales específicas (y diferentes) para cada una. Esto puede:
●● Provocar una experiencia de usuario inconexa. Los usuarios suelen olvidarse de las credenciales
de inicio de sesión cuando tienen muchas diferentes.

●● Expone vulnerabilidades de seguridad. Cuando un usuario deja la empresa, se debe cancelar


el aprovisionamiento de la cuenta inmediatamente. Es fácil olvidarse de hacerlo en organizaciones
de gran tamaño.

●● Complica la administración de usuarios. Los administradores deben administrar credenciales para


todos los usuarios y realizar tareas adicionales, como proporcionar recordatorios de contraseña.

Normalmente, los usuarios prefieren utilizar las mismas credenciales para todas estas aplicaciones.

170 CAPÍTULO 6 | Catálogo de patrones


Solución
Implemente un mecanismo de autenticación que pueda utilizar la identidad federada. Separe la
autenticación de usuario del código de la aplicación y delegue la autenticación en un proveedor de
identidades de confianza. Esto puede simplificar el desarrollo y permite a los usuarios autenticarse
utilizando una amplia gama de proveedores de identidad (IdP), al tiempo que se reduce al mínimo la
sobrecarga administrativa. También le permite separar claramente la autenticación de la autorización.

Los proveedores de identidades de confianza incluyen directorios corporativos, servicios de federación


locales, otros servicios de token de seguridad (STS) proporcionados por socios empresariales o
proveedores de identidades sociales que pueden autenticar a los usuarios que tienen, por ejemplo,
una cuenta de Microsoft, Google, Yahoo! o Facebook.

La figura muestra el patrón de identidad federada que se utiliza cuando una aplicación cliente necesita
acceder a un servicio que requiere autenticación. La autenticación se realiza por medio de un IdP que
trabaja en colaboración con un STS. El IdP emite tokens de seguridad que proporcionan información sobre
el usuario autenticado. Esta información, denominada notificaciones, incluye la identidad del usuario y
también puede incluir otra información, como la pertenencia a roles y derechos de acceso más detallados.

Este modelo suele denominarse control de acceso basado en notificaciones. Las aplicaciones y los
servicios autorizan el acceso a características y funcionalidades basadas en las notificaciones contenidas
en el token. El servicio que requiere autenticación debe confiar en el IdP. La aplicación cliente entra en
contacto con el IdP que realiza la autenticación. Si la autenticación es correcta, el IdP devuelve un token
que contiene las notificaciones que identifican al usuario de STS (tenga en cuenta que el IdP y STS puede
ser el mismo servicio). El STS puede transformar y aumentar las notificaciones en el token en función de
reglas predefinidas antes de devolvérselo al cliente. Luego, la aplicación cliente puede pasar el token al
servicio como prueba de su identidad.

Puede haber STS adicionales en la cadena de confianza. Por ejemplo, en el escenario que se describe más
adelante, un STS local confía en otro STS que se encarga de acceder a un proveedor de identidades para
autenticar al usuario. Este enfoque es común en escenarios de empresa donde hay un directorio y STS local.

171 CAPÍTULO 6 | Catálogo de patrones


La autenticación federada ofrece una solución basada en estándares para la cuestión de la confianza en
identidades en diversos dominios y puede admitir el inicio de sesión único. Cada vez es más común en
todos los tipos de aplicaciones, especialmente en aplicaciones alojadas en cloud, porque admite el inicio
de sesión único sin necesidad de una conexión de red directa a los proveedores de identidades. El usuario
no tiene que introducir las credenciales para cada aplicación. Esto aumenta la seguridad porque evita la
creación de las credenciales necesarias para acceder a muchas aplicaciones diferentes y también oculta las
credenciales del usuario a todos, excepto al proveedor de identidades original. Las aplicaciones solo ven la
información de la identidad autenticada que contiene el token.

La identidad federada también tiene la gran ventaja de que la administración de la identidad y las
credenciales es responsabilidad del proveedor de identidades. La aplicación o el servicio no necesitan
proporcionar funciones de administración de identidades. Asimismo, en escenarios corporativos, el directorio
corporativo no tiene que conocer al usuario si confía en el proveedor de identidades. Esto elimina toda la
sobrecarga administrativa que supone tener que administrar la identidad del usuario en el directorio.

Problemas y consideraciones
Tenga en cuenta lo siguiente a la hora de diseñar aplicaciones que implementen la autenticación federada:
●● La autenticación puede ser un único punto de error. Si implementa la aplicación en varios centros de
datos, piense en la posibilidad de implementar su mecanismo de administración de identidades en los
mismos centros de datos para mantener la disponibilidad y la fiabilidad de la aplicación.

●● Las herramientas de autenticación hacen posible configurar el control de acceso en función de las
notificaciones de roles que contiene el token de autenticación. Esto suele denominarse control de
acceso basado en roles (RBAC) y puede permitir un nivel de control más detallado del acceso a
recursos y características.

●● A diferencia de un directorio corporativo, la autenticación basada en notificaciones que utiliza


proveedores de identidades sociales no suele proporcionar información sobre el usuario autenticado
aparte de una dirección de correo electrónico y tal vez un nombre. Algunos proveedores de
identidades sociales, como una cuenta de Microsoft, proporcionan solamente un identificador único.
Normalmente, la aplicación necesita mantener información sobre los usuarios registrados y ser
capaz de emparejar esta información con el identificador contenido en las notificaciones del token.
Generalmente, esto se hace a través del registro cuando el usuario accede por primera vez a la
aplicación. Luego, la información se inyecta en el token como notificaciones adicionales después
de cada autenticación.

●● Si hay configurado más de un proveedor de identidades para el STS, debe detectar a qué proveedor
de identidades se debería redirigir al usuario para la autenticación. Este proceso se denomina
detección del dominio de inicio. El STS podría ser capaz de hacer esto automáticamente basándose en
una dirección de correo electrónico o nombre de usuario que proporciona el usuario, un subdominio
de la aplicación al que está accediendo el usuario, el ámbito de la dirección IP del usuario o en el
contenido de una cookie almacenada en el navegador del usuario. Por ejemplo, si el usuario introduce
una dirección de correo electrónico en el dominio de Microsoft, como usuario@live.com, el STS
redirigirá al usuario a la página de inicio de sesión de la cuenta de Microsoft. En visitas posteriores, el
STS podría utilizar una cookie para indicar que el último inicio de sesión se realizó con una cuenta de
Microsoft. Si la detección automática no puede determinar el dominio de inicio, el STS mostrará una
página de detección del dominio de inicio que incluye a los proveedores de identidades de confianza
y el usuario debe seleccionar el que desea utilizar.

172 CAPÍTULO 6 | Catálogo de patrones


Cuándo utilizar este patrón
Este patrón es útil en casos como los siguientes:
●● Inicio de sesión único en la empresa. En este caso, tiene que autenticar a empleados para
aplicaciones corporativas que se alojan en el cloud, fuera de los límites de seguridad de la empresa,
sin necesidad de que inicien sesión cada vez que visitan una aplicación. La experiencia del usuario
es la misma que cuando se utilizan aplicaciones locales, donde se autentican cuando inician sesión
en una red corporativa y, de ahí en adelante, tienen acceso a todas las aplicaciones relevantes sin
necesidad de volver a iniciar sesión.

●● Identidad federada con múltiples socios. En este caso, es necesario autenticar a empleados
corporativos y socios de negocio que no tienen cuentas en el directorio corporativo. Esto es común
en aplicaciones de negocio a negocio, aplicaciones que se integran con servicios de terceros y donde
se han combinado empresas con diferentes sistemas de TI o recursos compartidos.

●● Identidad federada en aplicaciones SaaS. En este caso, los proveedores de software independientes
proporcionan un servicio listo para su uso para varios clientes o inquilinos. Cada inquilino se autentica
utilizando un proveedor de identidades adecuado. Por ejemplo, los usuarios de negocios usan
sus credenciales corporativas, mientras que los consumidores y clientes del inquilino utilizarán sus
credenciales de identidad social.

Este patrón podría no ser útil en las siguientes situaciones:


●● Todos los usuarios de la aplicación se pueden autenticar por medio de un proveedor de identidades
y no es necesario autenticarse con otro proveedor de identidades. Esto es típico en aplicaciones que
utilizan un directorio corporativo (accesible dentro de la aplicación) para la autenticación, usando una
VPN o (en un escenario alojado en cloud) a través de una conexión de red virtual entre el directorio
local y la aplicación.

●● La aplicación se creó originalmente con un mecanismo de autenticación diferente, quizá con


almacenes de usuario personalizados, o no tiene la capacidad de manejar los estándares de
negociación que utilizan las tecnologías basadas en notificaciones. Puede ser complejo, y
probablemente no es rentable, readaptar la autenticación basada en notificaciones y el control
de acceso en aplicaciones existentes.

Ejemplo
Una organización aloja una aplicación multiempresa de software como servicio (SaaS) en Microsoft
Azure. La aplicación incluye un sitio web que los inquilinos pueden usar para administrar la aplicación
de sus propios usuarios. La aplicación permite a los inquilinos acceder al sitio web mediante el uso
de una identidad federada que generan los Servicios de federación de Active Directory (ADFS) cuando
el propio Active Directory de la organización autentica al usuario.

173 CAPÍTULO 6 | Catálogo de patrones


La figura muestra cómo los inquilinos autentican con su propio proveedor de identidades (paso 1), en
este caso ADFS. Después de la autenticación correcta de un inquilino, ADFS emite un token. El navegador
del cliente reenvía este token al proveedor de federación de la aplicación SaaS, que confía en los tokens
emitidos por el ADFS del inquilino, para devolver un token que es válido para el proveedor de federación
de SaaS (paso 2). Si es necesario, el proveedor de federación de SaaS transforma las notificaciones del
token en notificaciones que la aplicación reconoce (paso 3) antes de devolver el token nuevo al navegador
cliente. La aplicación confía en los tokens emitidos por el proveedor de federación de SaaS y utiliza las
notificaciones del token para aplicar las reglas de autorización (paso 4).

Los inquilinos no necesitan recordar credenciales distintas para acceder a la aplicación y un administrador
de la empresa del inquilino puede configurar la lista de usuarios que pueden acceder a la aplicación en su
propio ADFS.

Orientación relacionada
●● Microsoft Azure Active Directory
●● Servicios de dominio Active Directory
●● Servicios de federación de Active Directory
●● Administración de identidades para aplicaciones multiempresa en Microsoft Azure
●● Aplicaciones multiempresa en Azure

Patrón de gatekeeper
Protege las aplicaciones y servicios mediante el uso de una instancia de host dedicada que actúa
como intermediaria entre los clientes y la aplicación o el servicio, valida y verifica las solicitudes y envía
solicitudes y datos entre ellos. Puede proporcionar una capa adicional de seguridad y limitar la superficie
de ataque del sistema.

Contexto y problema
Las aplicaciones exponen su funcionalidad a los clientes aceptando y procesando solicitudes. En escenarios
alojados en cloud, las aplicaciones exponen los puntos de conexión a los que se conectan los clientes
y suelen incluir el código para manejar las solicitudes de los clientes. Este código realiza la autenticación
y la validación, parte del procesamiento las solicitudes, o incluso todo, y es probable que acceda al
almacenamiento y otros servicios en nombre del cliente.

Si un usuario malintencionado puede comprometer el sistema y acceder al entorno de hosting de la


aplicación, los mecanismos de seguridad que utiliza, como credenciales y claves de almacenamiento, y los
servicios y datos a los que accede, quedan expuestos. Como resultado, el usuario malintencionado puede
obtener acceso sin restricciones a información confidencial y otros servicios.

Solución
Para minimizar el riesgo de que los clientes accedan a información sensible y servicios, desacople los hosts
o las tareas que expongan puntos de conexión públicos del código que procesa solicitudes y tiene acceso
al almacenamiento. Para ello, use una fachada o una tarea dedicada que interactúe con los clientes y luego
entregue la solicitud, quizás a través de una interfaz desacoplada, a los hosts o tareas que la gestionan.
La figura ofrece información general de este patrón.

174 CAPÍTULO 6 | Catálogo de patrones


El patrón de gatekeeper puede utilizarse simplemente para proteger el almacenamiento o puede utilizarse
como una fachada más amplia para proteger todas las funciones de la aplicación. Los factores importantes
son:

●● Validación controlada. El gatekeeper valida todas las solicitudes y rechaza las que no cumplen los
requisitos de validación.

●● Exposición y riesgo limitados. El gatekeeper no tiene acceso a las credenciales o claves utilizadas por
el host de confianza para acceder al almacenamiento y los servicios. Si se compromete el gatekeeper,
el atacante no accede a estas credenciales o claves.

●● Seguridad apropiada. El gatekeeper se ejecuta en un modo de privilegios limitados, mientras que


el resto de la aplicación se ejecuta en el modo de confianza completa que es necesario para acceder
al almacenamiento y los servicios. Si se compromete el gatekeeper, no puede acceder directamente
a los servicios o los datos de las aplicaciones.

Este patrón actúa como un firewall en una topografía de red típica. Permite al gatekeeper examinar las
solicitudes y tomar una decisión sobre si pasar la solicitud al host de confianza (a veces denominado
maestro de claves) que realiza las tareas requeridas. Normalmente, esta decisión requiere que el
gatekeeper valide y sanee el contenido de la solicitud antes de pasarla al host de confianza.

Problemas y consideraciones
Tenga en cuenta los puntos siguientes a la hora de decidir cómo implementar este patrón:
●● Asegúrese de que los hosts de confianza que pasa el gatekeeper solicitan que solo se expongan
puntos de conexión internos o protegidos y se conectan solo con el gatekeeper. Los hosts de
confianza no deberían exponer interfaces ni puntos de conexión externos.

●● El gatekeeper debe ejecutarse en un modo de privilegios limitados. Normalmente, esto significa que
el gatekeeper y el host de confianza se ejecutan en servicios alojados o máquinas virtuales separados.

●● El gatekeeper no debe realizar ningún procesamiento relacionado con las aplicaciones o servicios ni
acceder a los datos. Su función es simplemente validar y sanear las solicitudes. Puede que los hosts de
confianza tengan que realizar una validación adicional de las solicitudes, pero la validación principal
debe realizarla el gatekeeper.

●● Utilice un canal de comunicación segura (HTTPS, SSL o TLS) entre el gatekeeper y los hosts de
confianza o las tareas donde esto es posible. Sin embargo, algunos entornos de hosting no admiten
HTTPS en puntos de conexión internos.
Es probable que la adición de una capa adicional a la aplicación para implementar el patrón
del gatekeeper tenga algún impacto en el rendimiento debido al procesamiento adicional y la
comunicación de red que requiere.

175 CAPÍTULO 6 | Catálogo de patrones


●● La instancia de gatekeeper podría ser un único punto de error. Para minimizar el impacto de un error,
piense en la posibilidad de implementar instancias adicionales y usar un mecanismo de escalado
automático para garantizar la capacidad de mantener la disponibilidad.

Cuándo utilizar este patrón


Este patrón es útil para:
●● Aplicaciones que manejan información sensible, exponen servicios que deben tener un alto grado
de protección contra ataques maliciosos o realizan operaciones de misión crítica que no deben
interrumpirse.

●● Aplicaciones distribuidas donde es necesario realizar la validación de solicitudes por separado de las
tareas principales o centralizar esta validación para simplificar la administración y el mantenimiento.

Ejemplo
En un escenario alojado en el cloud, este patrón se puede implementar desacoplando el gatekeeper o la
máquina virtual de los roles y servicios de confianza de una aplicación. Para ello, se utiliza un punto de
conexión interno, una cola o almacenamiento como un mecanismo de comunicación intermedio La figura
ilustra el uso de un punto de conexión interno.

Patrones relacionados
El patrón de clave auxiliar también podría ser importante al implementar el patrón de gatekeeper. Al
comunicarse entre el gatekeeper y los roles de confianza, es una buena práctica mejorar la seguridad
mediante el uso de claves o tokens que limitan los permisos para acceder a los recursos.

Patrón de agregación de puerta de enlace


Utiliza una puerta de entrada para combinar varias solicitudes individuales en una única solicitud. Este
patrón es útil cuando un cliente debe hacer varias llamadas a sistemas de back-end diferentes para realizar
una operación.

176 CAPÍTULO 6 | Catálogo de patrones


Contexto y problema
Para realizar una sola tarea, un cliente puede tener que hacer varias llamadas a diversos servicios de
back-end. Una aplicación que se basa en muchos servicios para realizar una tarea debe gastar recursos
en cada solicitud. Cuando se añade una nueva función o servicio a la aplicación, se necesitan solicitudes
adicionales, aumentan los recursos necesarios y hay más llamadas de red. Esta locuacidad entre un
cliente y un back-end puede afectar negativamente al rendimiento y la escalabilidad de la aplicación. Las
arquitecturas de microservicios han hecho que este problema sea más común, porque las aplicaciones que
se han creado en torno a muchos servicios más pequeños realizan, por naturaleza. una mayor cantidad de
llamadas de servicio cruzado.

En el diagrama siguiente, el cliente envía solicitudes a cada servicio (1,2,3). Cada servicio procesa
la solicitud y envía la respuesta de nuevo a la aplicación (4,5,6). En una red móvil con una latencia
normalmente alta, es ineficaz utilizar las solicitudes individuales de esta manera y se podría interrumpir
la conectividad o derivarse en solicitudes incompletas. Aunque que es posible realizar cada solicitud en
paralelo, la aplicación debe enviar, esperar y procesar los datos para cada solicitud, y todo en conexiones
independientes, lo que aumenta la posibilidad de que falle.

Solución
Use una puerta de enlace para reducir la locuacidad entre el cliente y los servicios. La puerta de enlace
recibe solicitudes de los clientes, envía solicitudes a los diferentes sistemas de back-end y luego agrupa
los resultados y se los devuelve al cliente solicitante.

Este patrón puede reducir el número de solicitudes que realiza la aplicación a los servicios de back-end
y mejorar el rendimiento de la aplicación en redes de alta latencia.

En el siguiente diagrama, la aplicación envía una solicitud a la puerta de enlace (1). La solicitud contiene
un paquete de solicitudes adicionales. La puerta de enlace los descompone y procesa cada solicitud
enviándola al servicio correspondiente (2). Cada servicio devuelve una respuesta a la puerta de enlace (3).
La puerta de enlace combina las respuestas de cada servicio y envía la respuesta a la aplicación (4).
La aplicación realiza una sola solicitud y recibe una única respuesta de la puerta de enlace.

177 CAPÍTULO 6 | Catálogo de patrones


Problemas y consideraciones
●● La puerta de enlace no debe introducir el acoplamiento de servicios en los servicios de back-end.

●● La puerta de enlace debe ubicarse cerca de los servicios de back-end para reducir la latencia todo
lo posible.

●● El servicio de puerta de enlace puede introducir un único punto de fallo. Asegúrese de que la puerta
de enlace está diseñada correctamente para satisfacer los requisitos de disponibilidad de la aplicación.
La puerta de enlace puede presentar un cuello de botella. Asegúrese de que la puerta de enlace tiene un
rendimiento suficiente para manejar la carga y puede escalarse para adaptarse al crecimiento esperado.

●● Realice pruebas de carga en la puerta de enlace para asegurarse de que no se introducen errores
en cascada de los servicios.

●● Implemente un diseño resistente, utilizando técnicas como bulkheads, interrupciones de circuitos,


reintentos y tiempos de espera.

●● Si una o más llamadas al servicio tardan demasiado, puede ser aceptable que se agote el tiempo
de espera y se devuelva un conjunto parcial de datos. Piense cómo manejará esta situación la aplicación.

●● Utilice una E/S asincrónica para asegurarse de que un retardo en el back-end no causa problemas de
rendimiento en la aplicación.

●● Implemente el rastreo distribuido usando ID de correlación para realizar un seguimiento de cada


llamada individual.

●● Supervise las métricas de las solicitudes y los tamaños de las respuestas.

●● Considere la posibilidad de devolver datos en caché como una estrategia de conmutación por error
para manejar errores.

●● En lugar de crear la agregación en la puerta de enlace, piense en la posibilidad de colocar un servicio


de agregación detrás de la puerta de enlace. Probablemente, la agregación de solicitudes tenga
requisitos de recursos diferentes que otros servicios de la puerta de enlace y puede afectar
al enrutamiento y la funcionalidad de descarga de la puerta de enlace.

178 CAPÍTULO 6 | Catálogo de patrones


Cuándo utilizar este patrón
Puede utilizar este patrón en los siguientes casos:
●● Un cliente necesita comunicarse con varios servicios de back-end para realizar una operación.
●● El cliente puede utilizar redes con una latencia significativa, como las redes móviles.

Este patrón puede no ser adecuado cuando:

●● Desea reducir el número de llamadas entre un cliente y un servicio único en varias operaciones.
En esa situación, sería mejor añadir una operación por lotes al servicio.

●● El cliente o la aplicación se encuentran cerca de los servicios de back-end y la latencia no es un factor


importante.

Ejemplo
En el ejemplo siguiente, se muestra cómo crear un servicio NGINX de agregación de puerta de enlace
sencillo con Lua.

worker_processes 4;

events {
worker_connections 1024;
}

http {
server {
listen 80;

location = /batch {
content_by_lua ‘
ngx.req.read_body()

-- read json body content


local cjson = require “cjson”
local batch = cjson.decode(ngx.req.get_body_data())[“batch”]

-- create capture_multi table


local requests = {}
for i, item in ipairs(batch) do
table.insert(requests, {item.relative_url, { method = ngx.HTTP_GET}})
end

-- execute batch requests in parallel


local results = {}
local resps = { ngx.location.capture_multi(requests) }
for i, res in ipairs(resps) do
table.insert(results, {status = res.status, body = cjson.decode(res.body), header =
res.header})
end

ngx.say(cjson.encode({results = results}))
‘;
}

location = /service1 {
default_type application/json;
echo ‘{“attr1”:”val1”}’;
}

location = /service2 {
default_type application/json;
echo ‘{“attr2”:”val2”}’;
}
}
}

179 CAPÍTULO 6 | Catálogo de patrones


Orientación relacionada

●● Patrón Back-ends for Front-ends


●● Patrón de descarga de puerta de enlace
●● Patrón de enrutamiento de puerta de enlace

Patrón de descarga de puerta de enlace


Descarga funcionalidades de servicio compartidas o especializadas a un proxy de puerta de enlace. Este
patrón puede simplificar el desarrollo de aplicaciones trasladando la funcionalidad de servicio compartido,
como el uso de certificados SSL, desde otras partes de la aplicación a la puerta de enlace.

Contexto y problema
Algunas características suelen utilizarse en varios servicios y requieren configuración, administración
y mantenimiento. Un servicio compartido o especializado que se distribuye con cada implementación
de la aplicación aumenta la sobrecarga administrativa y la probabilidad de que se produzca un error
de implementación. Las actualizaciones de una característica compartida se deben implementar en todos
los servicios que comparten esa característica.

Para gestionar adecuadamente los problemas de seguridad (validación de tokens, cifrado, administración
de certificados SSL) y otras tareas complejas, es posible que los miembros del equipo deban tener
habilidades enormemente especializadas. Por ejemplo, un certificado que necesita una aplicación se debe
configurar e implementar en todas las instancias de la aplicación. Con cada nueva implementación, hay
que administrar el certificado para que no caduque. Cualquier certificado común que caduque se debe
actualizar, probar y verificar en cada implementación de la aplicación.

Otros servicios comunes tales como la autenticación, la autorización, el registro, la supervisión o la


limitación pueden ser difíciles de implementar y administrar en un gran número de implementaciones.
Puede que sea mejor consolidar este tipo de funcionalidad, con el fin de reducir la sobrecarga y la
posibilidad de errores.

Solución
Descargue algunas de las características en una puerta de enlace API, particularmente las cuestiones de
transversalidad como la administración de certificados, la autenticación, la terminación SSL, la supervisión,
la conversión de protocolos o la limitación. Descargue algunas de las características en una puerta de
enlace API, particularmente las cuestiones de transversalidad como la administración de certificados,
la autenticación, la terminación SSL, la supervisión, la conversión de protocolos o la limitación.

El siguiente diagrama muestra una puerta de enlace API que termina las conexiones SSL entrantes.
Solicita datos en nombre del solicitante original desde cualquier servidor HTTP ascendente de la puerta
de enlace API.

180 CAPÍTULO 6 | Catálogo de patrones


Beneficios de este patrón:
●● Simplifica el desarrollo de los servicios eliminando la necesidad de distribuir y mantener recursos
de apoyo, tales como certificados de servidor web y configuración de sitios web seguros. La mayor
sencillez de la configuración facilita la administración y la escalabilidad y simplifica las actualizaciones
del servicio.

●● Permite que equipos dedicados implementen características que requieren conocimientos


especializados, como la seguridad. De esta forma, su equipo principal puede concentrarse en la
funcionalidad de la aplicación, dejando estas cuestiones especializadas pero transversales en manos
de los expertos correspondientes.

●● Proporciona cierta coherencia para el registro y la supervisión de solicitudes y respuestas. Incluso


si un servicio no se instrumenta correctamente, la puerta de enlace puede configurarse para asegurar
un nivel mínimo de supervisión y registro.

Problemas y consideraciones
●● Asegúrese de que la puerta de enlace API es altamente disponible y resistente a los errores. Evite
puntos únicos de error ejecutando varias instancias de su puerta de enlace API.

●● Asegúrese de que la puerta de enlace está diseñada para los requisitos de capacidad y escalado de
su aplicación y puntos de conexión. Asegúrese de que la puerta de enlace no se convierte en un
cuello de botella para la aplicación y es lo suficientemente escalable.

●● Solo descargue las características que utiliza toda la aplicación, tales como la transferencia de datos
o la seguridad.

●● La lógica de negocio no se debería descargar nunca en la puerta de enlace API.

●● Si necesita realizar un seguimiento de las transacciones, piense en la posibilidad de generar ID de


correlación con fines de registro.

Cuándo utilizar este patrón


Puede utilizar este patrón en los siguientes casos:
●● La implementación de una aplicación tenga un aspecto común, como el cifrado o los certificados SSL.

●● Haya una característica común en las implementaciones de aplicaciones que pueden tener diferentes
requisitos de recursos, como recursos de memoria, capacidad de almacenamiento o conexiones de red.

181 CAPÍTULO 6 | Catálogo de patrones


●● Desee trasladar la responsabilidad de problemas como la seguridad de la red, la limitación u otras
cuestiones de los límites de la red a un equipo más especializado.

Este patrón puede no ser conveniente si introduce el acoplamiento en los servicios.

Ejemplo
La siguiente configuración, en la que se utiliza Nginx como dispositivo de descarga SSL, termina una
conexión SSL entrante y distribuye la conexión a uno de tres servidores HTTP ascendentes.

upstream iis {
server 10.3.0.10 max_fails=3 fail_timeout=15s;
server 10.3.0.20 max_fails=3 fail_timeout=15s;
server 10.3.0.30 max_fails=3 fail_timeout=15s;
}

server {
listen 443;
ssl on;
ssl_certificate /etc/nginx/ssl/domain.cer;
ssl_certificate_key /etc/nginx/ssl/domain.key;

location / {
set $targ iis;
proxy_pass http://$targ;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
}
}

Orientación relacionada
●● Patrón Back-ends for Front-ends
●● Patrón de agregación de puerta de enlace
●● Patrón de enrutamiento de puerta de enlace

Patrón de enrutamiento de puerta de enlace


Enruta las solicitudes a varios servicios mediante un único punto de conexión. Este patrón es útil cuando
desea exponer varios servicios en un solo punto de conexión y enrutarlos hacia el servicio correspondiente
basado en la solicitud.

Contexto y problema
Cuando un cliente necesita consumir varios servicios, puede ser difícil establecer un punto de conexión
separado para cada servicio y que el cliente administre cada punto de conexión. Por ejemplo, una
aplicación de comercio electrónico podría proporcionar servicios de búsqueda, comentarios, cesta de la
compra, pago e historial de pedidos. Cada servicio tiene una API diferente con la que debe interactuar el
cliente y el cliente debe conocer cada punto de conexión para conectarse a los servicios. Si se modifica o
actualiza, el cliente debe actualizarse también. Si refactoriza un servicio en dos o más servicios distintos,
debe cambiar el código en el servicio y el cliente.

182 CAPÍTULO 6 | Catálogo de patrones


Solución
Coloque una puerta de enlace frente a un conjunto de aplicaciones, servicios o implementaciones. Use el
enrutamiento de capa 7 de las aplicaciones para enrutar la solicitud a las instancias correspondientes.

Con este patrón, la aplicación cliente solo necesita conocer un solo punto de conexión y comunicarse
con él. Si un servicio está consolidado o descompuesto, el cliente no requiere necesariamente una
actualización. Puede seguir realizando solicitudes a la puerta de enlace y solo cambia el enrutamiento.

Una puerta de enlace también permite resumir los servicios de back-end de los clientes, lo que le permite
que las llamadas de cliente sean sencillas y, al mismo tiempo, permite cambios en los servicios de back-end
detrás de la puerta de enlace. Las llamadas de cliente se pueden enrutar a cualquier servicio o servicios
que se necesiten para manejar el comportamiento esperado del cliente, lo que le permite añadir, dividir
y reorganizar los servicios detrás de la puerta de enlace sin cambiar el cliente.

Este patrón también puede ayudar con la implementación, permitiéndole administrar la forma en que se
despliegan las actualizaciones para los usuarios. Cuando se implementa una nueva versión de su servicio,
puede hacerse en paralelo con la versión existente. El enrutamiento le permite controlar qué versión del
servicio se presenta a los clientes, lo que le da la flexibilidad de utilizar diversas estrategias de publicación,
ya sean incrementales, en paralelo o implementaciones completas de actualizaciones. Cualquier problema
que se descubra después de que se implemente el nuevo servicio se puede revertir rápidamente
realizando un cambio de configuración en la puerta de enlace, sin que eso afecte a los clientes.

Problemas y consideraciones
●● El servicio de puerta de enlace puede introducir un único punto de fallo. Asegúrese de que está
diseñado correctamente para satisfacer sus requisitos de disponibilidad. A la hora de realizar la
implementación, tenga en cuenta las capacidades de resistencia y tolerancia a fallos.

●● El servicio de puerta de enlace puede presentar un cuello de botella. Asegúrese de que la puerta de
enlace tenga un rendimiento suficiente para manejar la carga y pueda escalarse fácilmente conforme
a sus expectativas de crecimiento.

183 CAPÍTULO 6 | Catálogo de patrones


●● Realice pruebas de carga en la puerta de enlace para asegurarse de que no se introducen errores
en cascada de los servicios.

●● El enrutamiento de puerta de enlace es de nivel 7. Se puede basar en la IP, el puerto, el encabezado


o la URL.

Cuándo utilizar este patrón

Puede utilizar este patrón en los siguientes casos:


●● Un cliente necesita consumir varios servicios a los que se puede acceder detrás de una puerta
de enlace.

●● Desea simplificar aplicaciones cliente mediante el uso de un solo punto de conexión.

●● Necesita enrutar solicitudes desde puntos de conexión con direcciones externas a puntos de conexión
virtuales internos, como los puertos que se exponen en una máquina virtual para agrupar en un
clúster las direcciones IP virtuales.

Puede que este patrón no sea adecuado cuando se tiene una aplicación sencilla que utiliza solamente
uno o dos servicios.

Ejemplo
El siguiente archivo de configuración, en el que se utiliza Nginx como enrutador, es un ejemplo de un
archivo de configuración sencillo en el que un servidor enruta solicitudes para aplicaciones que residen
en diferentes directorios virtuales para diferentes máquinas en el back-end.

server {
listen 80;
server_name domain.com;

location /app1 {
proxy_pass http://10.0.3.10:80;
}

location /app2 {
proxy_pass http://10.0.3.20:80;
}

location /app3 {
proxy_pass http://10.0.3.30:80;
}
}

Orientación relacionada
●● Patrón Back-ends for Front-ends
●● Patrón de agregación de puerta de enlace
●● Patrón de descarga de puerta de enlace

184 CAPÍTULO 6 | Catálogo de patrones


Patrón de supervisión de puntos de conexión
de estado
Implementa comprobaciones funcionales en una aplicación a la que pueden acceder herramientas
externas mediante puntos de conexión expuestos a intervalos regulares. Esto puede ayudar a verificar
si las aplicaciones y servicios están funcionando correctamente.

Contexto y problema
Es una buena práctica y, a menudo, un requisito empresarial, supervisar las aplicaciones web y servicios de
back-end para asegurarse de que están disponibles y funcionan correctamente. Sin embargo, es más difícil
supervisar los servicios que se ejecutan en el cloud que supervisar los servicios locales. Por ejemplo, no se
tiene control completo del entorno de hosting y, normalmente, los servicios dependen de otros servicios
proporcionados por proveedores de plataformas y terceros.

Hay muchos factores que afectan a las aplicaciones alojadas en cloud, como la latencia de red, el rendimiento
y la disponibilidad de los sistemas informáticos y de almacenamiento subyacentes y el ancho de banda de
red entre ellos. El servicio puede fallar completa o parcialmente por cualquiera de estos factores. Por lo tanto,
se debe verificar a intervalos regulares que el servicio esté funcionando correctamente para garantizar el
necesario nivel de disponibilidad, que puede formar parte de su acuerdo de nivel de servicio (SLA).

Solución
Implemente la supervisión del estado mediante el envío de solicitudes a un punto de conexión en la
aplicación. La aplicación debe realizar las comprobaciones necesarias y devolver una indicación de su estado.

Normalmente, una supervisión de estado combina dos factores:

●● Las comprobaciones (si hay alguna) que realiza la aplicación o servicio en respuesta a la solicitud
al punto de conexión de la verificación de estado.

●● Análisis de los resultados de la herramienta o el marco que realiza la verificación de estado.

El código de respuesta indica el estado de la aplicación y, opcionalmente, alguno de los componentes o


servicios que utiliza. La comprobación de la latencia o el tiempo de respuesta se realiza con la herramienta
o el marco de supervisión. La figura proporciona información general del patrón.

185 CAPÍTULO 6 | Catálogo de patrones


Estas son algunas otras comprobaciones que podrían realizarse por medio del código de supervisión de
estado de la aplicación:

●● Comprobación de la disponibilidad y el tiempo de respuesta del almacenamiento en cloud o una base


de datos.

●● Comprobación de otros recursos o servicios situados en la aplicación o en otro lugar, pero que utiliza
la aplicación.

Hay disponibles servicios y herramientas que supervisan aplicaciones web enviando una solicitud a un
conjunto de puntos de conexión configurables y evaluando los resultados con respecto a un conjunto
de reglas configurables. Es relativamente sencillo crear un punto de conexión de servicio cuyo único
propósito sea llevar a cabo algunas pruebas funcionales en el sistema.

Comprobaciones típicas que se pueden realizar por medio de las herramientas de supervisión:

●● Validación del código de respuesta. Por ejemplo, una respuesta HTTP 200 (OK) indica que la aplicación
respondió sin errores. El sistema de supervisión también puede comprobar otros códigos de respuesta
para obtener resultados más completos.

●● Comprobación del contenido de la respuesta para detectar errores, incluso cuando se devuelve un
código de estado 200 (OK). De esta forma, se pueden detectar errores que solo afectan a una sección
de la página web devuelta o la respuesta al servicio. Por ejemplo, la comprobación del título de una
página o la búsqueda de una frase específica que indica que se ha devuelto la página correcta.

●● Medición del tiempo de respuesta, que indica una combinación de la latencia de red y el tiempo que
ha tardado la aplicación en ejecutar la solicitud. Un valor en aumento puede indicar un problema
emergente con la aplicación o la red.

●● Comprobación de recursos o servicios ubicados fuera de la aplicación, como una red de distribución
de contenido que utiliza la aplicación para entregar contenido desde cachés globales.

●● Comprobación de la caducidad de los certificados SSL.

●● Medición del tiempo de respuesta de una búsqueda de DNS para la dirección URL de la aplicación
con el fin de medir la latencia de DNS y los fallos de DNS.

●● Validación de la dirección URL que devuelve la consulta DNS para garantizar entradas correctas. Esto
puede ayudar a evitar el redireccionamiento malintencionado de solicitudes por medio de un ataque
en el servidor DNS.

También es útil, siempre que sea posible, realizar estas comprobaciones desde diferentes ubicaciones
locales o alojadas para medir y comparar los tiempos de respuesta. Idealmente, debería supervisar
las aplicaciones desde ubicaciones que estén cerca de los clientes para obtener una visión precisa
del rendimiento de cada ubicación. Además de proporcionar un mecanismo de comprobación más
robusto, los resultados pueden ayudarle a decidir la ubicación de implementación de la aplicación
y si se implementa en más de un centro de datos.

También se deben realizar pruebas en todas las instancias de servicio que utilizan los clientes para
asegurarse de que la aplicación está funcionando correctamente para todos los clientes. Por ejemplo,
si el almacenamiento de cliente se extiende a más de una cuenta de almacenamiento, el proceso de
supervisión debería comprobar todo esto.

Problemas y consideraciones
Tenga en cuenta los puntos siguientes a la hora de decidir cómo implementar este patrón:
Cómo validar la respuesta. Por ejemplo, ¿un único código de estado 200 (OK) es suficiente para verificar
si la aplicación está funcionando correctamente? Aunque esto sirve de medida básica de la disponibilidad
de las aplicaciones y es la implementación mínima de este patrón, proporciona poca información sobre las
operaciones, tendencias y posibles problemas futuros en la aplicación.

186 CAPÍTULO 6 | Catálogo de patrones


Asegúrese de que la aplicación devuelve correctamente un código 200 (OK) cuando se encuentra y
procesa el recurso de destino. En algunas situaciones, como cuando se utiliza una página maestra para
alojar la página web de destino, el servidor envía un código de estado 200 (OK) en lugar de un código 404
(no encontrado), aun cuando no se ha encontrado la página de contenido de destino.

El número de puntos de conexión que se van a exponer para una aplicación. Un enfoque consiste en
exponer al menos un punto de conexión para los servicios fundamentales usados por la aplicación y otro
para servicios de menor prioridad, permitiendo la asignación de distintos niveles de importancia a cada
resultado de supervisión. Considere también la posibilidad de exponer más puntos de conexión, por
ejemplo, uno para cada servicio fundamental, a fin de obtener granularidad de supervisión adicional. Por
ejemplo, una comprobación de verificación de estado podría analizar la base de datos, el almacenamiento
y un servicio de geocodificación externo que usa una aplicación, requiriendo todo ello un nivel distinto
de tiempo de actividad y de respuesta. La aplicación podría seguir en buen estado si el servicio de
geocodificación o alguna otra tarea en segundo plano no está disponible durante unos minutos.

Si se usa el mismo punto de conexión para la supervisión de la misma forma que para el acceso general,
pero en una ruta de acceso específica diseñada para las comprobaciones de verificación de estado,
por ejemplo, /HealthCheck/{GUID}/ en el punto de conexión de acceso general. Esto permite que las
herramientas de supervisión ejecuten algunas pruebas funcionales de la aplicación, como agregar un
registro de nuevo usuario, iniciar sesión y hacer un pedido de prueba, comprobando también que el punto
de conexión de acceso general está disponible.

El tipo de información que se va a recopilar en el servicio en respuesta a las solicitudes de supervisión y


cómo devolver esta información. La mayoría de las herramientas y los marcos analizan solo el código de
estado HTTP que devuelve el punto de conexión. Para devolver y validar información adicional, podría
tener que crear un servicio o utilidad de supervisión personalizados.

Cuánta información se va a recopilar. Realizar un procesamiento excesivo durante la comprobación puede


sobrecargar la aplicación y afectar a otros usuarios. El tiempo que tarda podría superar el tiempo de
espera del sistema de supervisión, por lo que marca la aplicación como no disponible. La mayoría de las
aplicaciones incluyen instrumentación como controladores de errores y contadores de rendimiento que
registran el rendimiento e información de error detallada. Esto podría ser suficiente en lugar de devolver
información adicional de una comprobación de verificación de estado.

Almacenamiento en caché del estado del punto de conexión. Podría resultar caro ejecutar la
comprobación de estado con demasiada frecuencia. Si se informa del estado de mantenimiento a través
de un panel, por ejemplo, no quiere que cada solicitud del panel desencadene una comprobación de
estado. En su lugar, compruebe de forma periódica el estado del sistema y almacénelo en caché. Exponga
un punto de conexión que devuelva el estado almacenado en caché.

Cómo configurar la seguridad para los puntos de conexión de supervisión a fin de protegerlos del acceso
público, que podría exponer la aplicación a los ataques malintencionados, dar lugar a la exposición de
información confidencial o atraer los ataques de denegación de servicio (DoS). Normalmente, esto debería
hacerse en la configuración de la aplicación, de modo que pueda actualizarse fácilmente sin reiniciar la
aplicación. Considere la posibilidad de usar una o más de las siguientes técnicas:
●● Proteja el punto de conexión exigiendo autenticación. Para ello, use una clave de seguridad de
autenticación en el encabezado de solicitud o pase credenciales con la solicitud, siempre que el
servicio o herramienta de supervisión admita la autenticación.

● Use un punto de conexión confuso u oculto. Por ejemplo, exponga el punto de conexión
de una dirección IP diferente a la usada por la dirección URL de la aplicación predeterminada,
configure el punto de conexión de un puerto HTTP estándar o use una ruta de acceso
compleja a la página de prueba. Normalmente, puede especificar puertos y direcciones
de punto de conexión adicionales en la configuración de la aplicación, así como agregar
entradas de estos puntos de conexión al servidor DNS si es necesario para evitar tener que
especificar la dirección IP directamente.

187 CAPÍTULO 6 | Catálogo de patrones


● Exponga un método en un punto de conexión que acepte un parámetro como un valor
de clave o un valor de modo de operación. Dependiendo del valor suministrado para este
parámetro, cuando se recibe una solicitud, el código puede realizar una prueba o conjunto de
pruebas específicas, o bien devolver un error 404 (No encontrado) si el valor de parámetro no
se reconoce. Los valores de parámetro reconocidos podrían establecerse en la configuración
de la aplicación.

● Es probable que los ataques de DoS tengan menos impacto en un punto de conexión
independiente que realice pruebas funcionales básicas sin comprometer el funcionamiento
de la aplicación. Lo ideal es evitar el uso de una prueba que podría exponer información
confidencial. Si debe devolver información que podría ser útil para un atacante, tenga en
cuenta cómo protegerá el punto de conexión y los datos frente al acceso no autorizado. En
este caso, no es suficiente basarse simplemente en la confusión. También debería considerar
la posibilidad de usar una conexión HTTPS y cifrar los datos confidenciales, aunque esto
aumentará la carga del servidor.

●● Cómo obtener acceso a un punto de conexión protegido mediante autenticación. No todas las
herramientas y marcos se pueden configurar para incluir credenciales con la solicitud de verificación
de estado. Por ejemplo, las características de verificación de estado integradas de Microsoft Azure no
pueden proporcionar credenciales de autenticación. Algunas alternativas de terceros son Pingdom,
Panopta, NewRelic y Statuscake.

●● Cómo garantizar que el rendimiento del agente de supervisión sea correcto. Un enfoque consiste en
exponer un punto de conexión que solo devuelva un valor de la configuración de la aplicación o un
valor aleatorio que se pueda usar para realizar pruebas al agente.

Asimismo, garantice que el sistema de supervisión realice comprobaciones por su cuenta, como una
prueba automática y una prueba integrada, a fin de evitar que emita resultados falsos positivos.

Cuándo utilizar este patrón


Este patrón es útil para:
●● Supervisión de sitios web y aplicaciones web para comprobar su disponibilidad.

●● Supervisión de sitios web y aplicaciones web para comprobar su funcionamiento.

●● Supervisión de servicios de nivel intermedio o compartidos para detectar y aislar un error que podría
alterar otras aplicaciones.

●● Complementación de la instrumentación existente de la aplicación, como contadores de rendimiento


y controladores de errores. La comprobación de verificación de estado no reemplaza el requisito de
registro y auditoría de la aplicación. La instrumentación puede proporcionar información valiosa para
un marco existente que supervise los contadores y registros de errores para detectar errores u otros
problemas. Sin embargo, no puede proporcionar información si la aplicación no está disponible.

Ejemplo
En los siguientes ejemplos de código, tomados de la clase HealthCheckController (en GitHub hay
disponible una muestra que explica este patrón), se puede ver la exposición de un punto de conexión
para realizar diversas comprobaciones de estado.

El método CoreServices, mostrado a continuación en C#, realiza una serie de comprobaciones de los
servicios usados en la aplicación. Si todas las pruebas se ejecutan sin error, el método devuelve un código
de estado 200 (Correcto). Si cualquiera de las pruebas genera una excepción, el método devuelve un
código de estado 500 (Error interno). El método podría, de forma opcional, devolver información adicional
al producirse un error, si la herramienta o el marco de supervisión puede hacer uso de ella.

188 CAPÍTULO 6 | Catálogo de patrones


public ActionResult CoreServices()
{
try
{
// Run a simple check to ensure the database is available.
DataStore.Instance.CoreHealthCheck();

// Run a simple check on our external service.


MyExternalService.Instance.CoreHealthCheck();
}
catch (Exception ex)
{
Trace.TraceError(“Exception in basic health check: {0}”, ex.Message);

// This can optionally return different status codes based on the exception.
// Optionally it could return more details about the exception.
// The additional information could be used by administrators who access the
// endpoint with a browser, or using a ping utility that can display the
// additional information.
return new HttpStatusCodeResult((int)HttpStatusCode.InternalServerError);
}
return new HttpStatusCodeResult((int)HttpStatusCode.OK);
}

El método ObscurePath muestra cómo puede leer una ruta de acceso en la configuración de la aplicación
y usarla como punto de conexión para las pruebas. En este ejemplo, en C#, también se muestra cómo
puede aceptar un identificador como parámetro y usarlo para comprobar si hay solicitudes válidas.

public ActionResult ObscurePath(string id)


{
// The id could be used as a simple way to obscure or hide the endpoint.
// The id to match could be retrieved from configuration and, if matched,
// perform a specific set of tests and return the result. If not matched it
// could return a 404 (Not Found) status.

// The obscure path can be set through configuration to hide the endpoint.
var hiddenPathKey = CloudConfigurationManager.GetSetting(“Test.ObscurePath”);

// If the value passed does not match that in configuration, return 404 (Not Found).
if (!string.Equals(id, hiddenPathKey))
{
return new HttpStatusCodeResult((int)HttpStatusCode.NotFound);
}

// Else continue and run the tests...


// Return results from the core services test.
return this.CoreServices();
}

El método TestResponseFromConfig muestra cómo puede exponer un punto de conexión que compruebe
si hay un valor de parámetro de configuración especificado.

public ActionResult TestResponseFromConfig()


{
// Health check that returns a response code set in configuration for testing.
var returnStatusCodeSetting = CloudConfigurationManager.GetSetting(
“Test.ReturnStatusCode”);

int returnStatusCode;

if (!int.TryParse(returnStatusCodeSetting, out returnStatusCode))


{
returnStatusCode = (int)HttpStatusCode.OK;
}

return new HttpStatusCodeResult(returnStatusCode);


}

189 CAPÍTULO 6 | Catálogo de patrones


Supervisión de puntos de conexión en aplicaciones hospedadas de Azure
Algunas opciones para supervisar puntos de conexión en aplicaciones de Azure son:
●● Use las características de supervisión integradas de Azure.

●● Use un servicio o marco de terceros como Microsoft System Center Operations Manager.

●● Cree una utilidad personalizada o un servicio que se ejecute por su cuenta o en un servidor
hospedado.

A pesar de que Azure proporciona un conjunto razonablemente completo de opciones de supervisión,


puede usar servicios y herramientas adicionales para ofrecer información adicional. Azure Management
Services proporciona un mecanismo de supervisión integrado para reglas para alertas. Gracias a la sección
sobre alertas de la página de servicios de administración de Azure Portal, puede configurar hasta diez
reglas para alertas por suscripción para sus servicios. Estas reglas especifican una condición y un valor
de umbral para un servicio como la carga de CPU o el número de solicitudes o errores por segundo, y el
servicio puede enviar automáticamente notificaciones por correo electrónico a las direcciones que defina
en cada regla.

Las condiciones que puede supervisar varían dependiendo del mecanismo de hosting que elija para la
aplicación (como Web Sites, Cloud Services, Virtual Machines o Mobile Services), pero todas estas incluyen
la capacidad de crear una regla para alertas que usa un punto de conexión web que especifique en la
configuración del servicio. Este punto de conexión debe responder de forma oportuna de modo que
el sistema de alerta pueda detectar que la aplicación funciona correctamente.

Lea más información sobre cómo crear notificaciones de alertas.

Si hospeda la aplicación en roles web o de trabajo de Azure Cloud Services o Virtual Machines, puede
aprovechar uno de los servicios integrados de Azure llamado Traffic Manager. Traffic Manager es un
servicio de enrutamiento y equilibrio de carga que puede distribuir solicitudes a instancias específicas
de su aplicación hospedada de Cloud Services basada en una variedad de reglas y configuraciones.

Además de las solicitudes de enrutamiento, Traffic Manager hace ping a una dirección URL, un puerto
y una ruta de acceso relativa que especifique de forma periódica para determinar qué instancias de la
aplicación definida en sus reglas son activas y responden a solicitudes. Si detecta un código de estado
200 (Correcto), marca la aplicación como disponible. Cualquier otro código de estado hace que Traffic
Manager marque la aplicación como sin conexión. Puede ver el estado en la consola de Traffic Manager
y configurar la regla para recalcular solicitudes a otras instancias de la aplicación que respondan.

Sin embargo, Traffic Manager solo esperará diez segundos para recibir una respuesta de la dirección URL
de supervisión. Por lo tanto, debe garantizar que el código de verificación de estado se ejecute en este
momento, permitiendo la latencia de red para el viaje de ida y vuelta de Traffic Manager a la aplicación
y viceversa.

Lea más información sobre cómo usar Traffic Manager para supervisar las aplicaciones. También se
debate sobre Traffic Manager en la guía de implementación de varios centros de datos.

190 CAPÍTULO 6 | Catálogo de patrones


Orientación relacionada
La siguiente guía puede resultar útil al implementarse este patrón:
●● Guía de telemetría e instrumentación. La comprobación del estado de los servicios y componentes
suele llevarse a cabo por sondeo, pero también resulta de utilidad contar con información para
supervisar el rendimiento de la aplicación y detectar eventos acontecidos en tiempo de ejecución.
Estos datos se pueden transmitir de nuevo a las herramientas de supervisión como información
adicional para la supervisión de estado. En la guía de telemetría e instrumentación se explora la
recopilación de información de diagnóstico remota reunida por la instrumentación en las aplicaciones.

●● Recepción de notificaciones de alertas.

●● Este patrón incluye una aplicación de ejemplo que se puede descargar.

Patrón de tabla de índice


Crea índices sobre los campos de los almacenes de datos a los que se suele hacer referencia en las
solicitudes. Este patrón puede mejorar el rendimiento de las consultas permitiendo a las aplicaciones
encontrar rápidamente los datos que se desean recuperar de un almacén de datos.

Contexto y problema
Muchos almacenes de datos organizan los datos para una recopilación de entidades mediante la clave
principal. Una aplicación puede usar esta clave para encontrar y recuperar datos. En la figura se muestra un
ejemplo de un almacén de datos que incluye información del cliente. La clave principal es el Id. de cliente.
En la figura se muestra información del cliente organizada por la clave principal (Id. de cliente).

Aunque la clave principal es valiosa para las consultas que capturan datos en función del valor de esta
clave, una aplicación podría no poder usar la clave principal si necesita recuperar datos en función de
algún otro campo. En el ejemplo de los clientes, una aplicación no puede usar la clave principal Id.
de cliente para recuperar clientes si consulta datos únicamente haciendo referencia al valor de algún
otro atributo, como la ciudad en la que se encuentra el cliente. Para realizar una consulta como esta,
la aplicación podría tener que capturar y examinar todos los registros de cliente, lo que podría ser un
proceso lento.

Muchos sistemas de administración de bases de datos relacionales admiten índices secundarios.


Un índice secundario es una estructura de datos independiente organizada por uno o varios campos

191 CAPÍTULO 6 | Catálogo de patrones


clave no principales (secundarios) e indica dónde se almacenan los datos de cada valor indexado. Los
elementos de un índice secundario se suelen ordenar por el valor de las claves secundarias para habilitar
la búsqueda rápida de datos. Normalmente, el sistema de administración de bases de datos mantiene
automáticamente estos índices.

Puede crear tantos índices secundarios como necesite para respaldar las diferentes consultas realizadas
por la aplicación. Por ejemplo, en una tabla Clientes de una base de datos relacional donde el Id. de cliente
es la clave principal, es beneficioso agregar un índice secundario sobre el campo de ciudad si la aplicación
busca con frecuencia clientes según su ciudad de residencia.

Sin embargo, aunque los índices secundarios son habituales en los sistemas relacionales, la mayoría de
los almacenes de datos NoSQL usados por las aplicaciones en el cloud no proporcionan una característica
equivalente.

Solución
Si el almacén de datos no admite los índices secundarios, puede emularlos manualmente creando sus
propias tablas de índice. Una tabla de índice organiza los datos según una clave especificada. Se suelen
usar tres estrategias para estructurar una tabla de índice, dependiendo del número de índices secundarios
necesarios y la naturaleza de las consultas realizadas por una aplicación.

La primera estrategia es duplicar los datos de cada tabla de índice, pero organizarlos según diferentes
claves (desnormalización completa). En la siguiente figura se muestran las tablas de índice que organizan
la misma información de cliente por Ciudad y Apellido.

Esta estrategia es adecuada si los datos son relativamente estáticos en comparación con el número de
veces que se consultan con cada clave. Si los datos son más dinámicos, la sobrecarga de procesamiento
de mantenimiento de cada tabla de índice resulta demasiado grande para que este enfoque sea útil.
Asimismo, si el volumen de datos es muy grande, la cantidad de espacio necesaria para almacenar los
datos duplicados es significativa.

La segunda estrategia es crear tablas de índice normalizadas organizadas por diferentes claves y hacer
referencia a los datos originales mediante la clave principal en lugar de duplicarlos, como se muestra
en la siguiente figura. Los datos originales se llaman tabla de hechos.

192 CAPÍTULO 6 | Catálogo de patrones


Esta técnica ahorra espacio y reduce la sobrecarga de mantenimiento de los datos duplicados. La
desventaja es que una aplicación debe realizar dos operaciones de búsqueda para encontrar los datos
con una clave secundaria. Tiene que encontrar la clave principal de los datos en la tabla de índice y,
a continuación, usarla para buscar los datos en la tabla de hechos.

La tercera estrategia es crear tablas de índice normalizadas parcialmente organizadas por claves diferentes
que duplican campos recuperados con frecuencia. Haga referencia a la tabla de hechos para tener acceso
a campos a los que se tiene acceso de forma menos habitual. En la siguiente figura se muestra cómo
se duplican los datos a los que se tiene acceso de forma menos habitual en cada tabla de índice.

Con esta estrategia, puede lograr un equilibrio entre los primeros dos enfoques. Los datos de las consultas
habituales se pueden recuperar rápidamente mediante una sola búsqueda, mientras la sobrecarga de
espacio y mantenimiento no sea tan significativa como la duplicación de todo el conjunto de datos.

Si una aplicación consulta datos con frecuencia especificando una combinación de valores (por ejemplo,
"Busque todos los clientes que viven en Redmond y cuyo apellido es Smith"), podría implementar las
claves en los elementos de la tabla de índice como una concatenación del atributo Ciudad y el atributo
Apellido. En la siguiente figura se muestra una tabla de índice basada en claves compuestas. Las claves
se ordenan por Ciudad y, a continuación, por Apellido para los registros con el mismo valor de Ciudad.

193 CAPÍTULO 6 | Catálogo de patrones


Las tablas de índice pueden acelerar las operaciones de consulta sobre los datos con particiones y resultan
especialmente útiles allí donde se ha aplicado un algoritmo hash a la clave de partición. En la siguiente
figura se muestra un ejemplo donde la clave de partición es un hash del Id. de cliente. La tabla de índice
puede organizar los datos según el valor al que no se ha aplicado un algoritmo hash (Ciudad y Apellido)
y proporcionar la clave de partición fragmentada como los datos de búsqueda. Esto puede ahorrar a la
aplicación el cálculo repetido de claves hash (una operación cara) si necesita recuperar los datos que
se engloban en un rango o necesita capturar datos por orden de clave a la que no se ha aplicado un
algoritmo hash. Por ejemplo, una consulta como "Busque todos los clientes que viven en Redmond"
se puede resolver rápidamente localizando los elementos coincidentes en la tabla de índice, donde se
almacenan todos en un bloque contiguo. A continuación, siga las referencias a los datos de los clientes
mediante las claves de partición almacenadas en la tabla de índice.

Problemas y consideraciones
Tenga en cuenta los puntos siguientes a la hora de decidir cómo implementar este patrón:
●● La sobrecarga de mantenimiento de los índices secundarios puede ser significativa. Debe analizar
y comprender las consultas que usa su aplicación. Cree solo tablas de índice cuando sea posible su
uso regular. No cree tablas de índice especulativas para respaldar las consultas no realizadas por una
aplicación o realizadas solo de forma ocasional.

194 CAPÍTULO 6 | Catálogo de patrones


La duplicación de datos en una tabla de índice puede agregar una sobrecarga significativa en los
costes de almacenamiento y el esfuerzo necesario para mantener varias copias de datos.

●● La implementación de una tabla de índice como estructura normalizada que hace referencia a los
datos originales requiere que una aplicación realice dos operaciones de búsqueda para encontrar
datos. La primera operación busca la tabla de índice para recuperar la clave principal y la segunda
usa la clave principal para capturar los datos.

●● Si un sistema incorpora un número de tablas de índice sobre conjuntos de datos muy grandes, puede
resultar difícil mantener la coherencia entre las tablas de índice y los datos originales. Puede que sea
posible diseñar la aplicación en torno al modelo de coherencia al cabo del tiempo. Por ejemplo, para
insertar, actualizar o eliminar datos, una aplicación podría enviar un mensaje a una cola y permitir a
una tarea independiente realizar la operación y mantener las tablas de índice que hacen referencia
a estos datos de forma asincrónica. Para obtener más información sobre cómo implementar la
coherencia a lo largo del tiempo, consulte la Introducción a la coherencia de datos.

●● Las tablas de almacenamiento de Microsoft Azure admiten las actualizaciones transaccionales para los
cambios realizados en los datos albergados en la misma partición, que se denomina transacciones de
grupos de entidades. Si puede almacenar los datos de una tabla de hechos y una o varias tablas de
índice en la misma partición, puede usar esta característica para ayudar a garantizar la coherencia.

●● Las propias tablas de índice podrían particionarse.

Cuándo utilizar este patrón


Use este patrón para mejorar el rendimiento de las consultas cuando una aplicación deba recuperar datos
con frecuencia mediante una clave distinta de la calve principal (o de partición).

Este patrón podría no ser útil cuando:


●● Los datos son volátiles. Una tabla de índice puede dejar de estar actualizada con mucha rapidez,
lo que hace que sea ineficaz o que la sobrecarga de mantenimiento de la tabla de índice sea mayor
que el ahorro destinado a su uso.

●● Un campo seleccionado como clave secundaria de una tabla de índice es imparcial y solo puede tener
un conjunto pequeño de valores (por ejemplo, género).

●● El equilibrio de los valores de los datos de un campo seleccionado como la clave secundaria de una tabla
de índice es altamente sesgado. Por ejemplo, si el 90 % de los registros contienen el mismo valor en
un campo, la creación y el mantenimiento de una tabla de índice para buscar datos en función de este
campo podría crear una mayor sobrecarga que la detección secuencial a través de los datos. Sin embargo,
si las consultas se dirigen con frecuencia a los valores situados en el 10 % restante, este índice puede
resultar útil. Debe entender las consultas realizadas por su aplicación y la frecuencia con que lo hace.

Ejemplo
Las tablas de almacenamiento de Azure proporcionan un almacén de datos de pares clave-valor altamente
escalable para las aplicaciones que se ejecutan en el cloud. Las aplicaciones almacenan y recuperan los
valores de los datos especificando una clave. Los valores de los datos pueden contener varios campos,
pero la estructura de un elemento de datos es opaca para el almacenamiento de tablas, que se limita
a controlar un elemento de datos como una matriz de bytes.

Las tablas de almacenamiento de Azure también admiten el particionamiento. La clave de


particionamiento incluye dos elementos, una clave de partición y una clave de fila. Los elementos que
tienen la misma clave de partición se almacenan en la misma partición y los elementos se almacenan

195 CAPÍTULO 6 | Catálogo de patrones


por orden de clave de fila en una partición. El almacenamiento de tablas se optimiza para realizar
consultas que capturen datos englobados en un rango contiguo de valores de fila en una partición. Si
crea aplicaciones en el cloud que almacenan información en tablas de Azure, debe estructurar los datos
teniendo en cuenta esta característica.

Por ejemplo, piense en una aplicación que almacena información sobre películas. La aplicación suele
consultar las películas por género (acción, documental, histórica, comedia, drama, etc.). Podría crear una
tabla de Azure con particiones para cada género usando este como clave de partición y especificando el
nombre de la película como clave de fila, como se muestra en la siguiente figura.

Este enfoque es menos eficaz si también es necesario que la aplicación consulte las películas por actor
protagonista. En este caso, puede crear una tabla de Azure independiente que actúe como tabla de índice.
La clave de partición es el actor y la clave de fila es el nombre de la película. Los datos de cada actor se
almacenarán en particiones independientes. Si una película tiene más de un actor protagonista, la misma
película aparecerá en varias particiones.

Puede duplicar los datos de la película en los valores albergados por cada partición adoptando el
primer enfoque descrito en la sección Solución anterior. Sin embargo, es probable que cada película se
replique varias veces (una por cada actor), por lo que podría resultar más eficaz desnormalizar de forma
parcial los datos para respaldar las consultas (como los nombres de los otros actores) y permitir a una
aplicación recuperar cualquier detalle restante incluyendo la clave de partición necesaria para encontrar la
información completa en las particiones de género. En la tercera opción de la sección Solución se describe
este enfoque. En la siguiente figura se muestra este enfoque.

196 CAPÍTULO 6 | Catálogo de patrones


Guías y patrones relacionados
Los siguientes patrones y guías también pueden ser relevantes al implementar este patrón:
●● Introducción a la coherencia de datos. Una tabla de índice debe mantenerse, ya que los datos que
indexa cambian. En el cloud, podría no ser posible o adecuado realizar operaciones que actualicen un
índice como parte de la misma transacción que modifica los datos. En ese caso, un enfoque coherente
con el tiempo es más adecuado. Proporciona información sobre los problemas relacionados con la
coherencia al cabo del tiempo.

●● Patrón de particionamiento. El patrón de tabla de índice suele usarse junto con datos particionados
mediante particiones. El patrón de particionamiento proporciona más información sobre cómo dividir
un almacén de datos en un conjunto de particiones.

●● Patrón de vistas materializadas. En lugar de indexar datos para respaldar las consultas que resumen
los datos, podría ser más adecuado crear una vista materializada de los datos. Describe cómo
respaldar consultas de resumen eficaces generando vistas previamente completadas sobre los datos.

Patrón de elección del líder


Coordine las acciones realizadas por una recopilación de instancias de colaboración en una aplicación
distribuida eligiendo una instancia como líder que asume la responsabilidad por la administración del resto.
Esto puede ayudar a garantizar que las instancias no entren en conflicto entre sí, causen una contención
considerable para recursos o interfieran accidentalmente con el trabajo que realizan otras instancias.

Contexto y problema
Una aplicación en el cloud típica tiene muchas tareas que actúan de forma coordinada. Todas estas tareas
podrían ser instancias que ejecutan el mismo código y requieren acceso a los mismos recursos, o bien
podrían funcionar conjuntamente en paralelo para realizar las partes individuales de un cálculo complejo.

197 CAPÍTULO 6 | Catálogo de patrones


Las instancias de tarea podrían ejecutarse por separado la mayor parte del tiempo, pero también podría
ser necesario coordinar las acciones de cada instancia para garantizar que no entren en conflicto, que no
causen contención para recursos compartidos o que no interfieran accidentalmente con el trabajo que
realizan otras instancias de tarea.

Por ejemplo:

●● En un sistema basado en el cloud que implementa escalado horizontal, varias instancias de la misma
tarea podrían ejecutarse al mismo tiempo con cada instancia que presta servicio a un usuario
diferente. Si estas instancias se escriben en un recurso compartido, es necesario coordinar sus
acciones para evitar que cada instancia sobrescriba los cambios realizados por el resto.

●● Si las tareas realizan elementos individuales de un cálculo complejo en paralelo, los resultados deben
agregarse al completarse en su totalidad.

Todas las instancias de tarea son compañeras, por lo que no hay un líder natural que pueda actuar como
coordinador o acumulador.

Solución
Debe elegirse una sola instancia de tarea para actuar como líder y esta debe coordinar las acciones de las
otras instancias de tarea subordinadas. Si todas las instancias de tarea ejecutan el mismo código, cada una
podrá actuar como líder. Por lo tanto, el proceso de elección debe administrarse con cuidado para evitar
que dos o más instancias asuman el rol de líder al mismo tiempo.

El sistema debe proporcionar un mecanismo sólido para seleccionar el líder. Este método debe hacer
frente a eventos como interrupciones de red o errores de proceso. En muchas soluciones, las instancias de
tarea subordinadas supervisan el líder a través de algún tipo de método de latido o por sondeo. Si el líder
designado termina de forma inesperada o un error de red hace que el líder no se encuentre disponible
para las instancias de tarea subordinadas, es necesario la elección de un nuevo líder.

Existen varias estrategias para elegir un líder entre un conjunto de tareas en un entorno distribuido,
incluyéndose las siguientes:
●● Selección de la instancia de tarea con el Id. de proceso o la instancia de menor rango.

●● Adquisición rápida de una exclusión mutua compartida y distribuida. La primera instancia de tarea
que adquiere la exclusión mutua es el líder. Sin embargo, el sistema debe asegurarse de que, si el
líder termina o se desconecta del resto del sistema, la exclusión mutua se libera para permitir a otra
instancia de tarea convertirse en el líder.

●● Implementación de uno de los algoritmos de elección de líder habituales como Bully Algorithm o Ring
Algorithm. Estos algoritmos suponen que cada candidato de la elección tiene un identificador único
y que puede comunicarse con los otros candidatos de forma fiable.

Problemas y consideraciones
Tenga en cuenta los puntos siguientes a la hora de decidir cómo implementar este patrón:
●● El proceso de elección de un líder debe ser resistente a los errores transitorios y persistentes.

●● Debe ser posible detectar si el líder ha fracasado o, de lo contrario, ha dejado de estar disponible
(por ejemplo, debido a un error de comunicaciones). Con qué rapidez depende del sistema la

198 CAPÍTULO 6 | Catálogo de patrones


detección que es necesaria. Algunos sistemas podrían funcionar durante un breve período de tiempo
sin un líder. En este tiempo, un error transitorio podría corregirse. En otros casos, podría ser necesario
detectar un error del líder de forma inmediata y desencadenar una nueva elección.

●● En un sistema que implementa escalado automático horizontal, el líder podría terminarse si la escala
del sistema se reduce de nuevo y cierra algunos de los recursos informáticos.

●● El uso de una exclusión mutua compartida y distribuida presenta una dependencia del servicio
externo que proporciona la exclusión mutua. El servicio constituye un único punto de error. Si deja
de estar disponible por alguna razón, el sistema no podrá elegir un líder.

●● El uso de un solo proceso dedicado como líder es un enfoque muy sencillo. Sin embargo, si el proceso
no se realiza correctamente, podría producirse un retraso significativo mientras se reinicia. La latencia
resultante puede afectar al rendimiento y los tiempos de respuesta de otros procesos si esperan a que
el líder coordine una operación.

●● Al implementarse uno de los algoritmos de elección de líder manualmente se proporciona la mayor


flexibilidad para ajustar y optimizar el código.

Cuándo utilizar este patrón


Use este patrón cuando las tareas de una aplicación distribuida (por ejemplo, una solución hospedada
en el cloud) necesiten una coordinación cuidadosa y no haya un líder natural.

Evite hacer del líder un cuello de botella en el sistema. El propósito del líder es coordinar el trabajo de
las tareas subordinadas y no tiene que participar necesariamente en este trabajo (aunque debería poder
hacerlo si la tarea no se elige como líder).

Este patrón podría no ser útil si:


●● Existe un líder natural o proceso dedicado que siempre puede actuar como líder. Por ejemplo, podría
ser posible implementar un proceso singleton que coordine las instancias de tarea. Si este proceso no
se realiza correctamente o está en mal estado, el sistema puede cerrarlo y reiniciarlo.

●● La coordinación entre las tareas puede lograrse mediante un método más ligero. Por ejemplo, si varias
instancias de tarea solo necesitan acceso coordinado a un recurso compartido, una mejor solución
consiste en usar un bloqueo optimista o pesimista para controlar el acceso.

●● Una solución de terceros es más adecuada. Por ejemplo, el servicio de Microsoft Azure HDInsight
(basado en Apache Hadoop) usa los servicios proporcionados por Apache Zookeeper para coordinar
el mapa y reducir las tareas que recopilan y resumen datos.

Ejemplo
El proyecto DistributedMutex de la solución LeaderElection (en GitHub hay disponible una muestra que
explica este patrón) muestra cómo usar una concesión en Azure Storage Blob para proporcionar un
mecanismo para implementar una exclusión mutua compartida y distribuida. Esta exclusión mutua puede
usarse para elegir un líder entre un grupo de instancias de rol en un servicio de cloud de Azure. La primera
instancia de rol en adquirir la concesión se elige como líder y permanece como tal hasta que libera la
concesión o no puede renovar esta. Otras instancias de rol pueden seguir supervisando la concesión de
blobs en caso de que el líder deje de estar disponible.

199 CAPÍTULO 6 | Catálogo de patrones


Una concesión de blobs es un bloqueo de escritura exclusivo sobre un blob. Un solo blob puede ser el
tema de solo una concesión en cualquier punto en el tiempo. Una instancia de rol puede solicitar una
concesión sobre un blob especificado, concediéndose esta si ninguna otra instancia de rol cuenta con
una concesión sobre el mismo blob. De lo contrario, la solicitud presentará una excepción.

Para evitar una instancia de rol con errores que retenga la concesión de forma indefinida, especifique un
ciclo de vida para la concesión. Cuando este caduca, la concesión pasa a estar disponible. Sin embargo,
aunque una instancia de rol tiene la concesión, puede solicitar que esta se renueve y se le concederá la
concesión durante un período adicional. La instancia de rol puede repetir este proceso continuamente
si desea retener la concesión. Para obtener más información sobre cómo conceder un blob, consulte el
artículo relativo al blob de concesión (API de REST).

La clase BlobDistributedMutex del ejemplo de C# siguiente contiene el método RunTaskWhenMutexAquired


que permite a una instancia de rol intentar adquirir una concesión sobre un blob especificado. Los detalles
del blob (el nombre, el contenedor y la cuenta de almacenamiento) se pasan al constructor en un objeto
BlobSettings al crearse el objeto BlobDistributedMutex (este objeto es una estructura sencilla que se incluye
en el código de ejemplo). El constructor también acepta una tarea que hace referencia al código que la
instancia de rol debe ejecutar si adquiere correctamente la concesión sobre el blob y se elige como líder.
Tenga en cuenta que el código que controla los detalles de bajo nivel de adquisición de la concesión se
implementa en una clase auxiliar independiente denominada BlobLeaseManager.

public class BlobDistributedMutex


{
...
private readonly BlobSettings blobSettings;
private readonly Func<CancellationToken, Task> taskToRunWhenLeaseAcquired;
...

public BlobDistributedMutex(BlobSettings blobSettings,


Func<CancellationToken, Task> taskToRunWhenLeaseAquired)
{
this.blobSettings = blobSettings;
this.taskToRunWhenLeaseAquired = taskToRunWhenLeaseAquired;
}

public async Task RunTaskWhenMutexAcquired(CancellationToken token)


{
var leaseManager = new BlobLeaseManager(blobSettings);
await this.RunTaskWhenBlobLeaseAcquired(leaseManager, token);
}
...

El método RunTaskWhenMutexAquired del ejemplo de código anterior invoca el método


RunTaskWhenBlobLeaseAcquired mostrado en el siguiente ejemplo de código para adquirir realmente la
concesión. El método RunTaskWhenBlobLeaseAcquired se ejecuta de forma asincrónica. Si la concesión
se adquiere correctamente, la instancia de rol se ha elegido como líder. El objetivo del delegado
taskToRunWhenLeaseAcquired es realizar el trabajo que coordina las otras instancias de rol. Si no se
adquiere la concesión, otra instancia de rol se ha elegido como líder y la instancia de rol actual permanece
como subordinada. Tenga en cuenta que el método TryAcquireLeaseOrWait es un método auxiliar que usa
el objeto BlobLeaseManager para adquirir la concesión.

200 CAPÍTULO 6 | Catálogo de patrones


private async Task RunTaskWhenBlobLeaseAcquired(
BlobLeaseManager leaseManager, CancellationToken token)
{
while (!token.IsCancellationRequested)
{
// Try to acquire the blob lease.
// Otherwise wait for a short time before trying again.
string leaseId = await this.TryAquireLeaseOrWait(leaseManager, token);

if (!string.IsNullOrEmpty(leaseId))
{
// Create a new linked cancellation token source so that if either the
// original token is canceled or the lease can’t be renewed, the
// leader task can be canceled.
using (var leaseCts =
CancellationTokenSource.CreateLinkedTokenSource(new[] { token }))
{
// Run the leader task.
var leaderTask = this.taskToRunWhenLeaseAquired.Invoke(leaseCts.Token);
...
}
}
}
...
}

La tarea iniciada por el líder también se ejecuta de forma asincrónica. Mientras se ejecuta esta tarea,
el método RunTaskWhenBlobLeaseAquired mostrado en el siguiente ejemplo de código intenta renovar
la concesión de forma periódica. Esto ayuda a garantizar que la instancia de rol permanezca como líder.
En la solución de ejemplo, el retraso entre las solicitudes de renovación es inferior al tiempo especificado
para la duración de la concesión a fin de impedir que otra instancia de rol se elija como líder. Si la
renovación no se realiza correctamente por alguna razón, se cancela la tarea.

Si la concesión no se renueva correctamente o se cancela la tarea (posiblemente como resultado de la


instancia de rol que se cierra), se libera la concesión. En este momento, esta u otra instancia de rol podría
elegirse como líder. En el extracto de código siguiente se muestra esta parte del proceso.

private async Task RunTaskWhenBlobLeaseAcquired(


BlobLeaseManager leaseManager, CancellationToken token)
{
while (...)
{
...
if (...)
{
...
using (var leaseCts = ...)
{
...
// Keep renewing the lease in regular intervals.
// If the lease can’t be renewed, then the task completes.
var renewLeaseTask =
this.KeepRenewingLease(leaseManager, leaseId, leaseCts.Token);

// When any task completes (either the leader task itself or when it
// couldn’t renew the lease) then cancel the other task.
await CancelAllWhenAnyCompletes(leaderTask, renewLeaseTask, leaseCts);
}
}
}
}
...
}

201 CAPÍTULO 6 | Catálogo de patrones


El método KeepRenewingLease es otro método auxiliar que usa el objeto BlobLeaseManager para renovar
la concesión. El método CancelAllWhenAnyCompletes cancela las tareas especificadas como los primeros
dos parámetros. En el siguiente diagrama se ilustra el uso de la clase BlobDistributedMutex para elegir un
líder y ejecutar una tarea que coordina las operaciones.

En el siguiente ejemplo de código se muestra cómo usar la clase BlobDistributedMutex en un rol de


trabajo. Este código adquiere una concesión sobre un blob denominado MyLeaderCoordinatorTask
en el contenedor de la concesión en almacenamiento de desarrollo y especifica que el código definido
en el método MyLeaderCoordinatorTask debe ejecutarse si la instancia de rol se elige como líder.

202 CAPÍTULO 6 | Catálogo de patrones


var settings = new BlobSettings(CloudStorageAccount.DevelopmentStorageAccount,
“leases”, “MyLeaderCoordinatorTask”);
var cts = new CancellationTokenSource();
var mutex = new BlobDistributedMutex(settings, MyLeaderCoordinatorTask);
mutex.RunTaskWhenMutexAcquired(this.cts.Token);
...

// Method that runs if the role instance is elected the leader


private static async Task MyLeaderCoordinatorTask(CancellationToken token)
{
...
}

Tenga en cuenta los siguientes puntos sobre la solución de ejemplo:


●● El blob es un posible punto de error único. Si Blob service deja de estar disponible o es inaccesible,
el líder no podrá renovar la concesión y ninguna otra instancia de rol podrá adquirir la concesión. En
este caso, ninguna instancia de rol podrá actuar como líder. Sin embargo, Blob service está diseñado
para ser resistente, de modo que el colapso completo de Blob service se considera algo sumamente
remoto.

●● Si la tarea realizada por el líder se detiene, el líder podría continuar renovando la concesión,
impidiendo a cualquier otra instancia de rol adquirir la concesión y asumir el rol de líder para
coordinar las tareas. En el mundo real, el estado del líder debe comprobarse con frecuencia.

●● El proceso de elección no es determinista. No puede realizar ninguna suposición sobre qué instancia
de rol adquirirá la concesión de blobs y se convertirá en el líder.

●● El blob usado como destino de la concesión de blobs no debe utilizarse con cualquier otro propósito.
Si una instancia de rol intenta almacenar datos en este blob, no se podrá obtener acceso a estos datos
a menos que la instancia de rol sea el líder y cuente con la concesión de blobs.

Guías y patrones relacionados


Los siguientes consejos también pueden ser pertinentes al implementar este patrón:
●● Este patrón tiene una aplicación de ejemplo que se puede descargar.
●● Guía de escalado automático. Es posible iniciar y detener instancias de los hosts de la tarea al variar
la carga de la aplicación. El escalado automático puede ayudar a mantener el rendimiento durante los
momentos de más procesamiento.

●● Guía de partición de recursos informáticos. En esta guía se describe cómo asignar tareas a hosts en
un servicio de cloud de una manera que ayude a minimizar los costes manteniendo la escalabilidad, el
rendimiento, la disponibilidad y la seguridad del servicio.

●● El patrón asincrónico basado en tareas.

●● Un ejemplo donde se ilustra Bully Algorithm.

●● Un ejemplo donde se ilustra Ring Algorithm.

●● Apache Curator una biblioteca cliente para Apache ZooKeeper.

●● El artículo relativo al blob de concesión (API de REST) de MSDN.

203 CAPÍTULO 6 | Catálogo de patrones


Patrón de vistas materializadas
Genera vistas precompletadas sobre los datos en uno o más almacenes de datos cuando los datos no
tienen el formato ideal para las operaciones de consulta necesarias. Esto puede ayudar a respaldar la
realización de consultas adecuadas y la extracción de datos, así como a mejorar el rendimiento de las
aplicaciones.

Contexto y problema
Al almacenar datos, la prioridad para los desarrolladores y los administradores de datos se suele centrar en
cómo se almacenan los datos, a diferencia de cómo se leen. El formato de almacenamiento elegido suele
estar estrechamente relacionado con el formato de los datos, los requisitos para administrar el tamaño de
los datos, la integridad de los datos y el tipo de almacenamiento en uso. Por ejemplo, al usar el almacén
de documentos NoSQL, los datos suelen representarse como una serie de agregados, cada uno de los
cuales contiene toda la información de esa entidad.

Sin embargo, esto puede tener un efecto negativo en las consultas. Si una consulta solo necesita un
subconjunto de los datos de algunas entidades, como un resumen de los pedidos de varios clientes sin
todos los detalles de pedido, debe extraer todos los datos de las entidades pertinentes para obtener la
información requerida.

Solución
Para respaldar la realización de consultas adecuadas, una solución habitual es generar, de antemano, una
vista que materialice los datos en un formato adaptado al conjunto de resultados requeridos. El patrón
de vistas materializadas describe la generación de vistas previamente completadas de datos en entornos
donde los datos de origen no tienen un formato compatible con la realización de consultas, donde la
generación de una consulta adecuada es difícil o donde la realización de consultas es escasa debido
a la naturaleza de los datos o el almacén de datos.

Estas vistas materializadas, que solo contienen los datos requeridos por una consulta, permiten a las
aplicaciones obtener de forma rápida la información que necesitan. Además de unir tablas o combinar
entidades de datos, las vistas materializadas pueden incluir los valores actuales de las columnas o
elementos de datos calculados, los resultados de combinar valores o ejecutar transformaciones en los
elementos de datos y los valores especificados como parte de la consulta. Una vista materializada incluso
puede optimizarse para solo una consulta única.

Un punto clave es que una vista materializada y los datos que contiene son totalmente descartables
porque se pueden crear de nuevo por completo con los almacenes de datos de origen. Una aplicación
nunca actualiza directamente una vista materializada, de manera que se trata de una memoria caché
especializada.

Cuando los datos de origen de la vista cambian, esta debe actualizarse para incluir la nueva información.
Puede programar esto para que se produzca de forma automática o si el sistema detecta un cambio en
los datos originales. En algunos casos podría ser necesario regenerar la vista manualmente. En la figura
se muestra un ejemplo de cómo puede usarse el patrón de vistas materializadas.

204 CAPÍTULO 6 | Catálogo de patrones


Problemas y consideraciones
Tenga en cuenta los puntos siguientes a la hora de decidir cómo implementar este patrón:
Cómo y cuándo se actualizará la vista. Idealmente, regenerará la vista en respuesta a un evento indicador
de un cambio en los datos de origen, aunque esto puede dar lugar a una sobrecarga excesiva si los datos
de origen cambian rápidamente. Opcionalmente, considere la posibilidad de usar una tarea programada,
un desencadenador externo o una acción manual para regenerar la vista.

En algunos sistemas, por ejemplo, al usar el patrón de abastecimiento de eventos para mantener un
almacén con solo los eventos que han modificado los datos, las vistas materializadas son necesarias.
Completar vistas de forma previa examinando todos los eventos para determinar el estado actual
podría ser la única manera de obtener información del almacén de eventos. Si no usa el abastecimiento
de eventos, debe considerar si una vista materializada es útil o no. Las vistas materializadas tienden
a adaptarse de forma específica a una consulta o a un número pequeño de ellas. Si se usan muchas
consultas, las vistas materializadas pueden dar lugar a unos requisitos de capacidad de almacenamiento
inaceptables y al coste de almacenamiento.

Considere el impacto en la coherencia de los datos al generar la vista y al actualizarla si esto ocurre según
una programación establecida. Si los datos de origen cambian al generarse la vista, la copia de los datos
en la vista no será totalmente coherente con los datos originales.

Tenga en cuenta dónde almacenará la vista. La vista no debe estar en el mismo almacén o partición que
los datos originales. Puede ser un subconjunto de varias particiones diferentes combinadas.

Una vista se puede crear de nuevo en caso de pérdida. Por ello, si la vista es transitoria y se usa solo para
mejorar el rendimiento de las consultas reflejando el estado actual de los datos, o bien para mejorar la
escalabilidad, puede almacenarse en una memoria caché o en una ubicación menos fiable.

Al definir una vista materializada, maximice su valor agregándole columnas o elementos de datos en
función del cálculo o la transformación de elementos de datos existentes, de los valores pasados en la
consulta o de las combinaciones de estos valores cuando sea apropiado.

Allí donde el mecanismo de almacenamiento lo respalde, considere la posibilidad de indexar la vista


materializada para aumentar aún más el rendimiento. La mayoría de las bases de datos relacionales
respaldan la indexación de vistas, igual que las soluciones de Big Data basadas en Apache Hadoop.

205 CAPÍTULO 6 | Catálogo de patrones


Cuándo utilizar este patrón
Este patrón es útil al:
●● Crear vistas materializadas sobre datos que sean difíciles de consultar directamente o donde
las consultas deben ser muy complejas para extraer datos almacenados de forma normalizada,
semiestructurada o no estructurada.

●● Crear vistas temporales que puedan mejorar drásticamente el rendimiento de las consultas o puedan
actuar directamente como vistas de origen u objetos de transferencia de datos para la IU, para los
informes o para las presentaciones.

●● Respaldar de forma ocasional escenarios con conexión o sin conexión en los cuales la conexión al
almacén de datos no siempre se encuentra disponible. En este caso, la vista puede almacenarse en
caché de forma local.

●● Simplificar consultas y exponer datos para la experimentación de modo que no sea necesario conocer
el formato de datos de origen. Por ejemplo, al unir diferentes tablas en una o varias bases de datos,
o bien uno o varios dominios en almacenes NoSQL y, a continuación, formatear los datos para
adaptarse a su uso final.

●● Proporcionar acceso a subconjuntos específicos de los datos de origen a los que, por motivos de
seguridad o privacidad, no debería tenerse acceso en general. Dichos datos tampoco deberían quedar
expuestos a su modificación ni de forma total a los usuarios.

●● Abarcar diferentes almacenes de datos para aprovechar sus funcionalidades individuales. Por ejemplo,
el uso de un almacén en el cloud que sea eficaz a la hora de escribir como almacén de datos de
referencia y una base de datos relacional que ofrezca un buen rendimiento de lectura y consulta para
guardar las vistas materializadas.

Este patrón no es útil en las siguientes situaciones:


●● Los datos de origen son sencillos y fáciles de consultar.

●● Los datos de origen cambian muy rápido o bien se puede tener acceso a ellos sin usar una vista.
En estos casos, debe evitar la sobrecarga de procesamiento de creación de vistas.

●● La prioridad de la coherencia es alta. Las vistas podrían no ser siempre totalmente coherentes con
los datos originales.

206 CAPÍTULO 6 | Catálogo de patrones


Ejemplo
En la siguiente figura se muestra un ejemplo de cómo usar el patrón de vistas materializadas para
generar un resumen de las ventas. Las tablas Data in the Order, OrderItem y Customer de particiones
independientes de una cuenta de almacenamiento de Azure se combinan para generar una vista con
el valor total de ventas de cada producto de la categoría Electrónica, junto con un recuento del número
de clientes que compraron cada artículo.

La creación de esta vista materializada requiere consultas complejas. Sin embargo, al exponer el resultado
de la consulta como vista materializada, los usuarios pueden obtener fácilmente los resultados y usarlos
directamente o incorporarlos en otra consulta. Es probable que la vista se use en un panel o sistema de
informes y se puede actualizar periódicamente (por ejemplo, semanalmente).

Aunque en este ejemplo se usa Almacenamiento de tablas de Azure, muchos sistemas de administración
de bases de datos relacionales también proporcionan soporte nativo para las vistas materializadas.

Guías y patrones relacionados


Los siguientes patrones y guías también pueden ser relevantes al implementar este patrón:
●● Introducción a la coherencia de datos. La información de resumen de una vista materializada
debe mantenerse de modo que refleje los valores de datos subyacentes. Como los valores de datos
cambian, podría no resultar práctico actualizar los datos de resumen en tiempo real y, en su lugar,
deberá adoptar un enfoque coherente con el tiempo. Resume las cuestiones relacionadas con el
mantenimiento de la coherencia en datos distribuidos y describe las ventajas y concesiones de los
diversos modelos de coherencia.

●● Patrón CQRS (Command and Query Responsibility Segregation). Úselo para actualizar la
información de una vista materializada respondiendo a eventos que se producen cuando cambian
los valores de datos subyacentes.

●● Patrón de abastecimiento de eventos. Úselo junto con el patrón CQRS para mantener la información
de una vista materializada. Cuando cambian los valores de datos en los que se basa una vista
materializada, el sistema puede generar eventos que describan estos cambios y guardarlos en un
almacén de eventos.

207 CAPÍTULO 6 | Catálogo de patrones


●● Patrón de tabla de índice. Los datos de una vista materializada se suelen organizar por una clave
principal, pero las consultas podrían necesitar recuperar información de esta vista examinando los
datos de otros campos. Úselo para crear índices secundarios sobre conjuntos de datos para almacenes
de datos que no admiten índices secundarios nativos.

Patrón de canalizaciones y filtros


Descomponga una tarea que realice un procesamiento complejo en una serie de elementos separados que
puedan reutilizarse. Esto puede mejorar el rendimiento, la escalabilidad y la reutilización permitiendo a los
elementos de tarea responsables del procesamiento implementarse y escalarse de forma independiente.

Contexto y problema
Es necesaria una aplicación para realizar una variedad de tareas de complejidad diversa sobre la
información que procesa. Un enfoque sencillo pero inflexible para implementar una aplicación consiste
en realizar este procesamiento como un módulo monolítico. Sin embargo, es probable que este enfoque
disminuya las oportunidades de refactorizar el código, optimizarlo o reutilizarlo si se requieren partes del
mismo procesamiento en cualquier otro lugar de la aplicación.

En la figura se ilustran los problemas con el procesamiento de datos mediante el enfoque monolítico. Una
aplicación recibe y procesa datos de dos fuentes. Un módulo independiente procesa los datos de cada
fuente. Este realiza una serie de tareas para transformar estos datos, antes de pasar el resultado a la lógica
empresarial de la aplicación.

208 CAPÍTULO 6 | Catálogo de patrones


Algunas de las tareas realizadas por los módulos monolíticos son muy similares a nivel funcional,
pero los módulos se han diseñado de forma independiente. El código que implementa las tareas está
estrechamente emparejado en un módulo y se ha desarrollado con poca o ninguna consideración a su
reutilización o escalabilidad.

Sin embargo, las tareas de procesamiento realizadas por cada módulo o los requisitos de implementación
de cada tarea podrían cambian a medida que se actualizan los requisitos empresariales. Algunas tareas
podrían consumir muchos recursos informáticos y podrían beneficiarse de su ejecución en hardware eficaz,
mientras que otras podrían no requerir unos recursos tan caros. Además, podría requerirse procesamiento
adicional en el futuro o podría cambiar el orden en que el procesamiento realizó las tareas. Es necesaria
una solución que resuelva estos problemas y aumente las posibilidades de reutilización del código.

Solución
Derribe el procesamiento requerido para cada flujo en un conjunto de componentes (o filtros)
independientes, cada uno de los cuales realiza una sola tarea. Al estandarizar el formato de los datos que
cada componente recibe y envía, estos filtros se pueden combinar juntos en una canalización. Esto ayuda
a evitar la duplicación del código y facilita la eliminación, la sustitución o la integración de componentes
adicionales si cambian los requisitos de procesamiento. En la siguiente figura se muestra una solución
implementada mediante canalizaciones y filtros.

El tiempo que tarda en procesarse una sola solicitud depende de la velocidad del filtro más lento de
la canalización. Uno o varios filtros podrían ser un cuello de botella, especialmente si aparece un gran
número de solicitudes en un flujo de un determinado origen de datos. Una ventaja clave de la estructura
de la canalización es que ofrece oportunidades para ejecutar instancias paralelas de filtros lentos, lo que
permite al sistema repartir la carga y mejorar el rendimiento.

Los filtros que constituyen una canalización pueden ejecutarse en diferentes máquinas, lo que les permite
escalarse de forma independiente y aprovechar la elasticidad proporcionada por muchos entornos
de cloud. Un filtro que hace un uso intensivo de computación puede ejecutarse en hardware de alto
rendimiento, mientras que otros filtros menos exigentes se pueden hospedar en hardware básico menos
caro. Incluso no es necesario que los filtros estén en el mismo centro de datos o ubicación geográfica,
lo que permite a cada uno de los elementos de una canalización ejecutarse en un entorno cercano a los
recursos que requieren. En la siguiente figura se muestra un ejemplo aplicado a la canalización de los
datos de la primera fuente.

209 CAPÍTULO 6 | Catálogo de patrones


Si la entrada y la salida de un filtro se estructuran como un flujo, es posible realizar el procesamiento de cada
filtro en paralelo. El primer filtro de la canalización puede iniciar su trabajo y generar sus resultados, que se
pasan directamente al siguiente filtro de la secuencia antes de que el primer filtro haya completado su trabajo.

Otra ventaja es la resiliencia que este modelo puede proporcionar. Si se produce un error en un filtro o
la máquina en la que se ejecuta este ya no está disponible, la canalización puede volver a programar el
trabajo realizado por el filtro y dirigir este trabajo a otra instancia del componente. Si se produce un error
en un solo filtro, esto no significa necesariamente que se vaya a producir otro error en toda la canalización.

Usar el patrón de canalizaciones y filtros junto con el patrón de transacciones de compensación es un


enfoque alternativo para implementar las transacciones distribuidas. Una transacción distribuida se puede
dividir en tareas independientes y compensables. Cada una de ellas puede implementarse mediante un
filtro que también se encarga de implementar el patrón de transacciones de compensación. Los filtros de
una canalización se pueden implementar como tareas hospedadas independientes que se ejecutan casi al
límite de los datos que mantienen.

Problemas y consideraciones

Debe considerar los puntos siguientes a la hora de decidir cómo implementar este patrón:
●● Complejidad. La mayor flexibilidad que proporciona este patrón también puede presentar
complejidad, especialmente si los filtros de una canalización se distribuyen en diversos servidores.

●● Fiabilidad. Use una infraestructura que garantice que los datos que fluyen entre los filtros de una
canalización no se perderán.

●● Idempotencia. Si se produce un error en un filtro de una canalización tras recibir un mensaje y el trabajo
se vuelve a programar para otra instancia del filtro, parte del trabajo podría haberse completado ya. Si
este trabajo actualiza algún aspecto del estado global (como la información almacenada en una base de
datos), la misma actualización podría repetirse. Un problema similar podría producirse si se produce un
error en un filtro después de publicar sus resultados en el siguiente filtro de la canalización, pero antes
de indicar que su trabajo se ha completado correctamente. En estos casos, otra instancia del filtro podría
repetir el mismo trabajo, dando lugar a la doble publicación de los mismos resultados. Esto podría
ocasionar el doble procesamiento de los mismos datos por parte de filtros posteriores de la canalización.
Por lo tanto, los filtros de una canalización deben diseñarse para ser idempotentes. Para obtener más
información, consulte los patrones de idempotencia en el blog de Jonathan Oliver.

●● Mensajes repetidos. Si se produce un error en un filtro de una canalización después de publicar un


mensaje en la siguiente etapa de la canalización, podría ejecutarse otra instancia del filtro y publicará
una copia del mismo mensaje en la canalización. Esto podría dar lugar a que dos instancias del mismo
mensaje se pasen al siguiente filtro. Para evitar esto, la canalización debe detectar y eliminar los
mensajes duplicados.

210 CAPÍTULO 6 | Catálogo de patrones


Si implementa la canalización mediante colas de mensajes (como las colas de Microsoft Azure
Service Bus), la infraestructura de colas de mensajes podría proporcionar la detección y eliminación
automáticas de los mensajes duplicados.

●● Contexto y estado. En una canalización, cada filtro se ejecuta esencialmente de forma aislada
y no debe realizar ninguna suposición sobre su invocación. Esto significa que cada filtro debe
proporcionarse con contexto suficiente para realizar su trabajo. Este contexto podría incluir gran
cantidad de información de estado.

Cuándo utilizar este patrón


Puede utilizar este patrón en los siguientes casos:
●● El procesamiento requerido por una aplicación se puede dividir fácilmente en un conjunto de pasos
independientes.

●● Los pasos de procesamiento realizados por una aplicación tienen diferentes requisitos de
escalabilidad. Los filtros que deben aplicarse en el mismo proceso pueden agruparse. Para obtener
más información, consulte el patrón de consolidación de recursos de proceso.

●● Se requiere flexibilidad para permitir la reordenación de los pasos de procesamiento realizados por
una aplicación o la posibilidad de agregar y quitar pasos.

●● El sistema puede beneficiarse de la distribución del procesamiento para los pasos correspondientes
a los distintos servidores.

●● Es necesaria una solución fiable que minimice los efectos del error en un paso mientras se procesan
los datos.

Este patrón podría no ser útil cuando:


●● Los pasos de procesamiento realizados por una aplicación no son independientes o deben realizarse
juntos como parte de la misma transacción.

●● La cantidad de información de contexto o estado requerida por un paso hace que este enfoque
resulte ineficaz. Podría ser posible la persistencia de información de estado en una base de datos
en su lugar, pero no use esta estrategia si la carga adicional de la base de datos da lugar a una
contención excesiva.

Ejemplo
Puede usar una secuencia de colas de mensajes para proporcionar la infraestructura requerida para
implementar una canalización. Una cola de mensajes inicial recibe mensajes sin procesar. Un componente
implementado como una tarea de filtro atiende un mensaje de esta cola, realiza su trabajo y,
a continuación, publica el mensaje transformado en la siguiente cola de la secuencia. Otra tarea de filtro
puede atender los mensajes de esta cola, procesarlos, publicar los resultados en otra cola, etc., hasta que
los datos completamente transformados aparezcan en el mensaje final de la cola. En la siguiente figura se
ilustra la implementación de una canalización mediante las colas de mensajes.

211 CAPÍTULO 6 | Catálogo de patrones


Si crea una solución en Azure, puede usar las colas de Service Bus para proporcionar un mecanismo de
colas fiable y escalable. La clase ServiceBusPipeFilter que aparece a continuación en C# muestra cómo
puede implementar un filtro que recibe mensajes de entrada de una cola, procesa estos mensajes
y publica los resultados en otra cola.

La clase ServiceBusPipeFilter se define en el proyecto PipesAndFilters.Shared disponible en GitHub.

public class ServiceBusPipeFilter


{
...
private readonly string inQueuePath;
private readonly string outQueuePath;
...
private QueueClient inQueue;
private QueueClient outQueue;
...

public ServiceBusPipeFilter(..., string inQueuePath, string outQueuePath = null)


{
...
this.inQueuePath = inQueuePath;
this.outQueuePath = outQueuePath;
}

public void Start()


{
...
// Create the outbound filter queue if it doesn’t exist.
...
this.outQueue = QueueClient.CreateFromConnectionString(...);

...
// Create the inbound and outbound queue clients.
this.inQueue = QueueClient.CreateFromConnectionString(...);
}

public void OnPipeFilterMessageAsync(


Func<BrokeredMessage, Task<BrokeredMessage>> asyncFilterTask, ...)
{
...

this.inQueue.OnMessageAsync(
async (msg) =>
{
...
// Process the filter and send the output to the
// next queue in the pipeline.
var outMessage = await asyncFilterTask(msg);

// Send the message from the filter processor


// to the next queue in the pipeline.
if (outQueue != null)
{
await outQueue.SendAsync(outMessage);
}

// Note: There’s a chance that the same message could be sent twice
// or that a message gets processed by an upstream or downstream
// filter at the same time.
// This would happen in a situation where processing of a message was
// completed, it was sent to the next pipe/queue, and then failed
// to complete when using the PeekLock method.
// Idempotent message processing and concurrency should be considered
// in a real-world implementation.
},
options);
}

public async Task Close(TimeSpan timespan)


{
// Pause the processing threads.
this.pauseProcessingEvent.Reset();

// There’s no clean approach for waiting for the threads to complete


// the processing. This example simply stops any new processing, waits
// for the existing thread to complete, then closes the message pump
// and finally returns.
s Thread.Sleep(timespan);

this.inQueue.Close();
...
}

212 CAPÍTULO 6 | Catálogo de patrones


El método Start de la clase ServiceBusPipeFilter se conecta a un par de colas de entrada y salida, mientras
que el método Close se desconecta de la cola de entrada. El método OnPipeFilterMessageAsync realiza el
procesamiento real de mensajes, mientras que el parámetro asyncFilterTask de este método especifica el
procesamiento que se va a realizar. El método OnPipeFilterMessageAsync espera los mensajes entrantes
de la cola de entrada, ejecuta el código especificado por el parámetro asyncFilterTask sobre cada mensaje
conforme llega y publica los resultados en la cola de salida. El constructor especifica las propias colas.

La solución de ejemplo implementa filtros en un conjunto de roles de trabajo. Cada rol de trabajo se
puede escalar de forma independiente, dependiendo de la complejidad del procesamiento empresarial
que realice o los recursos requeridos para el procesamiento. Además, varias instancias de cada rol de
trabajo se pueden ejecutar en paralelo para mejorar el rendimiento.

En el siguiente código se muestra un rol de trabajo de Azure denominado PipeFilterARoleEntry, definido


en el proyecto PipeFilterA de la solución de ejemplo.

public class PipeFilterARoleEntry : RoleEntryPoint


{
...
private ServiceBusPipeFilter pipeFilterA;

public override bool OnStart()


{
...
this.pipeFilterA = new ServiceBusPipeFilter(
...,
Constants.QueueAPath,
Constants.QueueBPath);

this.pipeFilterA.Start();
...
}

public override void Run()


{
this.pipeFilterA.OnPipeFilterMessageAsync(async (msg) =>
{
// Clone the message and update it.
// Properties set by the broker (Deliver count, enqueue time, ...)
// aren’t cloned and must be copied over if required.
var newMsg = msg.Clone();

await Task.Delay(500); // DOING WORK

Trace.TraceInformation(“Filter A processed message:{0} at {1}”,


msg.MessageId, DateTime.UtcNow);

newMsg.Properties.Add(Constants.FilterAMessageKey, “Complete”);

return newMsg;
});

...
}

...
}

Este rol contiene un objeto ServiceBusPipeFilter. El método OnStart del rol se conecta a las colas para
recibir mensajes de entrada y publicar mensajes de salida (los nombres de las colas se definen en la clase
Constants). El método Run invoca el método OnPipeFilterMessagesAsync para procesar cada mensaje
recibido (en este ejemplo, el procesamiento se simula esperando durante un breve período de tiempo).
Al completarse el procesamiento, se crea un nuevo mensaje con los resultados (en este caso, el mensaje
de entrada tiene una propiedad personalizada agregada), el cual se publica en la cola de salida.

213 CAPÍTULO 6 | Catálogo de patrones


El código de ejemplo contiene otro rol de trabajo denominado PipeFilterBRoleEntry en el proyecto
PipeFilterB. Este rol es similar a PipeFilterARoleEntry, con la excepción de que realiza un procesamiento
diferente en el método Run. En la solución de ejemplo, estos dos roles se combinan para crear una
canalización. La cola de salida de rol PipeFilterARoleEntry es la cola de entrada del rol PipeFilterBRoleEntry.

La solución de ejemplo también proporciona dos roles adicionales denominados InitialSenderRoleEntry


(en el proyecto InitialSender) y FinalReceiverRoleEntry (en el proyecto FinalReceiver). El rol
InitialSenderRoleEntry proporciona el mensaje inicial de la canalización. El método OnStart se conecta a
una sola cola y el método Run publica un método en esta cola. Esta cola es la cola de entrada usada por el
rol PipeFilterARoleEntry, de modo que si se publica un mensaje en ella, el rol PipeFilterARoleEntry recibirá
y procesará el mensaje. A continuación, el mensaje procesado pasa a través del rol PipeFilterBRoleEntry.

La cola de entrada del rol FinalReceiveRoleEntry es la cola de salida del rol PipeFilterBRoleEntry. El método
Run del rol FinalReceiveRoleEntry, mostrado a continuación, recibe el mensaje y realiza una parte del
procesamiento final. A continuación, escribe los valores de las propiedades personalizadas agregadas por
los filtros de la canalización a la salida de seguimiento.

public class FinalReceiverRoleEntry : RoleEntryPoint


{
...
// Final queue/pipe in the pipeline to process data from.
private ServiceBusPipeFilter queueFinal;

public override bool OnStart()


{
...
// Set up the queue.
this.queueFinal = new ServiceBusPipeFilter(...,Constants.QueueFinalPath);
this.queueFinal.Start();
...
}

public override void Run()


{
this.queueFinal.OnPipeFilterMessageAsync(
async (msg) =>
{
await Task.Delay(500); // DOING WORK

// The pipeline message was received.


Trace.TraceInformation(
“Pipeline Message Complete - FilterA:{0} FilterB:{1}”,
msg.Properties[Constants.FilterAMessageKey],
msg.Properties[Constants.FilterBMessageKey]);

return null;
});
...
}

...
}

Guías y patrones relacionados


Los siguientes patrones y guías también pueden ser relevantes al implementar este patrón:
●● En GitHub se ofrece un ejemplo donde se ilustra cómo utilizar este patrón.

●● Patrón de consumidores competitivos. Una canalización puede contener varias instancias de uno
o varios filtros. Este enfoque resulta útil para ejecutar instancias paralelas de filtros lentos, lo que
permite al sistema repartir la carga y mejorar el rendimiento. Cada instancia de un filtro competirá por
encontrar información con las otras instancias. Dos instancias de un filtro no deben poder procesar los
mismos datos. Proporciona una explicación de este enfoque.

214 CAPÍTULO 6 | Catálogo de patrones


●● Patrón de consolidación de recursos de proceso. Los filtros que deben aplicarse en el mismo
proceso pueden agruparse. Proporciona más información sobre los beneficios y las ventajas de esta
estrategia.

●● Patrón de transacciones de compensación. Un filtro puede implementarse como operación


reversible o que cuenta con una operación compensatoria que restaura el estado a una versión
anterior, en caso de error. Explica cómo implementarlo para mantener la coherencia o llegar a
conseguirla.

●● Patrones de idempotencia en el blog de Jonathan Oliver.

Patrón de colas de prioridad


Prioriza las solicitudes enviadas a los servicios de modo que las solicitudes con una prioridad más alta
se reciban y procesen más rápidamente que las que tienen menor prioridad. Este patrón es útil para las
aplicaciones que ofrecen distintas garantías de nivel de servicio a clientes individuales.

Contexto y problema
Las aplicaciones pueden delegar tareas específicas a otros servicios como, por ejemplo, procesar en
segundo plano o integrarse en otras aplicaciones o servicios. En el cloud, suele usarse una cola de
mensajes para delegar las tareas al procesamiento en segundo plano. En muchos casos, el orden en que
un servicio recibe las solicitudes no es importante. Sin embargo, en otros, es necesario priorizar solicitudes
concretas que deben procesarse antes que otras con prioridad más baja, enviadas previamente por la
aplicación.

Solución
Una cola suele ser una estructura de tipo "primero en entrar, primero en salir" (FIFO) y los consumidores
reciben los mensajes en el mismo orden en el que se enviaron a esta. Sin embargo, algunas colas admiten
el envío de mensajes con prioridad. La aplicación que envía el mensaje puede asignar una prioridad y
los mensajes de la cola se reordenan automáticamente, de forma que aquellos con prioridad más alta
se reciban antes que los de prioridad más baja. En la ilustración se muestra una cola de mensajes con
prioridad.

215 CAPÍTULO 6 | Catálogo de patrones


La mayoría de las implementaciones de colas de mensajes admiten varios consumidores (de acuerdo con
el patrón de consumidores paralelos) y el número de procesos de consumidor puede incrementarse o
reducirse, en función de la demanda.

Una solución alternativa para los sistemas que no admiten las colas de mensajes basadas en prioridad
es contar con una cola independiente para cada prioridad. La aplicación es responsable de enviar los
mensajes a la cola adecuada y cada cola puede tener un grupo de consumidores distinto. Las colas de
mayor prioridad pueden tener un grupo de consumidores más grande y ejecutarse en un hardware más
rápido que las de prioridad más baja. En la ilustración siguiente se muestra el uso de colas de mensajes
independientes para cada prioridad.

Una variación de esta estrategia es tener un único grupo de consumidores que compruebe primero los
mensajes de las colas de prioridad alta y que, solo después de haberlo hecho, comience a recuperar los
mensajes de las colas de menor prioridad. Existen algunas diferencias semánticas entre una solución
que usa un único grupo de procesos de consumidor (ya sea con una cola única que admite mensajes
con distintas prioridades o con varias colas que administran por separado los mensajes de prioridades
diferentes) y una solución que usa varias colas con un grupo independiente para cada una.

En el enfoque de un solo grupo, los mensajes de prioridad más alta siempre se reciben y se procesan
antes que los de menor prioridad. En teoría, los mensajes con una prioridad muy baja podrían relevarse
continuamente y no llegar a procesarse jamás. En el enfoque de varios grupos, los mensajes de prioridad
más baja siempre se procesarán, solo que no tan rápido como los de mayor prioridad (dependiendo del
tamaño relativo de los grupos y de los recursos que tienen disponibles).

El uso de un mecanismo de puesta en cola por prioridad puede proporcionar las ventajas siguientes:

●● Permite a las aplicaciones satisfacer requisitos de negocio que requieren el establecimiento de


prioridades de disponibilidad o rendimiento, por ejemplo, ofrecer distintos niveles de servicio a
grupos de clientes específicos.

216 CAPÍTULO 6 | Catálogo de patrones


●● Puede ayudar a minimizar los costes operativos. En el enfoque de cola única, se puede reducir el
número de consumidores, si es necesario. Los mensajes de prioridad alta se seguirán procesando en
primer lugar (aunque posiblemente con mayor lentitud) y los de prioridad más baja pueden retrasarse
durante más tiempo. Si se ha implementado el enfoque de varias colas de mensajes con grupos
de consumidores independientes para cada una, puede reducir el grupo de consumidores para las
colas de prioridad más baja o incluso suspender el procesamiento de ciertas colas de prioridad muy
reducida al detener a todos los consumidores que reciben mensajes de estas.

●● El enfoque de varias colas de mensajes puede ayudar a maximizar el rendimiento y la escalabilidad de


la aplicación al crear particiones de los mensajes en función de los requisitos de procesamiento. Por
ejemplo, puede darse prioridad a las tareas vitales para que se administren en receptores de ejecución
inmediata, mientras que las tareas en segundo plano y menos importantes pueden delegarse a
receptores programados para ejecutarse en períodos de menos actividad.

Problemas y consideraciones
Tenga en cuenta los puntos siguientes a la hora de decidir cómo implementar este patrón:
Defina las prioridades en el contexto de la solución. Por ejemplo, una prioridad alta puede significar que
los mensajes deben procesarse en un plazo de diez segundos. Identifique los requisitos para administrar
los elementos con una prioridad alta, así como el resto de recursos que debe asignarse para satisfacer
estos criterios.

Decida si todos los elementos de prioridad alta deben procesarse antes que cualquier otro de prioridad
más baja. Si del procesamiento de los mensajes se encarga un único grupo de consumidores, debe
proporcionarse un mecanismo que pueda anticiparse y suspender una tarea encargada de un mensaje
de prioridad baja si un mensaje de mayor prioridad pasa a estar disponible.

En el enfoque de varias colas, cuando se usa un grupo de procesos de consumidor único que escucha
en todas las colas en lugar de un grupo de consumidores dedicado para cada cola, el consumidor debe
aplicar un algoritmo que garantice que siempre se atienden los mensajes de las colas de mayor prioridad
antes que los de las de prioridad más baja.

Supervise la velocidad de procesamiento en las colas de prioridad alta y baja para asegurarse de que los
mensajes que contienen se procesan a la velocidad esperada.

Si debe garantizar el procesamiento de los mensajes de prioridad baja, es necesario aplicar el enfoque
de varias colas de mensajes con varios grupos de consumidores. Igualmente, en una cola que admite la
priorización de los mensajes, se puede incrementar dinámicamente la prioridad de un mensaje puesto
en cola a medida que pasa el tiempo. Sin embargo, este enfoque depende de la cola de mensajes que
proporcione esta característica.

El uso de una cola independiente para cada prioridad de mensaje funciona mejor para los sistemas que
tienen un número reducido de prioridades bien definidas.

El sistema puede determinar las prioridades de los mensajes de forma lógica. Por ejemplo, en lugar de
tener mensajes explícitos de prioridad alta y baja, podrían designarse como "cliente que paga tarifas"
o "cliente que no paga tarifas". En función de modelo de negocio, el sistema puede asignar más recursos
al procesamiento de los mensajes de clientes que pagan tarifas frente a los de aquellos que no pagan.

La comprobación de un mensaje en la cola puede conllevar un coste económico y de procesamiento


(algunos sistemas de mensajería comercial cobran una pequeña cuota cada vez que un mensaje se envía
o se recupera, así como cada vez que se consultan los mensajes de una cola). Este coste aumenta al
comprobar varias colas.

El tamaño de un grupo de consumidores se puede ajustar dinámicamente en función de la longitud


de la cola del grupo. Para obtener más información, consulte la Guía de escalado automático.

217 CAPÍTULO 6 | Catálogo de patrones


Cuándo utilizar este patrón

Este patrón resulta útil en escenarios en los que:


●● El sistema debe administrar varias tareas con distintas prioridades.
●● Deben aplicarse prioridades diferentes a distintos usuarios o inquilinos.

Ejemplo
Microsoft Azure no ofrece un mecanismo de puesta en cola que admita de forma nativa la priorización
automática de los mensajes a través de su clasificación. Sin embargo, proporciona suscripciones y temas
de Azure Service Bus que admiten un mecanismo de puesta en cola con filtrado de mensajes, junto con
una amplia gama de funciones de gran flexibilidad que lo convierten en ideal para su uso en la mayoría
de las implementaciones de colas de prioridad.

Una solución de Azure puede implementar un tema de Service Bus al que una aplicación puede enviar
mensajes, de la misma forma que una cola. Los mensajes pueden contener metadatos en forma de
propiedades personalizadas definidas por la aplicación. Las suscripciones a Service Bus se pueden asociar
al tema y filtrar los mensajes en función de sus propiedades. Cuando una aplicación envía un mensaje a un
tema, dicho mensaje se dirige a la suscripción adecuada donde pueda leerlo un consumidor. Los procesos
de consumidor pueden recuperar mensajes de una suscripción con la misma semántica que una cola de
mensajes (una suscripción es una cola lógica). En la ilustración siguiente se muestra la implementación de
una cola de prioridades con suscripciones y temas de Azure Service Bus.

218 CAPÍTULO 6 | Catálogo de patrones


En la ilustración anterior, la aplicación crea varios mensajes y asigna a cada uno una propiedad
personalizada que se llama Priority con el valor High o Low. La aplicación envía estos mensajes a un tema.
El tema tiene dos suscripciones asociadas que examinan la propiedad Priority para filtrar los mensajes.
Una suscripción acepta los mensajes en los que la propiedad Priority está establecida en High y la otra
acepta aquellos en los que se establece como Low. Un grupo de consumidores lee los mensajes de cada
suscripción. El grupo de la suscripción de prioridad alta es más grande y puede que estos consumidores
se ejecuten equipos más potentes con más recursos disponibles que los consumidores del grupo de
prioridad baja.

Tenga en cuenta que no hay nada especial en la designación de los mensajes de prioridad alta y baja en
este ejemplo. Son simples etiquetas que se especifican como propiedades en cada mensaje y se usan para
dirigir los mensajes a una suscripción concreta. Si se requieren prioridades adicionales, es relativamente
fácil crear más suscripciones y grupos de procesos de consumidor para administrarlas.

La solución PriorityQueue disponible en GitHub contiene una implementación de este enfoque. Esta
solución contiene dos proyectos de roles de trabajo llamados PriorityQueue.High y PriorityQueue.
Low. Dichos roles heredan de la clase PriorityWorkerRole, que contiene la funcionalidad necesaria para
conectarse a una suscripción especificada en el método OnStart.
Los roles de trabajo PriorityQueue.High y PriorityQueue.Low se conectan a suscripciones distintas,
definidas en las opciones de configuración. Un administrador puede configurar que se ejecute un número
distinto de instancias de cada rol. Normalmente habrá más instancias del rol de trabajo PriorityQueue.High
que de PriorityQueue.Low.

El método Run de la clase PriorityWorkerRole establece que el método ProcessMessage virtual (también
definido en la clase PriorityWorkerRole) debe ejecutarse para cada mensaje que se reciba en la cola.
El código siguiente muestra los métodos Run y ProcessMessage. La clase QueueManager, que se define
en el proyecto PriorityQueue.Shared, proporciona métodos auxiliares para usar colas de Azure Service Bus.

public class PriorityWorkerRole : RoleEntryPoint


{
private QueueManager queueManager;
...

public override void Run()


{
// Empezar a escuchar mensajes en la suscripción.
var subscriptionName = CloudConfigurationManager.GetSetting(“NombreSuscripción”);
this.queueManager.ReceiveMessages(subscriptionName, this.ProcessMessage);
...;
}
...

protected virtual async Task ProcessMessage(BrokeredMessage message)


{
// Simulación de proceso.
await Task.Delay(TimeSpan.FromSeconds(2));
}
}

Ambos roles de trabajo, PriorityQueue.High y PriorityQueue.Low, invalidan la funcionalidad


predeterminada del método ProcessMessage. El código siguiente muestra el método ProcessMessage
para el rol de trabajo PriorityQueue.High.

219 CAPÍTULO 6 | Catálogo de patrones


protected override async Task ProcessMessage(BrokeredMessage message)
{
// Simular el procesamiento de mensajes de prioridad alta.
await base.ProcessMessage(message);
Trace.TraceInformation(“Mensaje de prioridad alta procesado por “ +
RoleEnvironment.CurrentRoleInstance.Id + “ MessageId: “ + message.MessageId);
}

Cuando una aplicación envía mensajes al tema asociado a las suscripciones que usan los roles de trabajo
PriorityQueue.High y PriorityQueue.Low, especifica la prioridad mediante la propiedad personalizada
Priority, como se muestra en el ejemplo de código siguiente. Este código (que se implementa en la clase
WorkerRole en el proyecto PriorityQueue.Sender) usa el método auxiliar SendBatchAsync de la clase
QueueManager para enviar mensajes a un tema por lotes.

// Enviar un lote de prioridad baja.


var lowMessages = new List<BrokeredMessage>();

for (int i = 0; i < 10; i++)


{
var message = new BrokeredMessage() { MessageId = Guid.NewGuid().ToString() };
message.Properties[“Priority”] = Priority.Low;
lowMessages.Add(message);
}

this.queueManager.SendBatchAsync(lowMessages).Wait();
...

// Enviar un lote de prioridad alta.


var highMessages = new List<BrokeredMessage>();

for (int i = 0; i < 10; i++)


{
var message = new BrokeredMessage() { MessageId = Guid.NewGuid().ToString() };
message.Properties[“Priority”] = Priority.High;
highMessages.Add(message);
}

this.queueManager.SendBatchAsync(highMessages).Wait();

Guías y patrones relacionados


Los siguientes patrones y guías también pueden ser relevantes al implementar este patrón:
●● En GitHub se ofrece un ejemplo donde se ilustra cómo utilizar este patrón.

●● Introducción a la mensajería asincrónica. Es posible que un servicio de consumidor que procesa una
solicitud necesite enviar una respuesta a la instancia de la aplicación que envió la solicitud. Proporciona
información sobre las estrategias que se pueden usar para implementar mensajes de solicitud o respuesta.

●● Patrón de consumidores competitivos. Para incrementar el rendimiento de las colas, es posible


hacer que varios consumidores escuchen en la misma cola y procesar las tareas en paralelo. Estos
consumidores competirán por los mensajes, pero solo uno será capaz de procesar cada mensaje.
Proporciona más información sobre los beneficios y las ventajas de implementar este enfoque.

●● Patrón de limitación. Puede implementar la limitación mediante el uso de colas. Se pueden usar
mensajes con prioridad para asegurarse de que se da prioridad a las solicitudes de aplicaciones

220 CAPÍTULO 6 | Catálogo de patrones


críticas o de aplicaciones que ejecutan clientes de alto valor, frente a las solicitudes de aplicaciones
menos importantes.

●● Guía de escalado automático. Se puede escalar el tamaño del grupo de procesos de consumidor que
administra una cola en función de la longitud de dicha cola. Esta estrategia puede ayudar a mejorar el
rendimiento, sobre todo en el caso de los grupos que administran mensajes de prioridad alta.

●● Patrones de integración empresarial. con Service Bus en el blog de Abhishek Lal.

Patrón de nivelación de carga basada en cola


Use una cola que actúe como búfer entre una tarea y un servicio que esta invoca para suavizar las cargas
elevadas intermitentes que pueden provocar un error del servicio o que se agote el tiempo de espera
de la tarea. Esto puede ayudar a minimizar el impacto de los picos de demanda en la disponibilidad
y la capacidad de respuesta, tanto de la tarea como del servicio.

Contexto y problema
Muchas soluciones en el cloud implican la ejecución de tareas que invocan servicios. En este entorno,
si un servicio se somete a cargas elevadas intermitentes, puede causar problemas de rendimiento o de
fiabilidad.

Un servicio puede formar parte de la misma solución que las tareas que lo usan o bien ser un servicio de
terceros que proporciona acceso a recursos de uso frecuente, como una memoria caché o un servicio de
almacenamiento. Si varias tareas simultáneas usan el mismo servicio, puede ser difícil predecir el volumen
de solicitudes para el servicio en cualquier momento.

Un servicio puede experimentar picos de demanda que hacen que se sobrecargue y no pueda responder a
las solicitudes de manera puntual. Asimismo, si un servicio se desborda con un número elevado de solicitudes
simultáneas, puede generar un error al no poder administrar la contención causada por las solicitudes.

Solución
Refactorice la solución e inserte una cola entre la tarea y el servicio. La tarea y el servicio se ejecutan
de forma asincrónica. La tarea envía un mensaje con los datos requeridos por el servicio a una cola. La
cola actúa como búfer y almacena el mensaje hasta que el servicio lo recupera. El servicio recupera los
mensajes de la cola y los procesa. Las solicitudes de diversas tareas, que pueden generarse a un ritmo muy
variable, pueden pasarse al servicio a través de la misma cola de mensajes. En esta ilustración se muestra
el uso de una cola para nivelar la carga en un servicio.

221 CAPÍTULO 6 | Catálogo de patrones


Esta cola desacopla las tareas del servicio, lo que permite a este último administrar los mensajes a su
propio ritmo, independientemente del volumen de solicitudes de las tareas simultáneas. Además, las
tareas no se retrasan si el servicio no está disponible en el momento de enviar un mensaje a la cola.

Este patrón ofrece las ventajas siguientes:

●● Ayuda a maximizar la disponibilidad, ya que los posibles retrasos de los servicios no tienen un efecto
inmediato y directo en la aplicación, que puede seguir enviando mensajes a la cola incluso cuando el
servicio no está disponible o no procesa los mensajes.

●● Ayuda a maximizar la escalabilidad, ya que se puede modificar tanto el número de colas como de
servicios para satisfacer la demanda.

●● Ayuda a controlar los costes, ya que el número de instancias del servicio implementadas tiene que ser
suficiente para satisfacer únicamente la carga media, no la carga máxima.

Algunos servicios implementan la limitación cuando la demanda alcanza un umbral que, en caso de
traspasarse, podría generar un error del sistema. La limitación puede reducir la funcionalidad disponible.
Para asegurarse de no alcanzar dicho umbral, puede implementar la nivelación de carga con estos servicios.

Problemas y consideraciones
Tenga en cuenta los puntos siguientes a la hora de decidir cómo implementar este patrón:
●● Debe implementarse lógica de aplicación que controle el ritmo al que los servicios administran
los mensajes, para evitar el desbordamiento del recurso de destino. Evite pasar picos de demanda
inesperados a la etapa siguiente del sistema. Pruebe el sistema en carga para asegurarse de que
proporciona la nivelación necesaria y, para ello, ajuste el número de colas y de instancias del servicio
que administran los mensajes.

●● Las colas de mensajes son un mecanismo de comunicación unidireccional. Si una tarea espera
respuesta de un servicio, puede que sea necesario implementar un mecanismo que el servicio pueda
usar para este fin. Para obtener más información, consulte Introducción a la mensajería asincrónica.

●● Tenga cuidado si aplica el escalado automático a los servicios que reciben las solicitudes en la cola.
Esto puede causar una mayor contención de los recursos compartidos por estos servicios y reducir
la eficacia del uso de la cola para nivelar la carga.

Cuándo utilizar este patrón


Este patrón es útil para cualquier aplicación que use servicios sometidos a sobrecarga.

Sin embargo, no lo es si la aplicación espera una respuesta del servicio con una latencia mínima.

Ejemplo
Un rol web de Microsoft Azure almacena datos mediante un servicio de almacenamiento independiente.
Si se ejecuta un gran número de instancias del rol web de forma simultánea, es posible que el servicio de
almacenamiento no pueda responder a las solicitudes con la rapidez suficiente para evitar que se agote el
tiempo de espera o se genere un error. En esta ilustración se resalta un servicio que se satura debido a un
número elevado de solicitudes simultáneas de instancias de un rol web.

222 CAPÍTULO 6 | Catálogo de patrones


Para resolverlo, puede usar una cola a fin de nivelar la carga entre las instancias del rol web y el servicio
de almacenamiento. Sin embargo, el servicio de almacenamiento está diseñado para aceptar solicitudes
sincrónicas y no puede modificarse fácilmente para leer mensajes y administrar el rendimiento. Puede
introducir un rol de trabajo que actúe como servicio de proxy que recibe las solicitudes de la cola y
las reenvía al servicio de almacenamiento. La lógica de aplicación del rol de trabajo puede controlar
la velocidad a la que este pasa las solicitudes al servicio de almacenamiento, a fin de evitar que dicho
servicio se sature. En la ilustración se muestra el uso de una cola y un rol de trabajo para nivelar la carga
entre las instancias del rol web y el servicio.

223 CAPÍTULO 6 | Catálogo de patrones


Guías y patrones relacionados
Los siguientes patrones y guías también pueden ser relevantes al implementar este patrón:
●● Introducción a la mensajería asincrónica. Las colas de mensajes son asincrónicas de forma intrínseca.
Puede que sea necesario rediseñar la lógica de aplicación de una tarea si se adapta de la comunicación
directa con un servicio al uso de una cola de mensajes. Del mismo modo, también puede ser necesario
refactorizar un servicio para aceptar solicitudes de una cola de mensajes. Como alternativa, puede que
sea posible implementar un servicio de proxy, tal y como se describe en el ejemplo.

●● Patrón de consumidores competitivos. Se pueden ejecutar varias instancias de un servicio y que


cada una actúe como consumidor de mensajes de la cola de nivelación de carga. Use este enfoque
para ajustar la velocidad a la que los mensajes se reciben y se pasan a un servicio.

●● Patrón de limitación. Una forma sencilla de implementar la limitación en un servicio es usar la


nivelación de carga mediante colas y enrutar todas las solicitudes a un servicio a través de una cola
de mensajes. El servicio puede procesar las solicitudes a una velocidad que garantice que los recursos
requeridos por el servicio no se agoten y destinada a reducir el nivel de contención que puede
producirse.

●● Conceptos de Queue service. Información acerca de cómo elegir un mecanismo de mensajería


y puesta en cola en las aplicaciones de Azure.

Patrón de reintento
Permita que una aplicación pueda controlar los errores transitorios cuando intenta conectarse a un servicio
o un recurso de red mediante un reintento transparente de las operaciones con errores. Esto puede
mejorar la estabilidad de la aplicación.

Contexto y problema
Una aplicación que se comunica con elementos que se ejecutan en el cloud tiene que ser sensible a los errores
transitorios que pueden ocurrir en este entorno. Entre estos errores se incluyen la pérdida momentánea de la
conectividad de red con los componentes y los servicios, la falta temporal de disponibilidad de un servicio o el
agotamiento del tiempo de espera que se produce cuando un servicio está ocupado.

Estos errores suelen corregirse automáticamente y, si la acción que desencadenó el error se repite tras
cierto tiempo, es posible que se realice correctamente. Por ejemplo, un servicio de base de datos que
procesa un gran número de solicitudes simultáneas puede implementar una estrategia de limitación
que rechace temporalmente cualquier otra solicitud hasta que la carga de trabajo disminuya. Quizás
una aplicación que intenta acceder a la base de datos no pueda conectarse pero, si lo vuelve a intentar
transcurrido cierto tiempo, es posible que lo consiga.

Solución
En el cloud son frecuentes los errores transitorios y una aplicación debe diseñarse para administrarlos
con elegancia y transparencia. Esto minimiza los efectos que los errores pueden tener en las tareas de
negocios que la aplicación realiza.

Si una aplicación detecta un error cuando intenta enviar una solicitud a un servicio remoto, puede
administrar el error mediante las estrategias siguientes:

●● Cancelar. Si se observa que el error no es transitorio o es poco probable que la acción se realice
correctamente en caso de repetirse, la aplicación debe cancelar la operación y notificar una excepción.
Por ejemplo, un error de autenticación debido a que se proporcionaron credenciales no válidas no se
va a solucionar, independientemente del número de veces que se repita.

224 CAPÍTULO 6 | Catálogo de patrones


●● Reintentar. Si el error específico que se ha notificado es inusual o poco frecuente, puede que lo hayan
causado circunstancias inusuales, por ejemplo, que un paquete de red haya sufrido daños mientras
se estaba transmitiendo. En este caso, la aplicación puede reintentar la solicitud errónea de forma
inmediata, ya que es poco probable que se repita el mismo error y es posible que la solicitud se
realice con éxito.

●● Reintentar después de un retraso. Si el error se produce debido a un error común relacionado con la
conectividad o la falta de disponibilidad, puede que la red o el servicio necesiten un breve período de
tiempo mientras se corrigen los problemas de conectividad o se elimina el trabajo pendiente.
La aplicación debe esperar un tiempo adecuado antes de reintentar la solicitud.

Para los errores transitorios más habituales, el período entre reintentos debe elegirse de forma que
abarque las solicitudes de varias instancias de la aplicación con la mayor uniformidad posible. Esto reduce
la posibilidad de que un servicio ocupado se siga sobrecargando. Si varias instancias de una aplicación
saturan continuamente un servicio con solicitudes de reintento, el servicio tardará más tiempo en
recuperarse.

Si la solicitud sigue generando errores, la aplicación puede esperar y realizar otro intento. Si es necesario,
el proceso puede repetirse con retrasos incrementales entre los reintentos, hasta haber probado con
un número máximo de solicitudes. El retraso puede incrementarse de forma progresiva o exponencial,
en función del tipo de error y de la probabilidad de que se corrija durante ese tiempo.

En el diagrama siguiente se ilustra la invocación de una operación en un servicio hospedado con


este patrón. Si la solicitud sigue generando errores tras realizar un número de intentos predefinido,
la aplicación debe tratar el error como excepción y administrarlo en consecuencia.

La aplicación debe encapsular todos los intentos de acceso a un servicio remoto en el código
que implementa una política de reintentos que coincide con una de las estrategias mencionadas
anteriormente. Las solicitudes enviadas a servicios diferentes pueden estar sujetas a políticas distintas.
Algunos proveedores proporcionan bibliotecas que implementan políticas de reintentos, donde la
aplicación puede especificar el número máximo de reintentos, el tiempo entre ellos y otros parámetros.

Una aplicación debe registrar los detalles de los errores y de las operaciones erróneas. Esta información
es útil para los operadores. Si un servicio está ocupado o no disponible con frecuencia, suele deberse
a que el servicio ha agotado sus recursos. Para reducir la frecuencia de estos errores, escale el servicio

225 CAPÍTULO 6 | Catálogo de patrones


horizontalmente. Por ejemplo, si un servicio de base de datos se sobrecarga continuamente, puede ser
beneficioso particionar la base de datos y repartir la carga entre varios servidores.

Microsoft Entity Framework ofrece servicios para reintentar las operaciones de base de datos. Además,
la mayoría de los SDK de cliente y los servicios de Azure incluyen un mecanismo de reintento. Para
obtener más información, consulte la guía de reintentos para servicios específicos.

Problemas y consideraciones
Debe considerar los puntos siguientes a la hora de decidir cómo implementar este patrón.

La política de reintentos debe ajustarse a los requerimientos empresariales de la aplicación y a la naturaleza


del error. En el caso de algunas operaciones no críticas, es mejor aceptar el error de forma inmediata
que reintentar varias veces y afectar al rendimiento de la aplicación. Por ejemplo, en una aplicación web
interactiva que accede a un servicio remoto, es mejor mostrar un error después de un pequeño número de
reintentos con solo un breve retraso entre ellos y mostrar un mensaje adecuado al usuario (por ejemplo,
"vuelva a intentarlo más tarde"). En el caso de una aplicación por lotes, puede resultar más adecuado
incrementar el número de reintentos con un retraso de crecimiento exponencial entre los intentos.

Una política de reintentos agresiva con un retraso mínimo entre los intentos y gran cantidad de reintentos,
puede degradar aún más un servicio ocupado que se ejecuta al límite de su capacidad, o casi. Esta política
de reintentos también puede afectar a la capacidad de respuesta de la aplicación si se intenta realizar una
operación con errores continuamente.

Si una solicitud sigue generando errores después de un número de reintentos significativo, es mejor que
la aplicación evite que se dirijan más solicitudes al mismo recurso y que simplemente notifique el error
de forma inmediata. Cuando el período expire, la aplicación puede dejar pasar una o más solicitudes
provisionalmente para ver si se procesan correctamente. Para obtener más detalles de esta estrategia,
consulte el patrón de interruptor de circuito.

Considere si la operación es idempotente. Si es así, es intrínsecamente segura para reintentar. De


lo contrario, los reintentos pueden hacer que la operación se ejecute más de una vez, con efectos
secundarios no deseados. Por ejemplo, un servicio puede recibir la solicitud, procesarla correctamente
pero generar un error al enviar la respuesta. En ese punto, puede que la lógica de reintento reenvíe la
solicitud, al dar por hecho que la primera no se recibió.

Una solicitud a un servicio puede generar errores por diversas razones y producir distintas excepciones
en función de la naturaleza del error. Algunas excepciones indican un error que puede resolverse
rápidamente, mientras que otras indican un error más permanente. Es útil para la política de reintentos
ajustar el tiempo entre reintentos en función del tipo de excepción.

Piense en la forma en que reintentar una operación que forma parte de una transacción afectará a la
coherencia global de esta última. Ajuste la política de reintentos para las operaciones transaccionales a fin
de maximizar las posibilidades de éxito y reducir la necesidad de deshacer todos los pasos de la transacción.

Asegúrese de que el código de reintento se haya probado en su totalidad en diversas condiciones de


error. Compruebe que no afecta gravemente al rendimiento o la fiabilidad de la aplicación, no carga
excesivamente los servicios y los recursos ni genera condiciones de carrera o cuellos de botella.

Implemente lógica de reintento solo allí donde se entienda todo el contexto de una operación de error.
Por ejemplo, si una tarea que contiene una política de reintentos invoca a otra que también contiene
una política de este tipo, el procesamiento puede sufrir largos retrasos debido a este nivel adicional de
reintentos. Puede ser preferible configurar la tarea de nivel inferior para el error inmediato y notificar del
motivo del error a la tarea que la invoca. A continuación, la tarea de nivel superior puede administrar el
error en función de su propia política.
Es importante registrar todos los errores de conectividad que causan un reintento para poder identificar
los problemas subyacentes con la aplicación, los servicios o los recursos.

226 CAPÍTULO 6 | Catálogo de patrones


Investigue los errores más probables de un servicio o un recurso para descubrir si pueden llegar a ser
permanentes o irreversibles. Si lo son, es mejor administrar el error como excepción. La aplicación puede
informar de la excepción o registrarla y, después, invocar un servicio alternativo (si hay alguno disponible)
u ofrecer funcionalidad degradada para poder continuar. Para obtener más información sobre cómo
detectar y administrar los errores permanentes, consulte el patrón de interruptor de circuito.

Patrón de Programador-Agente-Supervisor
Coordine un conjunto de acciones distribuidas como una operación única. Si cualquiera de las acciones
genera errores, intente administrarlos de forma transparente o deshaga el trabajo realizado para que la
operación al completo se realice correctamente o no, como un todo. Esto puede agregar resistencia a un
sistema distribuido, ya que le permite recuperar y reintentar acciones que no se pudieron realizar debido
a excepciones transitorias y errores permanentes o de proceso.

Contexto y problema
Una aplicación realiza tareas que incluyen una serie de pasos, algunos de los cuales pueden invocar
servicios remotos o acceder a recursos remotos. Los pasos individuales pueden ser independientes entre
sí, si bien se organizan mediante la lógica de aplicación que implementa la tarea.

Siempre que sea posible, la aplicación debe asegurarse de que la tarea se ejecuta hasta el final
y de resolver cualquier error que pueda ocurrir al acceder a los servicios o recursos remotos. Los
errores pueden producirse por muchas razones. Por ejemplo, puede que la red esté inactiva, que las
comunicaciones se hayan interrumpido, que un servicio remoto no responda o tenga un estado inestable,
o incluso que un recurso remoto esté temporalmente inaccesible debido a posibles restricciones de
los recursos. En muchos casos, los errores serán transitorios y pueden abordarse mediante el patrón de
reintento.

Si la aplicación detecta un error más persistente del que no puede recuperarse con facilidad, debe ser
capaz de restaurar el sistema a un estado coherente y de garantizar la integridad de toda la operación.

Solución
El patrón de Programador-Agente-Supervisor define los actores siguientes, que coordinan los pasos que
deben realizarse como parte de la tarea global.

●● El Programador organiza los pasos que componen la tarea que se va a ejecutar y coordina su
funcionamiento. Los pasos pueden combinarse en una canalización o en un flujo de trabajo. El
Programador es el responsable de garantizar que los pasos del flujo de trabajo se realicen en el orden
correcto. Al realizarse cada uno de los pasos, el Programador registra el estado del flujo de trabajo,
como "paso no iniciado aún", "paso en ejecución" o "paso completado". La información de estado
también debe incluir el límite de tiempo máximo permitido para la finalización del paso, llamado
tiempo de finalización. Si un paso requiere acceso a un recurso o servicio remoto, el Programador
invoca al Agente adecuado y le pasa los detalles del trabajo que debe realizarse. El Programador suele
comunicarse con un Agente mediante mensajes asincrónicos de solicitud/respuesta, que pueden
implementarse mediante colas, aunque también se pueden usar otras tecnologías de mensajes
distribuidos.

●● El Programador realiza una función similar al Administrador de procesos en el patrón de


Administrador de procesos. El flujo de trabajo real lo suele definir e implementar un motor de flujo
de trabajo controlado por el Programador. Este enfoque desacopla la lógica de negocios del flujo de
trabajo desde el Programador.

227 CAPÍTULO 6 | Catálogo de patrones


●● El Agente contiene lógica que encapsula una llamada a un servicio remoto o el acceso a un recurso
remoto al que se hace referencia en un paso de una tarea. Cada Agente suele encapsular las llamadas
a un único servicio o recurso, con la implementación de la lógica de reintento y de control de errores
adecuada (sujeta a una restricción de tiempo de espera, que se describe más adelante). Si los pasos
del flujo de trabajo que ejecuta el Programador usan varios servicios y recursos en distintos pasos,
cada paso puede hacer referencia a un Agente distinto (este es un detalle de la implementación del
patrón).

●● El Supervisor controla el estado de los pasos en la tarea que realiza el Programador. Se ejecuta
periódicamente (la frecuencia es específica del sistema) y examina el estado de los pasos que
mantiene el Programador. Si detecta que el tiempo de espera se ha agotado o se ha producido algún
error, organiza el Agente adecuado para recuperar el paso o ejecutar la acción correctiva adecuada
(que puede incluir la modificación del estado de un paso). Tenga en cuenta que el Programador y los
Agentes son los encargados de implementar las acciones de recuperación o correctivas. El Supervisor
solo tiene que solicitar que se realicen estas acciones.

El Programador, el Agente y el Supervisor son componentes lógicos y su implementación física depende


de la tecnología que se use. Por ejemplo, varios agentes lógicos pueden implementarse como parte de un
servicio web único.

El Programador conserva la información sobre el progreso de la tarea y el estado de cada paso en un


almacén de datos duradero, que se llama almacén de estado. El Supervisor puede usar esta información
para ayudar a determinar si un paso ha generado errores. En la ilustración se muestra la relación entre el
Programador, los Agentes, el Supervisor y el almacén de estado.

228 CAPÍTULO 6 | Catálogo de patrones


Este diagrama muestra una versión simplificada del patrón. En una implementación real, puede haber
varias instancias del Programador en ejecución al mismo tiempo, cada una con un subconjunto de
tareas. Del mismo modo, el sistema puede ejecutar varias instancias de cada Agente o incluso múltiples
Supervisores. En este caso, los Supervisores deben coordinar su trabajo detenidamente para asegurarse
de no competir por recuperar los mismos pasos y tareas con errores. El patrón de elección del líder
ofrece una solución posible a este problema.

Cuando la aplicación está lista para ejecutar una tarea, envía una solicitud al Programador. El Programador
registra la información de estado inicial sobre la tarea y sus pasos (por ejemplo, paso no iniciado aún)
en el almacén de estado y, después, comienza a realizar las operaciones que define el flujo de trabajo.
Al iniciar cada paso, el Programador actualiza la información de estado del paso concreto en dicho
almacén (por ejemplo, paso en ejecución).

Si un paso hace referencia a un recurso o servicio remoto, el Programador envía un mensaje al Agente
adecuado. El mensaje contiene la información que el Agente necesita para pasar al servicio o acceder
al recurso, además del tiempo de finalización para la operación. Si el Agente completa la operación
correctamente, devuelve una respuesta al Programador. Entonces, el Programador puede actualizar
la información de estado en el almacén de estado (por ejemplo, paso completado) y realizar el paso
siguiente. Este proceso continúa hasta que se completa toda la tarea.

Un Agente puede implementar cualquier lógica de reintento necesaria para realizar su trabajo.
Sin embargo, si el Agente no completa su trabajo antes de que expire el tiempo de finalización, el
Programador da por hecho que la operación es errónea. En ese caso, el Agente debe detener su trabajo
e intentar no devolver nada al Programador (ni siquiera un mensaje de error) ni probar cualquier tipo de
recuperación. El motivo de esta restricción es que puede que haya otra instancia del Agente programada
para ejecutar el paso incorrecto, después de haberse agotado el tiempo de espera de este o de generar
un error (este proceso se describe más adelante).

Si se produce un error del Agente, el Programador no recibirá ninguna respuesta. El patrón no distingue
entre un paso para el que se ha agotado el tiempo de espera y otro con errores en sí.

Si se agota el tiempo de espera de un paso o este genera errores, el almacén de estado contendrá un
registro que indica que el paso se está ejecutando, pero el tiempo de finalización habrá transcurrido.
El Supervisor busca pasos como este e intenta recuperarlos. Una estrategia posible es que el Supervisor
actualice el valor de finalización para ampliar el tiempo disponible para completar el paso y que,
a continuación, envíe un mensaje al Programador donde se identifique el paso en el que se ha agotado
el tiempo de espera. Después, el Programador puede intentar repetir el paso. Sin embargo, este diseño
requiere que las tareas sean idempotentes.

Puede darse el caso de que el Supervisor tenga que evitar el reintento de un mismo paso si este genera
errores o expira continuamente. Para ello, puede conservar un recuento de reintentos para cada paso,
junto con la información de estado, en el almacén de estado. Si el recuento supera un umbral predefinido,
el Supervisor puede adoptar la estrategia de esperar durante un período de tiempo prolongado antes
de notificar al Programador que debe reintentar el paso, con la expectativa de que el error se resuelva
durante este tiempo. Como alternativa, el Supervisor puede enviar un mensaje al Programador para
solicitar que se deshaga la tarea entera mediante la implementación de un patrón de transacciones
de compensación. Este enfoque dependerá de que el Programador y los Agentes proporcionen la
información necesaria para implementar las operaciones de compensación para cada paso completado
correctamente.

El Supervisor no tiene como objetivo controlar al Programador y a los Agentes, ni reiniciarlos en


caso de error. De este aspecto del sistema debe encargarse la infraestructura en la que se ejecutan
estos componentes. Del mismo modo, el Supervisor no debe tener conocimiento de las operaciones
empresariales que ejecutan las tareas que realiza el Programador (incluso de cómo compensar en caso
de error de estas). Este es el objetivo de la lógica de flujo de trabajo que implementa el Programador.
La única responsabilidad del Supervisor es determinar si un paso es erróneo y organizar que este se repita
o que la tarea donde se incluye se deshaga al completo.

229 CAPÍTULO 6 | Catálogo de patrones


Si el Programador se reinicia después de un error o el flujo de trabajo que este ejecuta termina de
forma inesperada, el Programador debería poder determinar el estado de cualquier tarea que estuviera
procesando cuando se produjo el error y estar preparado para reanudarla a partir de ese punto. Es
probable que los detalles de implementación de este proceso sean específicos del sistema. Si la tarea
no se puede recuperar, puede que sea necesario deshacer el trabajo ya realizado por esta. Esto también
puede requerir la implementación de una transacción de compensación.

La ventaja clave de este patrón es que el sistema es resistente en caso de errores temporales o
irrecuperables inesperados y puede construirse de forma que se recupere automáticamente. Por ejemplo,
si se produce un error de un Agente o del Programador, puede iniciarse uno nuevo y el Supervisor
puede organizar que se reanude una tarea. Si el Supervisor falla, puede iniciarse otra instancia que
asuma el control a partir del punto donde se produjo el error. Si el Supervisor está programado para
ejecutarse periódicamente, puede iniciarse una nueva instancia automáticamente después de un intervalo
predefinido. El almacén de estado puede replicarse para alcanzar un grado de resistencia aún mayor.

Problemas y consideraciones
Debe considerar los puntos siguientes a la hora de decidir cómo implementar este patrón:
●● Este patrón puede ser difícil de implementar y requiere pruebas exhaustivas de cada modo de error
posible del sistema.

●● La lógica de recuperación o reintento que implementa el Programador es compleja y depende de la


información de estado contenida en el almacén correspondiente. También puede ser necesaria para
registrar la información requerida para implementar una transacción de compensación en un almacén
de datos duradero.

●● La frecuencia con la que se ejecuta el Supervisor es importante. Debe ejecutarse con una frecuencia
suficiente para impedir que los pasos con errores bloqueen una aplicación durante un período
prolongado y, a la vez, no tan a menudo como para convertirse en una sobrecarga.

●● Los pasos que realiza un Agente pueden ejecutarse más de una vez. La lógica que implementa estos
pasos debe ser idempotente.

Cuándo utilizar este patrón


Use este patrón cuando un proceso que se ejecuta en un entorno distribuido, como el cloud, deba ser
resistente a errores de comunicaciones o de funcionamiento.

Puede que este patrón no sea adecuado para las tareas que no invocan servicios remotos ni acceden
a recursos remotos.

Ejemplo
Se ha implementado en Microsoft Azure una aplicación web que implementa un sistema de comercio
electrónico. Los usuarios pueden ejecutarla para buscar los productos disponibles y realizar pedidos. La
interfaz de usuario se ejecuta como rol web y los elementos de procesamiento de pedidos de la aplicación
se implementan como un conjunto de roles de trabajo. Parte de la lógica de procesamiento de pedidos
implica acceder a un servicio remoto. Este aspecto del sistema puede ser propenso a errores duraderos
o transitorios. Por este motivo, los diseñadores usan el patrón de Programador-Agente-Supervisor a fin de
implementar los elementos de procesamiento de pedidos del sistema.

230 CAPÍTULO 6 | Catálogo de patrones


Cuando un cliente hace un pedido, la aplicación crea un mensaje que lo describe y envía dicho mensaje
a una cola. Un proceso de envío independiente, que se ejecuta en un rol de trabajo, recupera el mensaje,
inserta los detalles del pedido en la base de datos correspondiente y crea un registro para el proceso del
pedido en el almacén de estado. Tenga en cuenta que las inserciones en la base de datos de pedidos y en
el almacén de estado se realizan como parte de la misma operación. El proceso de envío está diseñado
para garantizar que ambas inserciones se realicen de forma conjunta.

La información de estado que el proceso de envío crea para el pedido incluye:


●● OrderID. El identificador del pedido en la base de datos correspondiente.

●● LockedBy. El identificador de instancia del rol de trabajo que administra el pedido. Puede haber varias
instancias del rol de trabajo que ejecuten el Programador, pero cada pedido debe administrarse en
una sola instancia.

●● CompleteBy. Plazo de tiempo para el proceso del pedido.

●● ProcessState. Estado actual de la tarea que administra el pedido. Los estados posibles son:
● Pendiente. El pedido ya se ha creado pero aún no ha se iniciado el procesamiento.
● En procesamiento. El pedido se está procesando.
● Procesado. El pedido se ha procesado correctamente.
● Error. No se pudo realizar el procesamiento del pedido.

●● FailureCount. Número de veces que se ha intentado procesar el pedido.

En esta información de estado, el campo OrderID se copia del identificador correspondiente del nuevo
pedido. Los campos LockedBy y CompleteBy se establecen como nulos, el campo ProcessState se
establece en Pendiente y el campo FailureCount se establece en 0.

En este ejemplo, la lógica de administración de pedidos es relativamente simple y tiene un único paso que
invoca un servicio remoto. En un escenario más complejo con múltiples pasos, el proceso de envío puede
implicar a varios de ellos, por lo que se crearían diversos registros en el almacén de estado y cada uno de
ellos describiría el estado de un paso individual.

El Programador también se ejecuta como parte de un rol de trabajo e implementa la lógica de negocios
que administra el pedido. Una instancia del Programador que sondea nuevos pedidos examina el almacén
de estado para encontrar registros en los que el campo LockedBy sea nulo y ProcessState esté pendiente.
Cuando el Programador encuentra un pedido nuevo, rellena inmediatamente el campo LockedBy con
su identificador de instancia propio, establece un valor adecuado en el campo CompleteBy y define
ProcessState como En procesamiento. El código está diseñado para ser exclusivo e individual, a fin de
garantizar que dos instancias simultáneas del Programador no intenten administrar un mismo pedido
al mismo tiempo.

A continuación, el Programador ejecuta el flujo de trabajo empresarial para procesar el pedido de forma
asincrónica y pasa el valor del campo OrderID del almacén de estado. El flujo de trabajo encargado del
pedido recupera los detalles de este de la base de datos de pedidos y realiza su trabajo. Cuando en un
paso del flujo de trabajo de procesamiento de pedidos debe invocarse al servicio remoto, se usa un
Agente. El paso del flujo de trabajo se comunica con el Agente mediante un par de colas de mensajes
de Azure Service Bus que actúan como canal de solicitud/respuesta. En la ilustración se muestra una vista
de alto nivel de la solución.

231 CAPÍTULO 6 | Catálogo de patrones


El mensaje que se envía al Agente desde un paso del flujo de trabajo describe el pedido e incluye el
tiempo de finalización. Si el Agente recibe una respuesta del servicio remoto antes de que expire el
tiempo de finalización, envía un mensaje de respuesta a la cola de Service Bus en la que escucha el
flujo de trabajo. Cuando el paso del flujo de trabajo recibe el mensaje de respuesta válido, completa su
procesamiento y el Programador establece el campo ProcessState de estado del pedido como Procesado.
En este punto, el procesamiento del pedido se ha completado correctamente.

Si el tiempo de finalización expira antes de que el Agente reciba una respuesta del servicio remoto,
el Agente detiene su procesamiento y termina de administrar el pedido. Del mismo modo, si el flujo de
trabajo que administra el pedido sobrepasa el tiempo de finalización, también se termina. En ambos casos,
el estado del pedido en el almacén de estado permanece como En procesamiento, pero el tiempo de
finalización indica que el tiempo para procesar el pedido ha pasado y se considera que el proceso no se
ha realizado correctamente. Tenga en cuenta que, si el Agente que accede al servicio remoto o el flujo de
trabajo que administra el pedido (o ambos) terminan de forma inesperada, la información del almacén de
estado quedará también establecida como En procesamiento y tendrá un valor de tiempo de finalización
expirado.

232 CAPÍTULO 6 | Catálogo de patrones


Si el Agente detecta un error irrecuperable no transitorio mientras intenta contactar con el servicio remoto,
puede devolver una respuesta de error al flujo de trabajo. El Programador puede establecer el estado
del pedido como Error y generar un evento que alerte a un operador. A continuación, el operador puede
intentar resolver manualmente el motivo del error y reenviar el paso de procesamiento erróneo.

El Supervisor examina periódicamente el almacén de estado para buscar pedidos cuyo valor de tiempo
de finalización haya expirado. Si encuentra un registro, incrementa el valor del campo FailureCount. Si el
valor de número de errores está por debajo de un umbral especificado, el Supervisor restablece el campo
LockedBy como nulo, actualiza el campo CompleteBy con un nuevo tiempo de caducidad y establece
el valor del campo ProcessState en Pendiente. Una instancia del Programador puede elegir este pedido
y realizar su procesamiento como se ha indicado. Si el valor de número de errores supera un umbral
especificado, se da por hecho que el motivo del error no es transitorio. El Supervisor establece el estado
del pedido como Error y genera un evento que alerta a un operador.

En este ejemplo, el Supervisor se implementa en un rol de trabajo independiente. Se pueden usar diversas
estrategias para organizar la ejecución de la tarea del Supervisor, incluido el uso del servicio Scheduler
de Azure (no debe confundirse con el componente Programador de este patrón). Para obtener más
información sobre el servicio Scheduler de Azure, visite la página de Scheduler.

Aunque no se muestra en este ejemplo, puede que el Programador tenga que mantener informada a la
aplicación que envió el pedido acerca del progreso y del estado del mismo. La aplicación y el Programador
están aislados entre sí para eliminar cualquier dependencia entre ellos. La aplicación no sabe qué instancia del
Programador administra el pedido y este a su vez desconoce qué instancia específica de la aplicación lo envió.

Para poder notificar el estado del pedido, la aplicación puede usar su propia cola de respuestas privada.
Los detalles de esta cola de respuestas se incluirían como parte de la solicitud dirigida al proceso de envío,
que incluiría esta información en el almacén de estado. A continuación, el Programador enviaría mensajes
a esta cola en los que se indicaría el estado del pedido (solicitud recibida, pedido completado, pedido con
errores, etc.) Además, debe incluir el identificador del pedido en los mensajes para que la aplicación pueda
correlacionarlos con la solicitud original.

Guías y patrones relacionados


Los siguientes patrones y guías también pueden ser relevantes al implementar este patrón:
●● Patrón de reintento. Un Agente puede usar este patrón para reintentar de forma transparente una
operación que accede a un servicio o recurso remoto que ha fallado anteriormente. Úselo cuando se
espera que el motivo del error sea transitorio y que pueda corregirse.

●● Patrón de interruptor de circuito. Un Agente puede usar este patrón para administrar los errores que
tardan un plazo de tiempo variable en corregirse al conectarse a un recurso o servicio remoto.

●● Patrón de transacciones de compensación. Si el flujo de trabajo que realiza un Programador no puede


completarse correctamente, puede que sea necesario deshacer cualquier trabajo realizado previamente. El
patrón de transacciones de compensación describe cómo puede lograrse este objetivo para las operaciones
que siguen el modelo de coherencia final. Este tipo de operaciones suele implementarlas un Programador
que realiza flujos de trabajo y procesos de negocio complejos.

●● Introducción a la mensajería asincrónica. Los componentes del patrón de Programador-Agente-Supervisor


suelen ejecutarse desacoplados entre sí y se comunican de forma asincrónica. Describe algunos de los
enfoques que pueden usarse para implementar la comunicación asincrónica basada en las colas de
mensajes.

●● Patrón de elección del líder. Puede ser necesario para coordinar las acciones de varias instancias

233 CAPÍTULO 6 | Catálogo de patrones


de un Supervisor a fin de evitar que intenten recuperar el mismo proceso con errores. El patrón de
elección del líder describe cómo hacerlo.

●● Arquitectura de cloud: el patrón de Programador-Agente-Supervisor en el blog de Clemens Vasters.

●● Patrón de Administrador de procesos

●● Referencia 6: Una saga de sagas. Ejemplo que muestra cómo el patrón CQRS usa un administrador
de procesos (parte de la guía de CQRS Journey).

●● Microsoft Azure Scheduler

Patrón de particionamiento
Divide un almacén de datos en un conjunto de particiones horizontales. Esto puede mejorar la
escalabilidad al almacenar grandes volúmenes de datos y al acceder a ellos.

Contexto y problema
Un almacén de datos hospedado por un solo servidor puede estar sujeto a las limitaciones siguientes:

●● Espacio de almacenamiento. Se espera que un almacén de datos para una aplicación en el cloud
a gran escala contenga un volumen de datos elevado que puede aumentar significativamente con
el tiempo. Normalmente, un servidor proporciona una cantidad finita de almacenamiento en disco,
pero pueden reemplazarse los discos existentes por otros más grandes o agregar más discos a una
máquina a medida que aumenta el volumen de datos. Sin embargo, el sistema llegará a alcanzar un
límite en el que no será posible incrementar la capacidad de almacenamiento de forma fácil en un
servidor dado.

●● Recursos informáticos. Una aplicación en el cloud debe admitir un gran número de usuarios
simultáneos, cada uno de los cuales ejecuta consultas que recuperan información del almacén
de datos. Es posible que un servidor único que hospede el almacén de datos no pueda ofrecer la
potencia de procesamiento necesaria para admitir esta carga. Los usuarios obtendrían tiempos de
respuesta más largos y se producirían errores frecuentes al agotarse el tiempo de espera cuando
las aplicaciones intentaran almacenar y recuperar datos. Podría agregarse memoria o actualizar los
procesadores, pero el sistema llegaría a un límite en el que no sería posible seguir aumentando los
recursos informáticos.

●● Ancho de banda de red. El rendimiento de un almacén de datos que se ejecuta en un único servidor
se rige, en última instancia, por la velocidad a la que el servidor puede recibir solicitudes y enviar
respuestas. El volumen del tráfico de red puede sobrepasar la capacidad de la red que se usa para
conectarse al servidor, lo que daría lugar a solicitudes erróneas.

●● Geografía. Puede que sea necesario almacenar los datos generados por usuarios específicos en la
misma región en la que se encuentran debido a motivos legales, de cumplimiento o de rendimiento,
o reducir la latencia de acceso a los datos. Si los usuarios están dispersos en distintos países o regiones,
puede que no sea posible almacenar todos los datos de la aplicación en un almacén de datos único.

Agregar capacidad de disco, potencia de procesamiento, memoria y conexiones de red para escalar
verticalmente puede posponer los efectos de algunas de estas limitaciones, pero seguramente solo se
trate de una solución temporal. Una aplicación comercial en el cloud capaz de admitir un gran número
de usuarios y volúmenes de datos elevados debe ser capaz de escalar de forma prácticamente indefinida,
por lo que el escalado vertical no es necesariamente la mejor solución.

234 CAPÍTULO 6 | Catálogo de patrones


Solución
Divida el almacén de datos en particiones horizontales. Cada partición tiene el mismo esquema, pero
contiene un subconjunto propio único de los datos. Una partición es un almacén de datos en sí (puede
contener datos de numerosas entidades de distintos tipos) que se ejecuta en un servidor el cual actúa
como nodo de almacenamiento.

Este patrón ofrece las ventajas siguientes:


●● Puede agregar más particiones que se ejecuten en nodos de almacenamiento adicionales para escalar
el sistema horizontalmente.

●● Un sistema puede utilizar hardware listo para usar en lugar de equipos caros y especializados para
cada nodo de almacenamiento.

●● Puede reducir la contención y mejorar el rendimiento al equilibrar la carga de trabajo entre las
distintas particiones.

●● En el cloud, las particiones pueden ubicarse físicamente cerca de los usuarios que van a acceder
a los datos.

Al dividir un almacén de datos en particiones, decida qué datos deben colocarse en cada una de ellas.
Normalmente, una partición contiene elementos que se engloban dentro de un rango determinado por
uno o más atributos de los datos. Estos atributos forman la clave de partición, que debe ser estática y no
estar basada en datos que pueden cambiar.

El particionamiento organiza los datos físicamente. Cuando una aplicación almacena y recupera datos, la
lógica de particionamiento dirige la aplicación a la partición adecuada. Dicha lógica puede implementarse
como parte del código de acceso a datos en la aplicación. También puede implementarla el sistema de
almacenamiento de datos, si admite el particionamiento de forma transparente.

La abstracción de la ubicación física de los datos en la lógica de particionamiento proporciona un nivel


de control elevado sobre qué particiones contienen qué datos. Además, permite que los datos se migren
entre particiones sin necesidad de reprocesar la lógica de negocios de una aplicación en el caso de que
las particiones deban redistribuirse más adelante (por ejemplo, si se desequilibran). La contrapartida es la
sobrecarga adicional de acceso a los datos que se requiere para determinar la ubicación de cada elemento
de datos al recuperarlo.

Para garantizar un rendimiento y una escalabilidad óptimos, es importante dividir los datos de forma
adecuada para los tipos de consultas que la aplicación realiza. En muchos casos, es poco probable que el
esquema de particionamiento coincida exactamente con los requisitos de cada consulta. Por ejemplo, en
un sistema multiinquilino, puede que una aplicación tenga que recuperar datos de inquilino mediante el
identificador correspondiente, pero también puede que tenga que buscar estos datos basándose en otros
atributos, como el nombre o la ubicación del inquilino. Para afrontar estas situaciones, implemente una
estrategia de particionamiento con una clave de partición que admita las consultas que se realizan con
más frecuencia.

Si las consultas recuperan datos de forma periódica mediante una combinación de valores de atributos,
seguramente podrá enlazar atributos entre sí para definir una clave de partición compuesta. Como
alternativa, use un patrón como el de tabla de índices para proporcionar una búsqueda rápida de los
datos basada en atributos que la clave de partición no cubre.

235 CAPÍTULO 6 | Catálogo de patrones


Estrategias de particionamiento
Al seleccionar la clave de partición y decidir cómo distribuir los datos entre las particiones se suelen usar
tres estrategias. Tenga en cuenta que no es necesario mantener una correspondencia unívoca entre las
particiones y los servidores que las hospedan: un único servidor puede hospedar varias particiones. Las
estrategias son:

La estrategia de búsqueda. En esta estrategia, la lógica de particionamiento implementa un mapa que


enruta una solicitud de datos a la partición que contiene dichos datos con la clave de partición. En una
aplicación multiinquilino, todos los datos de un inquilino pueden almacenarse juntos en una partición
y usar el identificador de inquilino como clave de partición. Varios inquilinos pueden compartir la misma
partición, pero los datos de un mismo inquilino no se reparten entre varias particiones. En la ilustración
se muestra el particionamiento de datos de inquilinos en función de sus identificadores.

La asignación entre la clave de partición y el almacenamiento físico puede basarse en particiones físicas
en las que cada clave de partición se asigna a una partición física. Una alternativa más flexible para
reequilibrar las particiones es el particionamiento virtual, donde las claves de partición se asignan al
mismo número de particiones virtuales que, a su vez, se asignan a un número menor de particiones
físicas. En este enfoque, una aplicación localiza los datos con una clave de partición que hace referencia
a una partición virtual y, a su vez, el sistema asigna las particiones virtuales a particiones físicas de forma
transparente. La asignación entre una partición virtual y una física puede cambiar sin necesidad de
modificar el código de la aplicación para que use un conjunto distinto de claves de partición.

La estrategia de rango. Esta estrategia agrupa los elementos relacionados en la misma partición y los
ordena por clave de partición: las claves de partición son secuenciales. Es útil para las aplicaciones que
suelen recuperar conjuntos de elementos mediante consultas por rango (consultas que devuelven un
conjunto de elementos de datos para una clave de partición que se engloba en un rango determinado).
Por ejemplo, si una aplicación necesita buscar de forma periódica todos los pedidos realizados en un
mes determinado, estos datos pueden recuperarse de forma más rápida si todos los pedidos de un mes
se almacenan por orden de fecha y hora en la misma partición. Si cada uno de los pedidos se almacena
en una partición distinta, tendrán que realizarse un gran número de consultas puntuales (consultas que
devuelven un solo elemento de datos) para recuperarlos individualmente. En la ilustración siguiente se
muestra el almacenamiento de conjuntos secuenciales (rangos) de datos en una partición.

236 CAPÍTULO 6 | Catálogo de patrones


En este ejemplo, la clave de partición es una clave compuesta que contiene el mes del pedido como
elemento más significativo, seguido del día y la hora del pedido. Los datos de los pedidos se ordenan de
forma natural al crear pedidos y agregarlos a una partición. Algunos almacenes de datos admiten claves de
partición de dos partes, con un elemento de la clave de partición que la identifica y una clave de fila que
identifica a un elemento de la partición de forma única. Los datos se suelen retener en el orden de la clave
de fila en la partición. Los elementos sujetos a consultas por rango que deben agruparse, pueden usar una
clave de partición con el mismo valor que la clave de fragmento, pero con un valor único para la clave de fila.

La estrategia de hash. El objetivo de esta estrategia es reducir las posibilidades de puntos calientes
(particiones que reciben una cantidad desproporcionada de la carga). Distribuye los datos entre las
particiones de forma que logra un equilibrio entre el tamaño de cada partición y la carga media de cada
una de ellas. La lógica de particionamiento calcula la partición en la que se va a almacenar un elemento en
función de un hash de uno o más atributos de los datos. La función de uso de hash elegida debe distribuir
los datos de manera uniforme entre las particiones, para lo que posiblemente inserte algún elemento
aleatorio en el proceso. En la ilustración siguiente se muestran datos de inquilinos de particionamiento
que se basan en un hash de identificadores de inquilino.

237 CAPÍTULO 6 | Catálogo de patrones


Para entender las ventajas de la estrategia de hash sobre otras estrategias de particionamiento, tenga en
cuenta la forma en que una aplicación multiinquilino que inscribe nuevos inquilinos de forma secuencial
puede asignar los inquilinos a particiones en el almacén de datos. Al usar la estrategia de rango, todos
los datos de los inquilinos 1 a n se almacenarán en la partición A, todos los datos de los inquilinos n+1
a m se almacenarán en la partición B y así sucesivamente. Si los inquilinos que se han registrado más
recientemente son también los más activos, la mayor parte de la actividad de los datos se producirá en un
pequeño número de particiones, lo que puede causar puntos calientes. Por el contrario, la estrategia de
hash asigna inquilinos a las particiones en función de un hash de su identificador de inquilino. Esto significa
que es más probable que los inquilinos secuenciales se asignen a particiones distintas, que distribuirán la
carga entre ellos. En la ilustración anterior se muestra este escenario para los inquilinos 55 y 56.

Las tres estrategias de particionamiento tienen las siguientes ventajas y consideraciones:

●● Búsqueda. Ofrece un mayor control sobre la forma en que se configuran y se usan las particiones.
El uso de particiones virtuales reduce el impacto al reequilibrar los datos, ya que pueden agregarse
nuevas particiones físicas para homogeneizar la carga de trabajo. La asignación entre una partición
virtual y las particiones físicas que la implementan puede modificarse sin afectar al código de
aplicación que usa una clave de partición para almacenar y recuperar datos. La búsqueda de
ubicaciones de partición puede imponer una sobrecarga adicional.

●● Rango. Es fácil de implementar y funciona bien con consultas por rango, ya que suelen capturar
varios elementos de datos de una sola partición en una operación única. Esta estrategia ofrece una
administración más fácil de los datos. Por ejemplo, si los usuarios de una misma región están en la
misma partición, se pueden programar las actualizaciones por zona horaria en función del patrón
de demanda y carga local. Sin embargo, esta estrategia no proporciona un equilibrio óptimo entre
las particiones, ya que reequilibrarlas es difícil y puede que no resuelva el problema de las cargas
desiguales si la mayoría de la actividad es para las claves de partición adyacentes.

●● Hash. Esta estrategia es más adecuada para llevar a cabo una distribución más uniforme de la carga y
los datos. El enrutamiento de solicitudes puede realizarse directamente mediante la función hash. No
es necesario mantener un mapa. Tenga en cuenta que el procesamiento del hash puede implicar una
sobrecarga adicional. Además, reequilibrar las particiones resulta difícil.

Los sistemas de particionamiento más habituales implementan uno de los enfoques descritos
anteriormente, pero también debe tener en cuenta los requisitos empresariales de sus aplicaciones
y sus patrones de uso de datos. Por ejemplo, en una aplicación multiinquilino:

●● Puede particionar los datos en función de la carga de trabajo. Puede separar los datos de inquilinos
muy volátiles en particiones independientes. Como resultado, puede incrementarse la velocidad de
acceso a datos de otros inquilinos.

●● Puede particionar los datos en función de la ubicación de los inquilinos. Se pueden tomar los datos
de los inquilinos de una región geográfica específica sin conexión para realizar la copia de seguridad
y el mantenimiento durante las horas de menos tráfico en esa región, mientras que los datos de los
inquilinos de otras regiones se mantienen online y están accesibles en su horario laboral.

●● A los inquilinos de alto valor se les pueden asignar sus propias particiones privadas, de alto
rendimiento y carga ligera, mientras que los de valor más bajo se puede esperar que compartan
particiones más ocupadas y densas.
Los datos de inquilinos que necesitan un alto grado de privacidad y aislamiento pueden almacenarse
en un servidor totalmente independiente.

●● Los datos de inquilinos que necesitan un alto grado de privacidad y aislamiento pueden almacenarse
en un servidor totalmente independiente.

238 CAPÍTULO 6 | Catálogo de patrones


Operaciones de transferencia de datos y escalado
Cada una de las estrategias de fragmentación conlleva diferentes capacidades y niveles de complejidad
para la gestión del escalado vertical y horizontal, el traslado de datos y el mantenimiento del estado.

La estrategia de búsqueda permite realizar operaciones de escalado y transferencia de datos orientadas


a los usuarios, ya sea online o sin conexión. La técnica consiste en suspender parte o toda la actividad de
los usuarios (posiblemente en períodos de menos tráfico), transferir los datos a la nueva partición virtual
o fragmentación física, cambiar las asignaciones, invalidar o actualizar las memorias caché que contengan
estos datos y luego permitir que se reanude la actividad del usuario. Por lo general, este tipo de operación
se puede gestionar de forma centralizada. La estrategia de búsqueda requiere que el estado se pueda
almacenar fácilmente en memoria caché y que admita réplicas.

La estrategia de intervalo impone algunas limitaciones en las operaciones de escalado y transferencia


de datos, que por lo general se realizan cuando una parte o la totalidad del almacén de datos no está
conectado ya que es preciso dividir y combinar datos en las fragmentaciones. Es posible que el hecho
de transferir los datos para reequilibrar las fragmentaciones no solucione el problema de las cargas
desiguales si la mayoría de la actividad es para claves de fragmentación adyacentes o identificadores
de datos que están en el mismo rango. Es posible que en la estrategia de intervalo, también se requiera
mantener algún estado para asignar intervalos a las particiones físicas.

La estrategia de hash incrementa la complejidad de las operaciones de escalado y transferencia de datos


porque las claves de partición son hashes de las claves de partición o identificadores de datos. Se determinará
la nueva ubicación de cada partición desde la función de hash o bien, la función modificada para proporcionar
las asignaciones correctas. Sin embargo, la estrategia de hash no requiere un mantenimiento del estado.

Problemas y consideraciones
Tenga en cuenta los puntos siguientes a la hora de decidir cómo implementar este patrón:
●● La fragmentación es complementaria a otras formas de partición, como la partición vertical y la
partición funcional. Por ejemplo, una sola fragmentación puede incluir entidades que se han dividido
en sentido vertical y una partición funcional puede implementarse como diversas fragmentaciones.
Para obtener más información sobre la fragmentación, consulte Guía de partición de datos.

●● Mantenga las fragmentaciones equilibradas para que todas ellas se ocupen de un volumen similar
de E/S. Dado que los datos se insertan y se eliminan, es necesario reequilibrar periódicamente las
fragmentaciones para garantizar una distribución uniforme y reducir las posibilidades de conflictos.
Las operaciones de reequilibrio pueden consumir muchos recursos. Para reducir la necesidad de
efectuar operaciones de reequilibrio, debe estar preparado para crecer y, para ello, deberá asegurarse
de que cada fragmentación disponga del suficiente espacio libre para ocuparse del volumen de
cambios esperado. También debe desarrollar estrategias y secuencias de comandos que pueda utilizar
para equilibrar rápidamente las fragmentaciones si esto fuera necesario.

●● Utilice datos estables para las claves de fragmentación. Si cambia la clave de fragmentación,
es posible que el elemento de datos correspondiente tenga que transferirse entre las fragmentaciones,
lo cual incrementaría la cantidad de trabajo que deben realizar las operaciones de actualización. Por
este motivo, se recomienda evitar basar la clave de fragmentación en información que pueda ser
volátil. En su lugar, busque atributos que no varíen o que de forma natural formen una clave.

●● Asegúrese de que las claves de fragmentación sean únicas. Por ejemplo, evite utilizar campos que
se incrementen automáticamente como la clave de fragmentación. En algunos sistemas, los campos
de incrementación automática no se pueden coordinar entre las distintas fragmentaciones, lo que
podría traducirse en que los elementos de las diferentes fragmentaciones tengan la misma clave de
fragmentación.

• Los valores de incrementación automática en otros campos que no sean de claves de


fragmentación también pueden provocar problemas. Por ejemplo, si utiliza campos de
incrementación automática para generar identificadores únicos, se podrán asociar al mismo
identificador dos elementos diferentes ubicados en fragmentaciones distintas.

239 CAPÍTULO 6 | Catálogo de patrones


No es posible diseñar una clave de fragmentación que coincida con los requisitos de cada
posible consulta a los datos. Divida los datos para admitir las consultas más frecuentes y, si
fuera necesario, cree tablas de índice secundarias para admitir las consultas que recuperan
datos mediante criterios basados en atributos que no formen parte de la clave de la
fragmentación. Para obtener más información, consulte Patrón de tabla e índice.
●● Las consultas que tienen acceso a una sola fragmentación son más eficientes que las que recuperan
datos de múltiples fragmentaciones, por tanto, es mejor no implantar un sistema de fragmentación
que obligue a las aplicaciones a efectuar gran cantidad de consultas que tengan que recurrir a datos
conservados en diferentes fragmentaciones. Recuerde que una sola fragmentación puede contener los
datos de múltiples tipos de entidades. Considere desnormalizar los datos para conservar las entidades
relacionadas, que generalmente se consultan a la vez, (por ejemplo, los detalles de los clientes y los
pedidos que han cursado) en la misma fragmentación para reducir el número de lecturas individuales
que debe realizar una aplicación.
● Si una entidad en una fragmentación hace referencia a una entidad almacenada en otra
fragmentación, incluya la clave de fragmentación de la segunda entidad en el esquema de la
primera entidad. Esto ayuda a mejorar el rendimiento de las consultas que hacen referencia a
datos relacionados en las fragmentaciones.
●● Si una aplicación tiene que realizar consultas que recuperen datos de múltiples fragmentaciones, estos
datos se pueden obtener a través de tareas paralelas. Entre los ejemplos se incluyen las consultas
de divergencia, donde las diversas fragmentaciones se recuperan en paralelo y, posteriormente, se
agregan en un resultado único. Sin embargo, este enfoque inevitablemente añade cierta complejidad
a la lógica de acceso a datos de las soluciones.
●● Para muchas aplicaciones, la creación de un número mayor de pequeñas fragmentaciones puede
resultar más eficiente que tener un número pequeño de fragmentaciones grandes porque ofrecen
más oportunidades para el equilibrio de cargas. Esto también puede ser útil si se prevé la necesidad
de migrar fragmentaciones entre ubicaciones físicas. Es más rápido transferir una fragmentación
pequeña que una grande.
●● Asegúrese de que los recursos disponibles para cada nodo de almacenamiento de la fragmentación
son suficientes para ocuparse de los requisitos de escalabilidad en términos de tamaño de los datos
y de rendimiento. Para obtener más información, consulte la sección sobre cómo diseñar particiones
para la escalabilidad en Guía de fragmentación de datos.
●● Piense en replicar datos de referencia en todas las fragmentaciones. Si una operación en donde
se recuperan datos desde una fragmentación hace referencia a datos estáticos o de transferencia
lenta en una misma consulta, agregue estos datos a la fragmentación. La aplicación puede entonces
recuperar todos los datos para la consulta fácilmente, sin tener hacer un recorrido adicional de ida
y vuelta hasta un almacén de datos independiente.
● Si cambian los datos de referencia que se conservan en diversas fragmentaciones, el sistema
debe sincronizar estos cambios en todas las fragmentaciones. El sistema puede experimentar
cierta inconsistencia mientras se produce esta sincronización. Si hace esto, deberá diseñar sus
aplicaciones para que puedan gestionarlo.
●● Puede resultar difícil mantener la integridad referencial y la coherencia entre fragmentaciones, por
lo que debería reducir al mínimo las operaciones que afecten a múltiples fragmentaciones. Si es
preciso que una aplicación modifique datos en las fragmentaciones, evalúe si va a ser necesario
completar la coherencia de los datos. En cambio, con un enfoque común en el cloud, se consigue
coherencia lo largo del tiempo. Los datos en cada partición se actualizan por separado y la lógica de
la aplicación debe asumir la responsabilidad de garantizar que todas actualizaciones se completan
correctamente, así como la gestión de las incoherencias que pueden surgir de la consulta de datos
mientras se ejecuta una operación coherente. Para obtener más información sobre cómo implementar
la coherencia a lo largo del tiempo, consulte la Introducción a la coherencia de datos.
●● Configurar y administrar un gran número de fragmentaciones puede resultar complicado. Tareas
como la supervisión, la realización de copias de seguridad, la comprobación de la coherencia y el
registro o la auditoría deben desarrollarse en múltiples fragmentaciones y servidores, probablemente

240 CAPÍTULO 6 | Catálogo de patrones


en diversas ubicaciones. Estas tareas se suelen implementar mediante secuencias de comandos u
otras soluciones de automatización, pero es posible que esto no elimine completamente los requisitos
administrativos adicionales.

●● Las fragmentaciones se puede localizar geográficamente para que los datos que contienen estén cerca
de las instancias de una aplicación que las utilice. Este enfoque puede mejorar considerablemente
el rendimiento, pero requiere una reflexión adicional para las tareas que deben acceder a múltiples
fragmentaciones en diferentes lugares.

Cuándo utilizar este patrón


Utilice este patrón cuando sea probable que un almacén deba escalarse hasta superar los recursos
disponibles de un nodo de almacenamiento único o bien, para mejorar su rendimiento mediante la
reducción de la contención en un almacén de datos.

El objetivo principal de la fragmentación es mejorar el rendimiento y la escalabilidad de un sistema, pero al


ser un subproducto también puede mejorar la disponibilidad por cómo se dividen los datos en particiones
individuales. Un fallo en una partición no impide necesariamente que una aplicación acceda a los datos
que se conservan en otras particiones y un operador puede llevar el mantenimiento o la recuperación de
una o más particiones sin bloquear el acceso a la totalidad de los datos de una aplicación. Para obtener
más información, consulte la Guía de partición de datos.

Ejemplo
El siguiente ejemplo en C# se sirve de un conjunto de bases de datos SQL Server que hacen las veces de
fragmentaciones. Cada base de datos contiene un subconjunto de los datos que utiliza una aplicación.
La aplicación recupera los datos que se distribuyeron a través de las fragmentaciones mediante su propia
lógica de fragmentación (este es un ejemplo de una consulta de divergencia). Los detalles de los datos
que se encuentra en cada fragmentación se devuelven a través de un método llamado GetShards. Este
método devuelve una lista enumerable de objetos ShardInformation, donde el tipo ShardInformation
contiene un identificador para cada fragmentación y la cadena de conexión de SQL Server que deben
utilizar las aplicaciones para conectarse a la fragmentación (las cadenas de conexión no aparecen en el
ejemplo de código).

private IEnumerable<ShardInformation> GetShards()


{
// This retrieves the connection information from a shard store
// (commonly a root database).
return new[]
{
new ShardInformation
{
Id = 1,
ConnectionString = ...
},
new ShardInformation
{
Id = 2,
ConnectionString = ...
}
};
}

El código siguiente muestra cómo utiliza la aplicación la lista de objetos ShardInformation para realizar
una consulta que recupera datos desde cada fragmentación en paralelo. No se presentan los detalles de
la consulta, sin embargo, en este ejemplo los datos que se recuperan contienen una cadena que podría
contener información como el nombre de un cliente si las fragmentaciones incluyen los detalles de los
clientes. Los resultados se agregan a una colección ConcurrentBag para que la aplicación los procese.

241 CAPÍTULO 6 | Catálogo de patrones


// Retrieve the shards as a ShardInformation[] instance.
var shards = GetShards();

var results = new ConcurrentBag<string>();

// Execute the query against each shard in the shard list.


// This list would typically be retrieved from configuration
// or from a root/master shard store.
Parallel.ForEach(shards, shard =>
{
// NOTE: Transient fault handling isn’t included,
// but should be incorporated when used in a real world application.
using (var con = new SqlConnection(shard.ConnectionString))
{
con.Open();
var cmd = new SqlCommand(“SELECT ... FROM ...”, con);

Trace.TraceInformation(“Executing command against shard: {0}”, shard.Id);

var reader = cmd.ExecuteReader();


// Read the results in to a thread-safe data structure.
while (reader.Read())
{
results.Add(reader.GetString(0));
}
}
});

Trace.TraceInformation(“Fanout query complete - Record Count: {0}”,


results.Count);

Guías y patrones relacionados


Los siguientes patrones y consejos también pueden ser relevantes al implementar este patrón:
●● Introducción a la coherencia de datos. Es posible que sea necesario mantener la coherencia de los
datos que están distribuidos en diversas fragmentaciones. Resume las cuestiones relacionadas con
el mantenimiento de la coherencia en datos distribuidos y describe las ventajas y concesiones de los
diversos modelos de coherencia.
●● Guía de partición de datos. La fragmentación de un almacén de datos puede introducir una serie
de nuevos problemas. Se describen estos problemas con respecto a los almacenes de datos de
fragmentación en el cloud con el fin de mejorar la escalabilidad, reducir la contención y optimizar
el rendimiento.
●● Patrón de tabla de índice. En ocasiones no es posible admitir consultas completamente a través
de tan solo el diseño de la clave de fragmentación. Permite que una aplicación recupere datos
rápidamente en un almacén de datos de gran tamaño mediante la especificación de una clave que
no sea la clave de fragmentación.
●● Patrón de vistas materializadas. Para mantener el rendimiento de algunas operaciones de consulta,
resulta útil crear vistas materializadas que agregan datos y los resume, especialmente si los datos del
resumen se basan en información que se distribuye por las diversas fragmentaciones. Describe cómo
generar y rellenar estas vistas.
●● Lecciones sobre fragmentación en el blog "Adding Simplicity" sobre cómo simplificar los procesos.
●● Fragmentación de bases de datos en el sitio web CodeFutures.
●● Introducción a las estrategias de escalabilidad: fragmentación de bases de datos en el blog de
Max Indelicato.
●● Generar bases de datos escalables: ventajas e inconvenientes de los diversos esquemas de
fragmentación de bases de datos en el blog de Dare Obasanjo.

242 CAPÍTULO 6 | Catálogo de patrones


Patrón de sidecar
Implementa los componentes de una aplicación en un proceso o contenedor separado para proporcionar
aislamiento y encapsulamiento. Este patrón también puede habilitar las aplicaciones para que las formen
componentes y tecnologías heterogéneos.

Este patrón se denomina sidecar porque se asemeja a los sidecares que están unidos a las motocicletas.
En el patrón, el sidecar se vincula a una aplicación principal y ofrece funciones complementarias para la
aplicación. El patrón sidecar también comparte el mismo ciclo de vida que la aplicación principal, además,
se crea y quita con ella. El patrón sidecar se denomina en ocasiones patrón compañero y es un patrón de
descomposición.

Contexto y problema
En muchas ocasiones las aplicaciones y los servicios requieren funcionalidad relacionada, como
servicios de supervisión, registro, configuración y conectividad a red. Estas tareas periféricas se pueden
implementar a modo de componentes o servicios independientes.

Si se integran correctamente en la aplicación, se pueden ejecutar en el mismo proceso que la aplicación,


de esta forma, los recursos compartidos se utilizan con eficacia. Sin embargo, esto también indica que
no están bien aislados y una interrupción en alguno de estos componentes puede afectar a otros o a
la aplicación en su totalidad. Por otra parte, se implementan normalmente en el mismo idioma que la
aplicación principal. Consecuentemente, el componente y la aplicación dependen estrechamente el uno
de la otra.

Si la aplicación se divide en servicios, cada uno de ellos se puede generar en diferentes idiomas y con
diversas tecnologías. Aunque esto favorezca la flexibilidad, también conlleva que cada componente
deba tener sus propias dependencias y bibliotecas de idioma específicas para acceder a la plataforma
subyacente y a cualquier recurso que se comparta con la aplicación principal. Además, la implementación
de estas características como diferentes servicios puede añadir latencia a la aplicación. La gestión del
código y las dependencias de estas interfaces de idioma específicas también pueden incrementar
considerablemente la complejidad, en particular para el hosting, la implementación y la administración.

Solución
Coubique un conjunto coherente de tareas junto con la aplicación principal, pero colóquelo dentro de su
propio proceso o contenedor. Esto crea una interfaz homogénea para los servicios de la plataforma en los
diversos idiomas.

243 CAPÍTULO 6 | Catálogo de patrones


Un servicio de sidecar no forma necesariamente parte de la aplicación, pero está conectado a ella. Va allí
donde vaya la aplicación principal. Los sidecares son procesos o servicios de respaldo que se implementan
con la aplicación principal. En una motocicleta, el sidecar va unido a ella y cada motocicleta tiene su propio
sidecar. De la misma manera, un servicio de sidecar comparte el destino de su aplicación principal. Para
cada instancia de la aplicación, se implementa una instancia del sidecar y se hospeda con dicha aplicación.

Entre las ventajas de usar un patrón de sidecar, se incluyen las siguientes:


●● Un sidecar es independiente de la aplicación principal en términos de entorno en tiempo de ejecución
y lenguaje de programación, por lo que no necesitará desarrollar un sidecar para cada idioma.

●● El sidecar puede tener acceso a los mismos recursos que la aplicación principal. Por ejemplo, un
sidecar puede supervisar los recursos del sistema que utiliza el mismo sidecar y la aplicación principal.

●● Dada su proximidad con la aplicación principal, no hay una latencia significativa al comunicarse entre sí.

●● Incluso puede utilizar un sidecar en aquellas aplicaciones que no ofrezcan un mecanismo de


extensibilidad para ampliar la funcionalidad mediante la colocación del sidecar en el mismo host
o contenedor secundario que la aplicación principal.

El patrón de sidecar se utiliza por lo general con contenedores y se denomina contenedor de sidecar
o contenedor compañero.

Problemas y consideraciones
●● Piense en la implementación y formato de embalaje que va a utilizar para implementar servicios,
procesos o contenedores. Los contenedores funcionan bastante bien con el patrón de sidecar.

●● Cuando se diseña un servicio de sidecar, tómese su tiempo en decidir el mecanismo de comunicación


entre los diferentes procesos. Trate de utilizar tecnologías que no dependan del idioma ni del marco
de trabajo, salvo que los requisitos de rendimiento no lo permitan.

●● Antes de agregar funcionalidad a un sidecar, piense si funcionaría mejor como servicio independiente
o como un daemon más tradicional.

●● Tenga también en cuenta si la funcionalidad podría implementarse a modo de biblioteca o mediante


un mecanismo tradicional de extensión. Es posible que las bibliotecas de idioma específicas presenten
un nivel de integración más profundo y sobrecarguen menos la red.

Cuándo utilizar este patrón


Puede utilizar este patrón en los siguientes casos:
●● La aplicación principal utiliza un conjunto heterogéneo de idiomas y marcos de trabajo. Las
aplicaciones que se escriben en diferentes idiomas y que usan diversos marcos de trabajo pueden
consumir los componentes ubicados en servicios de sidecar.

●● Los componentes pertenecen a un equipo remoto o a una organización diferente.

●● Es preciso que los componentes o las características estén coubicados en el mismo host que la aplicación.

●● Se necesita un servicio que comparta la totalidad del ciclo de vida de la aplicación principal, pero que
pueda actualizarse de forma independiente.

244 CAPÍTULO 6 | Catálogo de patrones


●● Es necesario disponer de un control pormenorizado de los límites de un recurso o componente
en particular. Por ejemplo, es posible que quiera restringir la cantidad de memoria que utiliza un
componente específico. Puede desplegar el componente como un sidecar y administrar el uso de
la memoria de forma independiente de la aplicación principal.

Este patrón puede no ser adecuado:


●● Cuando deba optimizarse la comunicación entre procesos. La comunicación entre una aplicación
principal y los servicios de sidecar presentan cierta sobrecarga, principalmente la latencia entre
llamadas. Probablemente esto no merezca la pena para los interfaces "locuaces".

●● En pequeñas aplicaciones donde los recursos invertidos en la implementación de un servicio de


sidecar para cada instancia no aporten nada al aislamiento.

●● Cuando el servicio deba escalarse de forma diferente o independientemente de las aplicaciones


principales. De ser así, es posible que sea mejor implementar la característica como un servicio
independiente.

Ejemplo
El patrón de sidecar se puede aplicar a muchas situaciones. Estos son algunos ejemplos comunes:
●● API de la infraestructura. El equipo de desarrollo de infraestructuras crea un servicio que se
implementa junto con cada aplicación, en lugar de una biblioteca cliente específica de idioma para
acceder a la infraestructura. El servicio se carga como un sidecar y proporciona una capa común
de servicios de infraestructura, que incluye servicios de registro, datos de entorno, almacén de
configuración, detección, comprobaciones de estado y vigilancia. El sidecar también supervisa el
entorno del host de la aplicación principal, además procesa (o usa un contenedor) y registra la
información en un servicio centralizado.

●● Administrar NGINX/HAProxy. Implemente NGINX con un servicio de sidecar que supervise el estado
del entorno, después actualice el archivo de configuración de NGINX y recicle el proceso cuando se
necesite un cambio de estado.

●● Sidecar embajador. Implemente un servicio embajador a modo de sidecar. La aplicación realiza las
llamadas a través del embajador. El embajador se encarga del registro de solicitudes, el enrutamiento,
la interrupción de circuitos y otras características relativas a la conectividad.

●● Proxy de descarga. Coloque un proxy de NGINX frente a una instancia de servicio node.js para
gestionar la entrega de contenido de archivos estáticos para el servicio.

Orientación relacionada
• Patrón embajador

245 CAPÍTULO 6 | Catálogo de patrones


Patrón de hosting de contenido estático
Implementa contenido estático en un servicio de almacenamiento en el cloud que puede enviarlo
directamente al cliente. Esto puede reducir la necesidad de utilizar instancias que consumen muchos
recursos informáticos.

Contexto y problema
Las aplicaciones web suelen incluir algunos elementos de contenido estático. Es posible que este contenido
estático incluya páginas HTML y otros recursos como imágenes y documentos que están disponibles para
clientes, bien porque forman parte de una página HTML (por ejemplo, imágenes online, hojas de estilo
y archivos de JavaScript en el cliente) o a modo descarga independiente (como documentos PDF).

Pese a que los servidores web están bien definidos para optimizar las solicitudes a través de la ejecución
eficiente de código de páginas dinámicas y el almacenamiento de resultados en la memoria caché, tienen
que gestionar peticiones para descargar contenido estático. Esto consume ciclos de procesamiento que en
la mayoría de los casos podrían utilizarse de forma más eficaz.

Solución
En la mayoría de los entornos de hosting en el cloud es posible reducir al mínimo la necesidad de utilizar
instancias de cálculo (por ejemplo, utilice una instancia más pequeña o menos instancias); para ello,
coloque algunos de los recursos de la aplicación y las páginas estáticas en un servicio de almacenamiento.
El consumo de recursos para el almacenamiento hospedado en el cloud es, por lo general, muy inferior
que para las instancias de cálculo.

Al hospedar algunas partes de una aplicación en un servicio de almacenamiento, las principales


consideraciones se refieren al despliegue de la aplicación y la protección de los recursos que no
van a estar disponibles para usuarios anónimos.

Problemas y consideraciones
Tenga en cuenta los puntos siguientes a la hora de decidir cómo implementar este patrón:
●● El servicio de almacenamiento hospedado debe exponer un extremo HTTP al que puedan obtener acceso
los usuarios para descargar recursos estáticos. Algunos servicios de almacenamiento admiten también
HTTPS, por lo que es posible hospedar recursos en servicios de almacenamiento que requieran SSL.
●● Para obtener un rendimiento y disponibilidad óptimos, piense en utilizar una red de distribución
de contenidos (CDN) para almacenar en memoria caché los contenidos del contenedor de
almacenamiento en múltiples centros de datos a lo largo y ancho del planeta. Sin embargo,
lo más probable es que los servicios de la CDN sean de pago.
Lo más habitual es que de forma predeterminada las cuentas de almacenamiento sean de replicación
geográfica con el fin de aportar resiliencia respecto a eventos que puedan afectar a los centros de
datos. Esto indica que la dirección IP puede cambiar, pero la dirección URL seguirá siendo la misma.
●● Cuando algunos de los contenidos están en una cuenta de almacenamiento y otros se hospedan en
una instancia de proceso, implementar y actualizar una aplicación se hace más complejo. Es posible
que tenga que efectuar implementaciones separadas y generar una versión de la aplicación y el
contenido para administrarla con mayor facilidad, especialmente cuando el contenido estático incluye
archivos de script o componentes de interfaz de usuario. Sin embargo, si solamente hay que actualizar
los recursos estáticos, bastaría con cargarlos en la cuenta de almacenamiento sin necesidad de volver
a desplegar el paquete de la aplicación.

246 CAPÍTULO 6 | Catálogo de patrones


●● Es posible que los servicios de almacenamiento no sean compatibles con el uso de nombres de
dominio personalizados. En tal caso, sería necesario especificar la dirección URL completa de
los recursos en los enlaces porque van a estar en un dominio diferente del contenido generado
dinámicamente que incluye los enlaces.
●● Los contenedores de almacenamiento se deben configurar para un acceso de lectura público, pero
es fundamental asegurarse de que no se hace para un acceso de escritura pública, al objeto de evitar
que los usuarios carguen contenidos. Se puede utilizar una llave de valet o un token para controlar el
acceso a los recursos que no deberían estar disponibles de forma anónima; consulte Patrón de llave
de valet para obtener más información.

Cuándo utilizar este patrón


Este patrón es útil para:
●● Reducir al mínimo el coste del hosting para los sitios web y aplicaciones que contengan algunos
recursos estáticos.
●● Reducir al mínimo el coste del hosting para sitios web solamente con contenidos y recursos estáticos.
En función de las capacidades del sistema de almacenamiento del proveedor de hosting, podría ser
posible hospedar un sitio web totalmente estático en una cuenta de almacenamiento.
●● Exponer recursos estáticos y contenido para aplicaciones que se ejecutan en otros entornos de
hosting o servidores locales.
●● Localizar contenido en más de una zona geográfica mediante una red de entrega de contenidos que
almacene en memoria caché los contenidos de la cuenta de almacenamiento en múltiples centros de
datos de todo el mundo.
●● Supervisar los costes y el uso del ancho de banda. Si se utiliza una cuenta de almacenamiento
independiente para una parte o la totalidad del contenido estático, los costes correspondientes
al hosting y al tiempo de ejecución se podrán dividir más fácilmente.

Es posible que este patrón no sea de utilidad en las siguientes situaciones:


●● La aplicación necesita procesar contenidos estáticos antes de entregarlos al cliente. Por ejemplo,
es posible que sea necesario agregar una marca de tiempo a un documento.
●● El volumen de contenido estático es muy pequeño. La sobrecarga que conlleva recuperar este
contenido en un almacenamiento independiente puede ser superior al coste-beneficio de separar
este almacenamiento de los recursos informáticos.

Ejemplo
Un explorador web puede obtener acceso directamente al contenido estático en Azure Blob Storage.
Azure ofrece una interfaz basada en HTTP sobre almacenamiento que puede exponerse públicamente
a los clientes. Por ejemplo, los contenidos en un contendor del almacenamiento Azure Blob se exponen
mediante una dirección URL con el siguiente formato:
http://[ nombre-cuenta-almacenamiento ].blob.core.windows.net/[ nombre-contenedor ]/[ nombre-archivo ]
Cuando se carga contenido es necesario crear uno o más contendores de blobs para conservar los
archivos y los documentos. Tenga en cuenta que los permisos predeterminados para los contenedores
nuevos son privados y deben cambiarse a públicos para que los clientes puedan tener acceso a los
contenidos. Si es necesario proteger el contenido de accesos anónimos, podrá implementar el Patrón de
llave de valet, por tanto, los usuarios deberán presentar un token válido para descargar los recursos.
En Conceptos de Blob service, se ofrece información acerca del almacenamiento de blob y las formas
de obtener acceso al mismo y utilizarlo.

247 CAPÍTULO 6 | Catálogo de patrones


Los enlaces de cada página especificarán la dirección URL del recurso y el cliente podrá obtener acceso
directamente desde el servicio de almacenamiento. La figura muestra la entrada de las porciones estáticas
de una aplicación directamente desde el servicio de almacenamiento.

Los enlaces en las páginas que se entregan al cliente deben especificar la dirección URL completa del
contenedor de blob y el recurso. Por ejemplo, una página que contenga un enlace a una imagen en un
contenedor público puede contener el siguiente código HTML.

<img src=”http://mystorageaccount.blob.core.windows.net/myresources/image1.png”
alt=”My image” />

Si los recursos están protegidos mediante una llave de valet, como una firma de acceso compartido de
Azure, esta firma se debe incluir en las direcciones URL de los enlaces.

En GitHub se encuentra disponible una solución llamada StaticContentHosting que utiliza almacenamiento
externo para recursos estáticos. El proyecto StaticContentHosting.Cloud contiene archivos de
configuración que especifican la cuenta de almacenamiento y el contendor que conserva el contenido
estático.

<Setting name=”StaticContent.StorageConnectionString”
value=”UseDevelopmentStorage=true” />
<Setting name=”StaticContent.Container” value=”static-content” />

La clase Settings del archivo Settings.cs del proyecto StaticContentHosting.Web contiene métodos para
extraer estos valores y generar un valor de cadena que contiene la dirección URL del contenedor de la
cuenta de almacenamiento en el cloud.

248 CAPÍTULO 6 | Catálogo de patrones


public class Settings
{
public static string StaticContentStorageConnectionString {
get
{
return RoleEnvironment.GetConfigurationSettingValue(
“StaticContent.StorageConnectionString”);
}
}

public static string StaticContentContainer


{
get
{
return RoleEnvironment.GetConfigurationSettingValue(“StaticContent.Container”);
}
}

public static string StaticContentBaseUrl


{
get
{
var account = CloudStorageAccount.Parse(StaticContentStorageConnectionString);

return string.Format(“{0}/{1}”, account.BlobEndpoint.ToString().TrimEnd(‘/’),


StaticContentContainer.TrimStart(‘/’));
}
}
}

La clase StaticContentUrlHtmlHelper del archivo StaticContentUrlHtmlHelper.cs expone un método


llamado StaticContentUrl que genera una dirección URL que contiene la ruta de acceso a la cuenta de
almacenamiento del cloud si la dirección URL que se pasó comienza con el carácter (~) de ruta raíz de
ASP.NET.

public static class StaticContentUrlHtmlHelper


{
public static string StaticContentUrl(this HtmlHelper helper, string contentPath)
{
if (contentPath.StartsWith(“~”))
{
contentPath = contentPath.Substring(1);
}

contentPath = string.Format(“{0}/{1}”, Settings.StaticContentBaseUrl.TrimEnd(‘/’),


contentPath.TrimStart(‘/’));

var url = new UrlHelper(helper.ViewContext.RequestContext);

return url.Content(contentPath);
}
}

El archivo Index.cshtml de la carpeta Views\Home contiene un elemento de imagen que utiliza el método
StaticContentUrl para crear la dirección URL para su atributo src.

249 CAPÍTULO 6 | Catálogo de patrones


<img src=”@Html.StaticContentUrl(“~/media/orderedList1.png”)” alt=”Test Image” />

Guías y patrones relacionados

●● En GitHub se ofrece un ejemplo donde se ilustra cómo utilizar este patrón.

●● Patrón de llave de valet. Si se parte de la base de que los recursos de destino no están disponibles
para los usuarios anónimos es necesario implementar una seguridad en el almacén que conserve el
contenido estático. Describe cómo utilizar un token o una llave que ofrezca a los clientes un acceso
restringido a un recurso o servicio específicos como los servicios de almacenamiento hospedados en
el cloud.

●● Un eficaz método de implementar un sitio web estático en Azure en el blog de Infosys.

●● Conceptos de Blob service

Patrón de estrangulador
Migra progresivamente un sistema heredado reemplazando gradualmente las partes específicas
de funcionalidades con nuevas aplicaciones y servicios. Como se están sustituyendo las características
de los sistemas heredados, el nuevo sistema terminará por sustituir las características del sistema anterior,
de forma que quede estrangulando y pueda retirarlo.

Contexto y problema
Según los sistemas van envejeciendo, las herramientas de desarrollo, las tecnologías de hosting e
incluso las arquitecturas del sistema donde estas se generaron caen en desuso. A medida que se van
sumando características y funcionalidades nuevas, la complejidad de estas aplicaciones puede aumentar
drásticamente, por lo que será más complicado mantener o añadir nuevas características.

Sustituir completamente a un sistema complejo puede ser una gran empresa. Lo habitual es realizar
una migración gradual a un nuevo sistema, a la vez que se mantiene el antiguo para gestionar las
características que no se migraron aún. Sin embargo, cuando se ejecutan dos versiones diferentes
de una aplicación, los clientes deben saber dónde se encuentran características particulares. Cada vez
que se migra una función o servicio, los clientes deben actualizarse para apuntar a la nueva ubicación.

Solución
Sustituir gradualmente porciones específicas de funcionalidad con las aplicaciones y los servicios nuevos.
Crear un muro que intercepte las peticiones dirigidas al sistema back-end heredado. Este muro dirigirá las
peticiones a la aplicación heredada o a los nuevos servicios. Las características existentes se pueden migrar
gradualmente al sistema nuevo y los consumidores podrán seguir utilizando la misma interfaz, sin saber
que se está llevando a cabo algún tipo de migración.

250 CAPÍTULO 6 | Catálogo de patrones


Este patrón ayuda a minimizar los riesgos que comporta la migración y a repartir los esfuerzos que se
invierten en el desarrollo a lo largo del tiempo. Con un muro que enrute con seguridad a los usuarios
hasta la aplicación correcta, puede agregar funcionalidad al sistema nuevo a cualquier ritmo que
desee, mientras que la aplicación heredada sigue funcionando. Con el tiempo, según se van migrando
características al sistema nuevo, el sistema heredado queda finalmente "estrangulado" y ya no resulta
necesario. Una vez finalizado este proceso, el sistema heredado se puede retirar con toda seguridad.

Problemas y consideraciones
●● Piense cómo gestionar los servicios y almacenes de datos que potencialmente puedan utilizar sistemas
nuevos y heredados. Asegúrese de que ambos pueden tener acceso a estos recursos en paralelo.

●● Estructure aplicaciones y servicios nuevos de forma que puedan interceptarse y reemplazarse


fácilmente en migraciones de estrangulación futuras.

●● Cuando se complete la migración, el muro de estrangulación desaparecerá o evolucionará para


convertirse en un adaptador para clientes heredados.

●● Asegúrese de que el muro sigue el ritmo de la migración.

●● Asegúrese de que el muro no se convierta en un punto único de fallos o un cuello de botella para
el rendimiento.

Cuándo utilizar este patrón

Utilice este patrón cuando vaya a migrar gradualmente una aplicación back-end a una arquitectura nueva.

Este patrón puede no ser adecuado:

●● Cuando no se puedan interceptar las solicitudes al sistema back-end.

●● Para sistemas más pequeños donde la complejidad de una sustitución integral es poca.

251 CAPÍTULO 6 | Catálogo de patrones


Orientación relacionada

●● Patrón de capa para evitar daños

●● Patrón de enrutamiento de puerta de enlace

Patrón de limitación
Controla el consumo de recursos que utiliza una instancia de una aplicación, un inquilino individual o un
servicio completo. De esta forma, el sistema sigue funcionando y cumple los acuerdos de nivel de servicio,
incluso cuando los aumentos de la demanda imponen una carga extrema en los recursos.

Contexto y problema
La carga en una aplicación en el cloud normalmente varía con el tiempo en función del número de
usuarios activos o los tipos de actividades que realizan. Por ejemplo, habrá más usuarios activos durante
las horas laborables o es posible que el sistema deba efectuar análisis de cálculo que consumen muchos
recursos al final de cada mes. También es posible que se produzca un aumento súbito e inesperado
en la actividad. Si los requisitos de procesamiento del sistema sobrepasan la capacidad de los recursos
disponibles, el rendimiento será insuficiente e incluso podría interrumpirse. Si el sistema tiene que cumplir
con un determinado nivel de servicio, una interrupción de tal naturaleza sería inaceptable.

Hay muchas estrategias disponibles para gestionar las diversas cargas en el cloud, en función de los
objetivos de negocio de la aplicación. Una estrategia es utilizar el escalado automático para hacer
corresponder los recursos aprovisionados con las necesidades del usuario en un momento determinado.
Con esto se puede satisfacer sistemáticamente la demanda del usuario, al mismo tiempo que se optimizan
los recursos invertidos en la ejecución. Sin embargo, aunque el escalado automático pueda desencadenar
el aprovisionamiento de recursos adicionales, este no será inmediato. Si la demanda crece rápidamente,
puede haber una ventana de tiempo donde se produzca un déficit de recursos.

Solución
Una estrategia alternativa al escalado automático es permitir que las aplicaciones utilicen los recursos
hasta un límite y restringirlos cuando se alcance este límite. El sistema debe supervisar cómo está
utilizando los recursos para que, cuando el uso exceda el umbral, pueda limitar las peticiones de uno o
más usuarios. Esto permitirá al sistema seguir funcionando y cumplir cualquier tipo de acuerdo de nivel de
servicio (SLA) en vigor. Para obtener más información sobre el control de uso de recursos, consulte la Guía
de telemetría e instrumentación.

El sistema podría implementar varias estrategias de limitación, entre las que se incluyen:

●● Rechazar solicitudes de un usuario individual que ya haya obtenido acceso a las API del sistema más
de un número definido de veces por segundo durante un período de tiempo determinado. Para esto
es necesario que el sistema mida el uso de los recursos de cada inquilino o usuario que ejecute una
aplicación. Para obtener más información, consulte la Guía de medición de servicios.

●● Deshabilitar o reducir la funcionalidad de determinados servicios que no son esenciales para que los
servicios esenciales puedan ejecutarse sin trabas con recursos suficientes. Por ejemplo, si la aplicación
está transmitiendo vídeo, puede cambiar a una resolución menor.

252 CAPÍTULO 6 | Catálogo de patrones


●● Usar la nivelación de carga para reducir el volumen de actividad (este método se explica con más
detalle en el Patrón de nivelación de carga basada en cola). En un entorno multiempresa, este
método reducirá el rendimiento de cada uno de los inquilinos. Si es preciso que el sistema admita una
combinación de inquilinos con SLA diferentes, el trabajo para inquilinos de alto valor podrá realizarse
inmediatamente. Las solicitudes para otros inquilinos pueden retenerse y gestionarse cuando haya
disminuido el trabajo pendiente. El patrón de la cola de prioridad podría utilizarse para ayudar
a implementar este método.

●● Aplazar las operaciones que se realizan en nombre de aplicaciones o inquilinos de menor prioridad.
Estas operaciones se pueden suspender o limitar y generar una excepción para informar al inquilino
de que el sistema está ocupado y que la operación se retomará más adelante.

La figura muestra un gráfico de área relativo al uso de los recursos (una combinación de memoria,
CPU, ancho de banda y otros factores) con respecto al tiempo dedicado a las aplicaciones que hacen
uso de tres características. Una característica es un área de funcionalidad, como un componente que
realiza un conjunto específico de tareas, una fragmentación de código que efectúa un cálculo complejo
o un elemento que ofrece un servicio como una memoria caché in-memory. Estas características están
etiquetadas como A, B y C.

La zona inmediatamente por debajo de la línea de una característica identifica los recursos que utilizan
las aplicaciones cuando invocan esta característica. Por ejemplo, el área por debajo de la línea de la
característica A muestra los recursos que consumen las aplicaciones que utilizan esta característica A, y el
área entre las líneas de las características A y B indica los recursos que utilizan las aplicaciones que invocan
la característica B. Al agregar áreas para cada característica, se muestra el uso total de recursos del sistema.

La figura anterior ilustra las consecuencias de aplazar operaciones. Justo antes del tiempo T1, el total de
recursos asignados a todas las aplicaciones que utilizan estas características alcanza un umbral (el límite
de uso de los recursos). Llegados a este punto, las aplicaciones corren el peligro de agotar los recursos
disponibles. En este sistema, la característica B no es tan crucial como las características A o C, por tanto se
deshabilita provisionalmente y se liberan los recursos que se estaban usando. Entre los tiempos T1

253 CAPÍTULO 6 | Catálogo de patrones


y T2, las aplicaciones que usan la característica A y C se siguen ejecutando normalmente. Finalmente, el
uso de recursos de estas dos características disminuye hasta el punto en que, en tiempo T2, hay capacidad
suficiente para habilitar de nuevo la característica B.

Los métodos de escalado automático y limitación también se pueden combinar para que las aplicaciones
conserven su capacidad de respuesta y cumplan los SLA. Si se espera que la demanda siga siendo alta,
el método de limitación ofrece una solución provisional mientras que el sistema se escala horizontalmente.
En este momento, se puede restablecer la funcionalidad completa del sistema.

La siguiente figura muestra un gráfico de área sobre el uso global de los recursos por parte de las
aplicaciones que se ejecutan en un sistema con respecto al tiempo e ilustra cómo se pueden combinar
los métodos de limitación y escalado automático.

En tiempo T1, se alcanza el umbral que especifica el límite suave de uso de los recursos. En estos
momentos, el sistema puede iniciar el escalado horizontal. Sin embargo, si los recursos nuevos no se
encuentran disponibles con la suficiente rapidez, es posible que se agoten los recursos existentes y se
produzcan errores en el sistema. Para evitar que esto ocurra, el sistema se limita provisionalmente según
se ha descrito anteriormente. Cuando se completa el escalado automático y los recursos adicionales se
encuentren disponibles, la limitación se puede moderar.

254 CAPÍTULO 6 | Catálogo de patrones


Problemas y consideraciones
Debe considerar los puntos siguientes a la hora de decidir cómo implementar este patrón:

●● La limitación de una aplicación, y la estrategia a utilizar, es una decisión arquitectónica que repercute
en la totalidad del diseño del sistema. El método de limitación debe plantearse en una fase temprana
del proceso de diseño de la aplicación porque una vez que se implementa el sistema no es fácil de
añadirlo.

●● El método de limitación se debe realizar rápidamente. Es preciso que el sistema sea capaz de detectar
un aumento en la actividad y reaccionar en consecuencia. El sistema también debe ser capaz de volver
a su estado original rápidamente después de que la carga haya disminuido. Esto requiere que los
datos de rendimiento apropiados se capturen y supervisen de forma continuada.

●● Si es necesario que un servicio deniegue provisionalmente la solicitud de un usuario, debe devolver


un código de error específico para que la aplicación cliente entienda que la razón del rechazo de
realizar una operación se debe a la limitación. La aplicación cliente puede esperar durante un período
de tiempo antes de volver a intentar la solicitud.

●● El método de limitación se puede utilizar como una medida provisional mientras que un sistema
realiza un escalado automático. En algunos casos es mejor simplemente aplicar la limitación antes
que escalar cuando haya una explosión en la actividad repentina y no se espera que dure demasiado,
porque un escalado puede incrementar considerablemente los costes de ejecución.

●● Si la limitación se utiliza como medida provisional mientras que un sistema se escala automáticamente
y si las demandas de recursos aumentan muy rápidamente, es posible que el sistema no pueda seguir
funcionando, incluso cuando opera en modo de limitación. Si esto no resulta aceptable, plantéese
conservar reservas mayores de capacidad y configurar un escalado automático más agresivo.

Cuándo utilizar este patrón


Utilice este patrón:
●● Para asegurarse de que un sistema sigue cumpliendo los acuerdos de nivel de servicio.

●● Para evitar que un inquilino monopolice los recursos que proporciona una aplicación.

●● Para gestionar los explosiones de actividad.

●● Para ayudar a optimizar los costes de un sistema mediante la limitación de los niveles máximos
de recursos necesarios con el fin de que siga funcionando.

Ejemplo
La cifra final ilustra cómo se puede implementar el método de limitación en un sistema multiempresa.
Los usuarios de cada una de las organizaciones inquilinas tienen acceso a una aplicación hospedada en el
cloud donde se completaron y enviaron encuestas. La aplicación contiene la instrumentación que controla
la tasa a la que estos usuarios presentan peticiones a la aplicación.

Al objeto de evitar que los usuarios de un inquilino repercutan en la capacidad de respuesta y la


disponibilidad de la aplicación para el resto de los usuarios, se limita el número de solicitudes por
segundo que pueden enviar los usuarios desde cualquier inquilino. La aplicación bloquea las solicitudes
que sobrepasan este límite.

255 CAPÍTULO 6 | Catálogo de patrones


Guías y patrones relacionados
Los siguientes patrones y consejos también pueden ser relevantes al implementar este patrón:

●● Guía de telemetría e instrumentación. El método de limitación depende de la recopilación de


información sobre la intensidad con la que se utiliza un servicio. Describe cómo generar y capturar
información personalizada de supervisión.

●● Guía de medición de servicios. Describe cómo medir los servicios con el fin de comprender cómo
se utilizan. Esta información puede ser útil para determinar cómo limitar los servicios.

●● Guía de escalado automático. El método de limitación se puede utilizar como medida provisional
mientras que un sistema se escala automáticamente o bien, para eliminar la necesidad de que un
sistema se escale automáticamente. Contiene información sobre estrategias de escalado automático.

●● Patrón de nivelación de carga basada en cola. La nivelación de cargas basada en cola es un


mecanismo utilizado frecuentemente para aplicar el método de limitación. Una cola puede actuar
como búfer que ayuda a homogeneizar la tasa a la que una aplicación envía solicitudes a un servicio.

●● Patrón de colas de prioridad. Un sistema puede utilizar las colas de prioridad como parte
de su estrategia de limitación, con el fin de mantener el rendimiento en aplicaciones fundamentales
o de más valor, mientras reduce el rendimiento de aplicaciones menos importantes.

Patrón de llave de valet


Utilice un token que ofrezca a los clientes un acceso directo restringido a recursos específicos para la
transferencia de datos de descarga desde la aplicación. Esto es especialmente útil en aplicaciones que
utilizan sistemas de almacenamiento hospedados en el cloud o colas y puede minimizar los costes y
maximizar la escalabilidad y el rendimiento.

256 CAPÍTULO 6 | Catálogo de patrones


Contexto y problema
Los programas y exploradores web cliente con frecuencia necesitan leer y escribir secuencias de archivos o
datos en la salida y entrada del almacenamiento de una aplicación. Por lo general, la aplicación se encargará
de gestionar la transferencia de datos, ya sea porque los recupera del almacenamiento y los transmite al
cliente o porque lee la secuencia cargada desde el cliente y la guarda en el almacén de datos. Sin embargo,
este enfoque consume recursos de gran valor como los cálculos, la memoria y el ancho de banda.
Los almacenes de datos tienen la capacidad de gestionar la carga y descarga de datos directamente, sin
necesidad de que la aplicación realice procesamiento alguno para transferir estos datos. Pero requiere
por lo general que el cliente tenga acceso a las credenciales de seguridad del almacén. Esto puede ser
una técnica útil para reducir al mínimo los costes de la transferencia de datos y el requisito de realizar un
escalado horizontal de la aplicación y maximizar el rendimiento. Esto indica, sin embargo, que la aplicación
ya no será capaz de gestionar la seguridad de los datos. Después de que el cliente establezca la conexión
al almacén de datos para su acceso directo, la aplicación no podrá actuar como gatekeeper. Ya no tendrá
el control del proceso y no podrá evitar cargas o descargas posteriores desde el almacén de datos.
Esto no es un enfoque realista cuando se trata de sistemas distribuidos que necesitan prestar servicio a
clientes no fiables. En su lugar, las aplicaciones deben ser capaces de controlar con seguridad el acceso
a datos de forma detallada y reducir la carga en el servidor al establecer esta conexión para después
permitir que el cliente se comunique directamente con el almacén de datos, al objeto de efectuar las
operaciones de lectura o escritura requeridas.

Solución
Necesita resolver el problema del control del acceso a los almacenes de datos en los que el almacén no
puede gestionar la autenticación y la autorización de clientes. Una solución típica consiste en restringir el
acceso a la conexión pública del almacén de datos y proporcionar al cliente una clave o token que pueda
validar el almacén de datos.
Esta clave o token se denomina por lo general llave de valet. Proporciona un acceso de tiempo limitado
a recursos específicos y solo permite realizar operaciones predefinidas tales como la lectura y la escritura
en el almacenamiento o las colas o bien, hacer cargas y descargas en un explorador web. Las aplicaciones
pueden crear y emitir llaves de valet a dispositivos de clientes y exploradores web de forma rápida y
sencilla, lo que permite a los clientes realizar las operaciones necesarias sin que la aplicación gestione
directamente la transferencia de datos. Esto elimina de la aplicación y el servidor la sobrecarga de
procesamiento, así como sus consecuencias en el rendimiento y el escalado.
El cliente utiliza este token para obtener acceso a recursos específicos en el almacén de datos durante solo
un período de tiempo específico y para aplicar restricciones en los permisos de acceso, tal como se muestra
en la figura. Después del período especificado, la llave quedará anulada y no permitirá el acceso al recurso.

257 CAPÍTULO 6 | Catálogo de patrones


También es posible configurar una clave con otras dependencias, tales como el ámbito de los datos. Por
ejemplo, en función de las funcionalidades del almacén de datos, la clave puede referirse específicamente
a una tabla completa en un almacén de datos o solo a filas concretas de una tabla. En los sistemas de
almacenamiento en el cloud, la clave puede referirse a un contenedor o a un elemento específico dentro
de un contenedor.

La aplicación también puede invalidar claves. Se trata de un método muy práctico si el cliente notifica al
servidor que la operación de transferencia de datos se ha completado. Posteriormente, el servidor puede
invalidar esa clave para evitar otras operaciones.

Si se utiliza este patrón, se simplifica la gestión del acceso a los recursos porque no es preciso crear y
autenticar a usuarios, conceder permisos y, después, volver a quitar al usuario. Asimismo, también es fácil
limitar la ubicación, el permiso y el periodo de validez, basta con generar una clave en tiempo de ejecución.
Los factores importantes son limitar el período de validez y, en particular, la ubicación del recurso en la
mayor medida de lo posible para que el receptor solo pueda utilizarlo para el propósito previsto.

Problemas y consideraciones

Tenga en cuenta los puntos siguientes a la hora de decidir cómo implementar este patrón:
Administrar el estado de validez y el período de la clave. Si se filtra o corre peligro, la clave
puede desbloquear eficazmente el elemento de destino y hace que pase a estar disponible para usos
malintencionados durante el periodo de validez. Normalmente, las claves se pueden revocar o deshabilitar,
en función de cómo se hayan emitido. Se pueden modificar las políticas del servidor o se puede invalidar
la clave del servidor con la que se firmó. Especifique un período de validez corto para reducir al mínimo
el riesgo de permitir que se produzcan operaciones no autorizadas en el almacén de datos. Sin embargo,
si el periodo de validez es demasiado corto, es posible que el cliente no pueda completar la operación
antes de que caduque la clave. Permita a los usuarios autorizados renovar la clave antes de que caduque
el periodo de validez si es necesario un acceso repetido a los recursos protegidos.

Controlar el nivel de acceso que proporcionará la clave. Por lo general, la clave debe permitir que
el usuario solamente pueda efectuar las acciones necesarias para completar la operación, tales como
un acceso de solo lectura si el cliente no va a poder cargar datos en el almacén de datos. Para la carga
de archivos, es habitual especificar una clave que proporcione permisos de solo escritura, así como la
ubicación y el periodo de validez. Es fundamental para especificar con precisión el recurso o el conjunto
de recursos a los que se aplica la clave.

Plantear cómo controlar el comportamiento de los usuarios. Al implementar este patrón se pierde
cierto control sobre los recursos a los que tienen acceso los usuarios. El nivel de control queda limitado
por las posibilidades de las políticas y permisos disponibles para el servicio o el almacén de datos de
destino. Por ejemplo, generalmente no es posible crear una clave que limite el tamaño de los datos que se
van a escribir en el almacenamiento o el número de veces que se puede utilizar la clave para tener acceso
a un archivo. Esto puede traducirse en costes inesperados muy elevados por la transferencia de datos,
incluso cuando el uso es por parte del cliente previsto, y es posible que lo haya provocado un error en el
código que desencadena operaciones repetidas de carga o descarga. Para limitar el número de veces que
se puede cargar un archivo, siempre que sea posible, fuerce que el cliente notifique a la aplicación cuándo
se ha completado una operación. Por ejemplo, algunos almacenes de datos generan eventos que puede
utilizar el código de la aplicación para supervisar las operaciones y controlar el comportamiento de los
usuarios. Sin embargo, es difícil hacer cumplir las cuotas para usuarios individuales en una situación de
multiempresa donde todos los usuarios en un inquilino utilizan la misma clave.

Valide y, opcionalmente, sanee todos los datos subidos. Los usuarios maliciosos que logren tener acceso
a la clave podrían cargar datos diseñados para poner en peligro el sistema. Asimismo, los usuarios autorizados
pueden cargar datos que no sean válidos y, cuando se procesen, podrían provocar errores o fallos en el
sistema. Al objeto de implementar una protección frente a estas situaciones, asegúrese de que todos los datos
cargados se validaron y que se haya comprado que no hubiera contenido malicioso antes de utilizarlos.

258 CAPÍTULO 6 | Catálogo de patrones


Auditar todas las operaciones. Muchos mecanismos basados en claves pueden registrar operaciones
como cargas, descargas y fallos. Estos registros generalmente pueden incorporarse a procesos de auditoría
y también se utilizan en facturación si se aplican cargos al usuario por el tamaño del archivo o el volumen
de datos. Utilice los registros para detectar fallos de autenticación que podrían deberse a problemas con
el proveedor de claves o a la eliminación accidental de una política de accesos almacenados.
Entregar claves de forma segura. Se puede incrustar en una dirección URL que active el usuario para una
página web o bien, se puede utilizar en una operación de redirección del servidor para que la descarga se
produzca automáticamente. Utilice siempre HTTPS para proporcionar la clave a través de un canal seguro.
Proteger datos confidenciales en tránsito. La entrega de datos confidenciales a través de la aplicación
se realiza generalmente mediante SSL o TLS, y esto se debe aplicar para que los clientes tengan acceso
directamente al almacén de datos.

Otras cuestiones que se deben tener en cuenta al implementar este patrón son:
●● Si el cliente no notifica o no puede notificar al servidor la finalización de la operación y el único límite
es el período de vencimiento de la clave, la aplicación no podrá realizar operaciones de auditoría
como contar el número de cargas o descargas o la prevención de múltiples cargas o descargas.
●● Es posible que se vea limitada la flexibilidad de las políticas de claves que se pueden generar. Por
ejemplo, algunos mecanismos solo permiten el uso de un período de vencimiento programado. Otros
no tienen capacidad para especificar permisos de lectura y escritura con los detalles suficientes.
●● Si se especifica la hora de inicio del periodo de validez de la clave o el token, asegúrese de que sea
ligeramente anterior a la hora en la que el servidor que se utiliza haya establecido como margen
para aquellos clientes que no estén sincronizados con precisión. El valor predeterminado, si no se
especifica, es normalmente la hora del servidor.
●● La dirección URL que contiene la clave se registrará en los archivos de registro del servidor. Aunque la
clave venza por lo general antes de que los archivos de registro se utilicen para su análisis, asegúrese
de limitar el acceso a los mismos. Si se transmiten datos de registro a un sistema de supervisión o se
almacenan en otra ubicación, plantéese aplicar un retardo para evitar fugas de claves hasta que haya
pasado su período de validez.
●● Si el código de cliente se ejecuta en un navegador web, es posible que el navegador tenga que ser
compatible con el uso compartido de recursos entre orígenes (CORS) para habilitar el código que se
ejecuta dentro en el explorador web, al objeto de tener acceso a datos en dominios diferentes al que
se utilizó para aprovisionar la página. Algunos exploradores antiguos y ciertos almacenes de datos
no admiten CORS, y es posible que el código que se ejecuta en estos exploradores pueda utilizar
una llave de valet para proporcionar acceso a datos en dominios diferentes, como una cuenta de
almacenamiento en el cloud.

Cuándo utilizar este patrón


Este patrón es muy práctico en las siguientes situaciones:
●● Para minimizar la carga de recursos y maximizar el rendimiento y la escalabilidad. No es necesario
que el recurso esté bloqueado para utilizar llaves de valet, así como tampoco es necesario invocar a
servidores, no existe un límite para el número de llaves de valet que se pueden emitir y se evita que
se produzca un único punto de error derivado de la transferencia de datos a través del código de la
aplicación. La generación de llaves de valet se efectúa por lo general mediante una sencilla operación
criptográfica de firma de una cadena con una clave.
●● Para reducir al mínimo los costes operativos. La habilitación de un acceso directo a los almacenes y las
colas no consume muchos recursos y los costes no son altos; además, puede reducir los viajes de ida y
vuelta a la red, y podría suponer una disminución del número de recursos de cálculo necesarios.
●● Cuando los clientes cargan o descargan datos con regularidad, en particular, cuando el volumen es
grande o cuando en cada operación hay archivos de gran tamaño.

259 CAPÍTULO 6 | Catálogo de patrones


●● Cuando los recursos de cálculo de la aplicación son limitados, ya sea por limitaciones de hosting
o consideraciones económicas. En esta situación, el patrón resultará más útil todavía si se producen
muchas cargas y descargas simultáneas porque alivia a la aplicación de tener que gestionar la
transferencia de datos.

●● Cuando los datos se almacenan en un almacén de datos remoto o un centro de datos diferente. Si la
aplicación tiene que actuar a modo de gatekeeper, podrían generarse cargos por el ancho de banda
adicional de la transferencia de datos entre centros de datos o bien, a través de redes públicas o
privadas entre el cliente y la aplicación y, posteriormente, entre la aplicación y el almacén de datos.

Este patrón podría no ser útil en las siguientes situaciones:

●● Si es preciso que la aplicación realice algún tipo de tarea en los datos antes de almacenarlos o antes
de enviarlos al cliente. Por ejemplo, si la aplicación necesita realizar la validación, obtener acceso a
los registros o ejecutar una transformación de los datos. Sin embargo, algunos clientes y almacenes
de datos pueden negociar y llevar a cabo transformaciones sencillas, como la compresión y la
descompresión (por ejemplo, un explorador web puede por lo general gestionar formatos GZip).

●● Si el diseño de una aplicación existente hace difícil incorporar el patrón. Al utilizar este patrón
normalmente es preciso utilizar un método arquitectónico diferente para la entrega y recepción de datos.

Ejemplo
Azure admite firmas de acceso compartidas en Azure Storage para controlar de forma pormenorizada el
acceso a datos en blobs, tablas y colas, y para temas y colas de Service Bus. Los tokens de firma de acceso
compartido se pueden configurar para proporcionar derechos de acceso específicos tales como lectura,
escritura, actualización y eliminación en tablas específicas, un intervalo de claves en una tabla, una cola, un
blob o un contenedor de blobs. La validez puede ser un período de tiempo determinado o no tener límite.

Las firmas para el acceso compartido de Azure también admiten políticas de acceso almacenadas en el
servidor que pueden estar asociadas a un recurso específico como una tabla o blob. Esta característica
ofrece un control adicional, así como flexibilidad en comparación con los tokens de firma de acceso
compartido generados por aplicaciones y debe utilizarse siempre que sea posible. Se puede cambiar la
configuración que se define en una política almacenada en el servidor y esto se refleja en el token sin
necesidad de emitir uno nuevo, pero las opciones definidas en el token no se pueden cambiar sin emitir
uno nuevo. Con este enfoque también se puede revocar un token de firma de acceso compartido válido
antes de que venza.

Para obtener más información, consulte Introducción a SAS (Shared Access Signature, firma de acceso
compartido) de tabla, SAS de cola y actualizar a SAS de blob y Usar firmas de acceso compartido en
MSDN.

El código siguiente muestra cómo crear un token de firma de acceso compartido que sea válido durante
cinco minutos. El método "GetSharedAccessReferenceForUpload" devuelve un token de firmas de acceso
compartido que se puede utilizar para cargar un archivo en Azure Blob Storage.

260 CAPÍTULO 6 | Catálogo de patrones


public class ValuesController : ApiController
{
private readonly CloudStorageAccount account;
private readonly string blobContainer;
...
/// <summary>
/// Return a limited access key that allows the caller to upload a file
/// to this specific destination for a defined period of time.
/// </summary>
private StorageEntitySas GetSharedAccessReferenceForUpload(string blobName)
{
var blobClient = this.account.CreateCloudBlobClient();
var container = blobClient.GetContainerReference(this.blobContainer);

var blob = container.GetBlockBlobReference(blobName);

var policy = new SharedAccessBlobPolicy


{
Permissions = SharedAccessBlobPermissions.Write,

// Specify a start time five minutes earlier to allow for client clock skew.
SharedAccessStartTime = DateTime.UtcNow.AddMinutes(-5),

// Specify a validity period of five minutes starting from now.


SharedAccessExpiryTime = DateTime.UtcNow.AddMinutes(5)
};

// Create the signature.


var sas = blob.GetSharedAccessSignature(policy);

return new StorageEntitySas


{
BlobUri = blob.Uri,
Credentials = sas,
Name = blobName
};
}

public struct StorageEntitySas


{
public string Credentials;
public Uri BlobUri;
public string Name;
}
}

La muestra completa se encuentra disponible en la solución de ValetKey que se puede descargar en


GitHub. El proyecto ValetKey.Web de esta solución contiene una aplicación web que incluye la clase
ValuesController de más arriba. En el proyecto ValetKey.Client, hay disponible una aplicación cliente de
ejemplo que utiliza esta aplicación web para recuperar claves de firma de acceso compartido y cargar
archivos en el almacenamiento de blobs.

261 CAPÍTULO 6 | Catálogo de patrones


Pasos siguientes
Los siguientes patrones y consejos también pueden ser relevantes al implementar este patrón:
●● En GitHub se ofrece un ejemplo donde se ilustra cómo utilizar este patrón.

●● Patrón de gatekeeper. Este patrón puede utilizarse junto con la llave de valet para proteger
aplicaciones y servicios mediante el uso de una instancia de host dedicada que actúa como
intermediaria entre clientes y la aplicación o el servicio. El gatekeeper valida y desinfecta las
solicitudes; además, pasa las peticiones y los datos entre el cliente y la aplicación. Esto puede
proporcionar una capa adicional de seguridad y reducir la superficie de ataque del sistema.

●● Patrón de hosting de contenido estático Describe cómo implementar recursos estáticos en un


servicio de almacenamiento basado en el cloud que puede entregar estos recursos directamente
al cliente con el fin de reducir el requisito de costosas instancias de cálculo. En los casos en que los
recursos no deban estar disponibles al público, se puede utilizar el patrón de llave de valet para
protegerlos.

●● Introducción a SAS (Shared Access Signature, firma de acceso compartido) de tabla, SAS de cola
y actualizar a SAS de blob

●● Utilizar firmas de acceso compartido

●● Autenticación de firmas de acceso compartido con el Bus de servicio

262 CAPÍTULO 6 | Catálogo de patrones


7

Listas de
comprobación de
revisión del diseño

263 CAPÍTULO 7 | Listas de comprobación de revisión del diseño


Lista de comprobación de DevOps
DevOps es la integración del desarrollo, el control de calidad y las operaciones de TI en una cultura
unificada y un conjunto de procesos para la entrega de software.

Utilice esta lista de comprobación como punto de partida para evaluar su cultura y procesos de DevOps.

Cultura
Garantizar la alineación del negocio en todas las organizaciones y equipos
●● Garantice que los equipos de negocio, desarrollo y operaciones estén todos alineados.

Garantizar que todo el equipo entiende el ciclo de vida del software


●● Asegúrese de que su equipo entiende el ciclo de vida de la aplicación y en qué parte del ciclo
de vida se encuentra la aplicación en esos momentos.

Reducir el tiempo del ciclo


●● Reduzca al máximo el tiempo que se invierte en pasar de las ideas a software desarrollado de
uso práctico.
●● Limite el tamaño y alcance de las versiones individuales para mantener bajo el peso de la prueba.
●● Automatice los procesos de compilación, pruebas, configuración y desarrollo siempre que sea
posible.
●● Elimine cualquier obstáculo en la comunicación entre desarrolladores y otros miembros del personal.

Revise y mejore los procesos.


●● Establezca revisiones periódicas de los actuales flujos de trabajo, procedimientos
y documentación, al objeto de mejorar de forma continuada.

Planificación proactiva.
●● Planifique de forma proactiva los posibles errores.
●● Implante procesos para identificar rápidamente los problemas cuando se produzcan.
●● Remítase a los miembros del equipo correctos para fijar y confirmar la solución.

Aprender del fracaso


●● Si se produce un error operativo, evalúe el problema, documente el motivo y la solución
y comparta las conclusiones.
●● Actualice los procesos de compilación para detectar automáticamente ese tipo de error en el futuro.

Optimizar la velocidad y la recopilación de datos


●● Trabaje en los incrementos más reducidos que pueda.
●● Trate las ideas nuevas como si fueran experimentos.
●● Instrumente los experimentos para poder recopilar los datos de producción con el fin de evaluar
su eficacia.
●● Esté preparado para un fracaso rápido si no se planteó la hipótesis correcta.

Tiempo para el aprendizaje


●● Antes de pasar a proyectos nuevos invierta el tiempo suficiente en recopilar conclusiones
relevantes y asegúrese de que el equipo las asimila.

264 CAPÍTULO 7 | Listas de comprobación de revisión del diseño


●● Conceda tiempo al equipo para desarrollar conocimientos, experimentar y aprender sobre
nuevas herramientas y técnicas.

Documentar operaciones
●● Documente todas las herramientas, los procesos y las tareas automatizadas con el mismo nivel
de calidad que su código de producto.
●● Documente el diseño actual y la arquitectura de los sistemas que admite, junto con los procesos
de recuperación y otros procedimientos de mantenimiento.
●● Céntrese en los pasos que emprende en la práctica y no en los procesos que teóricamente son
los mejores.
Revise y actualice periódicamente la documentación.
●● Respecto al código, asegúrese de que se incluyen comentarios significativos, especialmente
en las API públicas y utilice herramientas para generar automáticamente el código de la
documentación.

Compartir conocimiento
●● Asegúrese de que la documentación está organizada y se identifica con facilidad.
●● Utilice presentaciones informales, vídeos o boletines de noticias para compartir conocimientos.

Desarrollo
Ofrecer a los desarrolladores entornos similares a los de producción
●● Tenga los entornos de desarrollo y pruebas lo más cerca posible del entorno de producción.
●● Asegúrese de que los datos de las pruebas son coherentes con los datos que se utilizan en
producción, aunque se trate de datos de ejemplo y no de datos reales de producción (por
razones de privacidad o cumplimiento).
●● Planee generar y pasar a estado de anónimo los datos de pruebas de ejemplo.

Asegurarse de que todos los miembros del equipo autorizados pueden proporcionar una
infraestructura y desplegar la aplicación
●● Cualquier persona con los permisos adecuados tiene que poder crear o implementar recursos
similares a los de producción sin ir al equipo de operaciones.

Instrumentar la aplicación para obtener conocimiento


●● Incluya siempre instrumentación como requisito de diseño y compile la instrumentación
en la aplicación desde el principio.
●● La instrumentación debe incluir el registro de eventos para el análisis de la causa raíz, la
telemetría y las métricas con el fin de supervisar el estado y el uso generales de la aplicación.

Llevar el seguimiento de la deuda tecnológica


●● Lleve un seguimiento de cuándo los programas para los nuevos lanzamientos se pueden
establecer como prioritarios respecto a la calidad del código.
●● Documente los atajos u otras implementaciones que no sean óptimas y programe la revisión
de estos problemas en el futuro.

Transferir las actualizaciones directamente a producción


●● Para reducir el tiempo total del ciclo de lanzamiento, platéese transferir directamente el código
probado y confirmado a producción.
●● Utilice la alternancia de características para controlar las características que están habilitadas.

265 CAPÍTULO 7 | Listas de comprobación de revisión del diseño


Pruebas

Automatizar las pruebas


●● Automatice las tareas habituales de las pruebas e integre estas pruebas en sus procesos de
compilación.
●● Las pruebas de interfaces de usuario integradas también las debe realizar una herramienta
automatizada.

Pruebas para detectar errores


●● Las pruebas de integración de errores deben formar parte integrante de la revisión en los
entornos de pruebas y ensayos.
●● Cuando consolide los procesos y las prácticas de prueba, plantéese ejecutar estas pruebas
en el entorno de producción.

Pruebas en producción
●● Realice pruebas para garantizar que el código implementado funciona como se había previsto.
●● Para las implementaciones que no se actualicen con frecuencia, programe pruebas de
producción de forma periódica como parte del mantenimiento.

Automatizar las pruebas de rendimiento para identificar errores con antelación


●● Defina los objetivos de rendimiento aceptables para métricas como la latencia, los tiempos
de carga y el uso de los recursos.
●● Incluya pruebas de rendimiento automatizadas en el canal de lanzamiento para asegurarse
de que la aplicación cumple con esos objetivos.

Realizar pruebas de capacidad


●● Defina siempre límites máximos para la capacidad y el uso.
●● Asegúrese de que la aplicación puede gestionar esos límites, pero pruebe también lo que
sucedería cuando se sobrepasan dichos límites.
●● Las pruebas de capacidad deben realizarse de forma periódica.
●● Después del lanzamiento inicial, ejecute pruebas de rendimiento y capacidad cada vez que
se hagan actualizaciones al código de producción.
●● Utilice datos históricos para definir detalladamente las pruebas y determinar qué tipos de
pruebas se deben realizar.

Realizar pruebas automatizadas de penetración de seguridad


●● Realice pruebas de penetración como parte habitual de los procesos de compilación
e implementación.
●● Programe pruebas de seguridad periódicas y análisis de la vulnerabilidad en aplicaciones
implementadas, supervisión de puertos abiertos, extremos y ataques.

Realizar pruebas de continuidad del negocio automatizadas


●● Desarrolle pruebas de continuidad del negocio a gran escala, donde incluya la recuperación
de las copias de seguridad y la conmutación por error.
●● Defina procesos automatizados para realizar estas pruebas con regularidad.

Versión
Automatizar implementaciones
●● Automatice la implementación de la aplicación para probar los entornos de ensayo y de producción.

266 CAPÍTULO 7 | Listas de comprobación de revisión del diseño


Utilizar la integración continuada
●● Combine todo el código de programador en una base de código central periódicamente y,
después, efectúe de forma automática procesos de compilación y pruebas.
●● Ejecute el proceso de CI cada vez que se confirme o registre un código. Por lo menos una vez al día.
Considere la adopción de un modelo de desarrollo con base troncal.

Considere el uso de entrega continua


●● Asegúrese de que el código siempre esté listo para implementarse mediante la construcción,
las pruebas e implementación automáticas del código en entornos similares a los de producción.
●● La implementación continua es un proceso adicional que implementa automáticamente en la
producción cualquier actualización que haya pasado por el canal CI/CD.
●● Exige unas pruebas automáticas sólidas y un proceso de planificación avanzado.

Haga pequeños cambios incrementales


●● Mantenga pequeños los cambios.

Controle la exposición a los cambios


●● Utilice la alternancia de funciones para controlar cuándo se habilitan las características para los
usuarios finales.

Implemente estrategias de administración de versiones para reducir el riesgo de


implementación
●● Utilice estrategias como versiones de valores controlados o bien implementaciones azul
y verde para implementar actualizaciones en un subconjunto de usuarios.
●● Confirme que la actualización funciona según lo previsto y a continuación implemente
la actualización en el resto del sistema.

Documente todos los cambios


●● Mantenga siempre un registro claro de los cambios, aunque sean pequeños.
●● Registre todo lo que cambie, incluyendo los parches aplicados, los cambios de política y los
cambios en la configuración.
●● No incluya datos confidenciales en estos registros. Por ejemplo, registre la actualización de una
credencial y la persona que hizo el cambio, pero no registre las credenciales actualizadas.

Automatice las implementaciones


●● Automatice todas las implementaciones y disponga de sistemas para detectar problemas
durante la implementación.
●● Disponga de un proceso de mitigación para conservar el código y los datos existentes en
producción antes de que la actualización los sustituya en todas las instancias de producción.
●● Disponga de una forma automatizada de posponer correcciones o deshacer cambios.

Considere hacer que la infraestructura resulte inmutable


●● No debería modificar la infraestructura una vez que se haya implementado en la producción.

Supervisión
Haga que los sistemas se puedan observar
●● Establezca puntos de conexión de estado externos para supervisar el estado y garantizar que las
aplicaciones se codifiquen para instrumentar la métrica de las operaciones.
●● Utilice un esquema común y coherente que le permita correlacionar los eventos en varios sistemas.

267 CAPÍTULO 7 | Listas de comprobación de revisión del diseño


Agregue y correlacione registros y mediciones
●● Asegúrese de que los datos de telemetría y del registro se procesen y correlacionen en un
período de tiempo corto, para que el personal de operaciones tenga siempre una imagen
actualizada del estado del sistema.
●● Organice y presente los datos de formas que den una vista cohesiva de los problemas, para que,
en la medida de lo posible, quede claro cuándo los eventos están relacionados entre sí.
●● Consulte en su política de retención corporativa los requisitos de tratamiento de datos
y el tiempo que deben guardarse.

Implemente alertas y notificaciones automatizadas


●● Configure herramientas de control como Azure Monitor para detectar patrones o condiciones
que indiquen problemas potenciales o actuales y envíe alertas a los miembros del equipo que
pueden resolver los problemas.
●● Ajuste las alertas para evitar falsos positivos.

Controle la fecha de caducidad de activos y recursos


●● Asegúrese de seguir los activos que caduquen, su fecha de caducidad y los servicios o las
características que dependen de ellos.
●● Utilice procesos automatizados para controlar estos activos.
●● Notifique al equipo de operaciones antes de que expire un activo y remita el problema a una
instancia superior si la caducidad amenaza con interrumpir la aplicación.

Administración
Automatice las tareas de operaciones
●● Automatice los procesos de operaciones repetitivas siempre que sea posible para garantizar
la uniformidad de calidad y ejecución.
●● Al código que implementa la automatización se le debe aplicar la versión en el control de
código fuente.
Como con cualquier otro código, las herramientas de automatización deben probarse.

Aplique un enfoque de infraestructura como código al aprovisionamiento


●● Utilice scripts y plantillas de Azure Resource Manager.
●● Mantenga los scripts y las plantillas en el control de código fuente, como cualquier otro código
que mantenga.

Considere el uso de contenedores

Implemente resistencia y autorrecuperación


●● Instrumente sus aplicaciones para que los problemas se notifiquen de inmediato y usted puede
administrar las interrupciones u otros errores del sistema.

Tenga a mano un manual de operaciones


●● Disponga de un manual de operaciones o runbook para documentar los procedimientos
y la información de administración necesaria para que el personal de operaciones mantenga
un sistema.
●● Documente los escenarios de operaciones y planes de minimización que podrían entrar
en juego durante un error u otra interrupción de su servicio.
●● Cree esta documentación durante el proceso de desarrollo y manténgala siempre al día.
●● Revise, pruebe y mejore con regularidad.

268 CAPÍTULO 7 | Listas de comprobación de revisión del diseño


●● Aliente a los miembros del equipo a que aporten y compartan conocimientos.
●● Facilite el mantenimiento de documentos actualizados para que cualquier persona en el equipo
pueda ayudar a hacerlo.

Documente los procedimientos de guardia


●● Asegúrese de que las tareas, los horarios y los procedimientos de las guardias se documenten
y compartan con todos los miembros del equipo.
●● Mantenga actualizada esta información en todo momento.

Documente los procedimientos de remisión a una instancia superior para las dependencias
externas
●● Disponga de un plan para abordar las interrupciones si utiliza los servicios externos.
●● Cree documentación para los procesos de minimización planificados.
●● Incluya los contactos de soporte técnico y las vías de remisión a una instancia superior.

Utilice la administración de configuraciones


●● Planifique y registre los cambios de configuración.
●● Audite de forma periódica para garantizar que todo sucede según lo previsto.

Obtenga un plan de soporte técnico Azure para comprender el proceso


●● Determine el plan adecuado para sus necesidades y asegúrese de que todo el equipo sepa
cómo usarlo.
●● Los miembros del equipo deben entender los detalles del plan, cómo funciona el proceso
de apoyo y cómo abrir un ticket de soporte con Azure.
●● Si prevé un evento a gran escala, el soporte técnico de Azure le puede ayudar a aumentar
los límites de servicio.

Siga los principios de privilegio mínimo cuando otorgue acceso a los recursos.
●● Administre con prudencia el acceso a los recursos.
●● Solo conceda un acceso de usuario a lo que necesiten para completar sus tareas.

Utilice un control de acceso basado en roles.


●● Utilice la concesión de acceso Control de acceso basado en roles (RBAC) basado
en identidades y grupos de Azure Active Directory.

Utilice un sistema de seguimiento de errores para realizar el seguimiento de problemas.


●● Utilice una herramienta de seguimiento de errores para registrar detalles sobre los problemas,
asignar recursos para hacerles frente y proporcionar una pista de auditoría del progreso y el estado.

Administre todos los recursos en un sistema de administración de cambios.


●● Trate todos estos tipos de recursos como código durante todo el proceso de prueba/compilación/
revisión.

Utilice listas de comprobación.


●● Mantenga al día las listas de comprobación y siempre busque maneras de automatizar tareas
y optimizar procesos.

269 CAPÍTULO 7 | Listas de comprobación de revisión del diseño


Lista de comprobación de disponibilidad
La disponibilidad define la proporción de tiempo que el sistema es funcional y operativo. Revise los
elementos de esta lista de comprobación para mejorar la disponibilidad de su aplicación.

Evite cualquier único punto de error

●● Implemente todos los componentes, servicios y recursos, y calcule las instancias como varias
instancias.
●● Diseñe la aplicación de modo que se pueda configurar para utilizar varias instancias.
●● Diseñe la aplicación para que, de forma automática, detecte errores y redirija las solicitudes
a instancias sin error.

Descomponga la carga de trabajo por cada contrato de nivel de servicio diferente


●● Administre de manera diferente las cargas de trabajo críticas y las que no lo son.
●● Especifique las características del servicio y el número de instancias que debe atender
sus requisitos de disponibilidad.

Minimice y comprenda las dependencias de servicio

●● Minimice el número de servicios diferentes utilizados en la medida de lo posible.


●● Entienda las dependencias y el impacto de los errores o de un menor rendimiento
en cada uno de la aplicación.

Diseñe tareas y mensajes para que sean idempotentes (que se puedan repetir con seguridad)

●● Haga idempotentes a los consumidores de mensajes y las operaciones que llevan a cabo.
●● Detecte mensajes duplicados o bien utilice un enfoque optimista para gestionar conflictos.

Utilice un agente de mensajes de alta disponibilidad para las transacciones críticas

●● Utilice mensajería asincrónica que no bloquee al remitente mientras espera una respuesta.
●● Utilice un sistema de mensajería que proporcione alta disponibilidad y garantice semántica
por lo menos una vez.
●● Haga importante el procesamiento de mensajes (véase el punto anterior).

Diseñe aplicaciones que se degraden correctamente

●● Diseñe la aplicación para que se degrade correctamente de forma automática.


●● Cuando se alcancen los límites de recursos, adopte medidas adecuadas para reducir
al mínimo el impacto de disponibilidad reducida y conexiones erróneas al usuario.
●● Aplace las solicitudes a un subsistema con errores siempre que sea posible.

270 CAPÍTULO 7 | Listas de comprobación de revisión del diseño


Gestione correctamente los eventos en ráfaga rápida

●● Diseñe aplicaciones que gestionen cargas de trabajo variables, como los picos de primera hora
de la mañana o cuando se lanza un producto nuevo en un sitio de comercio electrónico.
●● Utilice el ajuste de escala automático siempre que sea posible.
●● Ponga en la cola las solicitudes de servicios y degrádelas correctamente cuando las colas casi
hayan completado su capacidad.
●● Asegúrese de que haya suficiente capacidad y rendimiento en condiciones no de ráfaga para
vaciar las colas y gestionar las solicitudes pendientes. Para obtener más información, consulte
Patrón de nivelación de carga basada en cola.

Implementación y mantenimiento

Implemente varias instancias de roles para cada servicio

●● Implemente por lo menos dos instancias de cada rol en el servicio. Esto permite que un rol no
esté disponible mientras el otro permanece activo.

Aloje aplicaciones en varias regiones

●● Aloje aplicaciones esenciales para el negocio en más de una región para ofrecer máxima
disponibilidad.

Automatice y pruebe las tareas de implementación y mantenimiento

●● Automatice la implementación mediante mecanismos comprobados como scripts y


●● Plantillas de Resource Manager.
●● Utilice técnicas automatizadas para llevar a cabo todas las actualizaciones de aplicaciones.
●● Pruebe completamente los procesos automatizados para garantizar que no haya errores.
●● Utilice restricciones de seguridad en las herramientas de automatización.
●● Defina y aplique políticas de implementación.

Considere el uso de las características de almacenamiento provisional y producción


de la plataforma

●● Azure App Service admite intercambiar entre los entornos de almacenamiento provisional
y producción sin que suponga tiempo de inactividad para las aplicaciones.
●● Si prefiere almacenar provisionalmente de forma local o bien implementar versiones diferentes
de la aplicación al mismo tiempo y migrar gradualmente los usuarios, es posible que no pueda
utilizar una operación de VIP Swap.

271 CAPÍTULO 7 | Listas de comprobación de revisión del diseño


Aplique cambios de configuración sin reciclaje

●● Los ajustes de configuración de una aplicación o un servicio Azure se pueden cambiar sin
necesidad de reiniciar el rol.
●● Diseñe una aplicación para que acepte cambios en los ajustes de configuración sin necesidad
de reiniciar toda la aplicación.

Utilice dominios de actualización para evitar tiempos de inactividad durante las actualizaciones

●● Especifique cuántos dominios de actualización deben crearse para un servicio cuando este
se implementa.

Nota

Los roles también se distribuyen entre dominios de error, cada uno de los cuales es bastante
independiente de otros dominios de error en cuanto a bastidor de servidor, alimentación y
suministro de refrigeración, a fin de reducir al mínimo la posibilidad de que un error afecte a todas
las instancias de rol. Esta distribución se produce automáticamente y no se puede controlar.

Configure conjuntos de disponibilidad para las máquinas virtuales Azure

●● Coloque dos o más máquinas virtuales en el mismo conjunto de disponibilidad para garantizar
que no se implementen en el mismo dominio de error.
●● Para potenciar al máximo la disponibilidad, cree varias instancias de cada máquina virtual crítica
utilizada por el sistema y colóquelas en el mismo conjunto de disponibilidad.
●● Si está ejecutando varias máquinas virtuales que atienden propósitos diferentes, cree un
conjunto de disponibilidad para cada máquina virtual.
●● Agregue las instancias de cada máquina virtual a cada conjunto de disponibilidad.

Administración de datos

Haga una réplica geográfica de datos en Azure Storage

●● Utilice el almacenamiento con redundancia geográfica (RA-GRS, por sus siglas en inglés) con
acceso de lectura para mayor disponibilidad.

Bases de datos con réplica geográfica

●● Utilice Azure SQL Database y Cosmos DB para obtener compatibilidad de réplica geográfica.
●● Configure réplicas de bases de datos secundarias en otras regiones.
●● Si hay una interrupción regional o no es posible conectarse a la base de datos principal,
conmute por error a la réplica secundaria.

Para obtener más información, consulte Cómo distribuir los datos globalmente con Azure
Cosmos DB.

272 CAPÍTULO 7 | Listas de comprobación de revisión del diseño


Utilice simultaneidad optimista y coherencia final

●● Utilice la partición para reducir al mínimo las posibilidades de que se produzcan actualizaciones
contradictorias.

Utilice la copia de seguridad periódica y la restauración a un momento dado

●● Asegúrese de que la copia de seguridad y la restauración cumplan el objetivo de punto


de recuperación (RPO).
●● De forma regular y automática, haga una copia de seguridad de los datos que no se conserven
en otros lugares.
●● Verifique si es posible restaurar con fiabilidad los datos y la propia aplicación en caso de
producirse algún error.
●● Haga que el proceso de copia de seguridad resulte seguro para proteger los datos en tránsito
y en almacenamiento.

Habilite la opción de alta disponibilidad para conservar una copia secundaria de una caché
de Azure Redis

●● Cuando utilice Azure Redis Cache, seleccione el nivel estándar o superior para conservar una
copia secundaria del contenido. Para obtener más información, consulte Crear una caché en
Azure Redis Cache.

Errores y fallos

Introduzca el concepto de tiempo de espera

●● Asegúrese de que los tiempos de espera que aplica sean adecuados para cada servicio o recurso
así como el cliente que está accediendo a ellos.
●● Quizás sea conveniente permitir un tiempo de espera más largo para una instancia determinada
de un cliente, en función del contexto y de otras acciones que esté realizando el cliente.

Reintente las operaciones con error producidas por fallos transitorios

●● Diseñe una estrategia de reintento para acceder a todos los servicios y recursos que no admitan
de forma inherente el reintento de conexión automático.
●● Utilice una estrategia que incluya un retraso creciente entre reintentos a medida que aumente el
número de errores.

Deje de enviar las solicitudes para evitar errores en cascada

●● En lugar de seguir reintentando una operación con pocas probabilidades de éxito, la aplicación
debe aceptar rápidamente que la operación ha fallado y gestionar correctamente este error.
●● Puede utilizar el modelo de interruptor de circuito para rechazar solicitudes de operaciones
específicas durante períodos definidos. Para obtener más información, consulte Patrón de
interruptor de circuito.

273 CAPÍTULO 7 | Listas de comprobación de revisión del diseño


Componga o recurra a varios componentes

●● Diseñe aplicaciones para que aprovechen varias instancias sin que ello afecte el funcionamiento
y las conexiones existentes en la medida de lo posible.
●● Utilice varias instancias y distribuya las solicitudes entre ellas, y detecte y evite enviar solicitudes
a las instancias con error, a fin de potenciar al máximo la disponibilidad.

Recurra a un servicio o flujo de trabajo diferente cuando sea posible

●● Proporcione un medio para reproducir las operaciones de escritura del almacenamiento de blobs
en SQL Database cuando el servicio esté disponible.
●● Detecte los errores y redirija las solicitudes a otros servicios que puedan ofrecer funciones
alternativas, o bien a instancias de copia de seguridad que puedan mantener las operaciones
fundamentales mientras el servicio principal esté desconectado.

Supervisión y recuperación ante desastres

Proporcione instrumentación enriquecida para posibles errores y eventos de errores

●● Para los errores posibles pero que aún no hayan ocurrido, proporcione los datos suficientes para
permitir que el personal de operaciones determinen la causa, subsanen la situación y garanticen
que el sistema permanezca disponible.
●● En el caso de errores que ya se hayan producido, la aplicación debe ofrecer un mensaje de error
al usuario pero intentar seguir operativa con funciones reducidas.
●● En todos los casos, el sistema de supervisión debe capturar datos completos para permitir una
recuperación rápida y modificar el sistema para evitar que la situación se repita.

Supervise el estado del sistema mediante la implementación de funciones de control

●● Implemente sondeos o funciones de comprobación que se ejecuten de forma periódica desde


fuera de la aplicación.

Con regularidad, pruebe todos los sistemas de conmutación por error y de reserva

●● Pruebe los sistemas de conmutación por error y de reserva antes de que se necesiten para
compensar un problema real durante el tiempo de ejecución.

Pruebe los sistemas de supervisión

●● Asegúrese de que la supervisión y la instrumentación funcionen correctamente.

274 CAPÍTULO 7 | Listas de comprobación de revisión del diseño


Siga el progreso de los flujos de trabajo de larga duración y reinténtelos en caso de producirse
algún error

●● Asegúrese de que cada paso sea independiente y puede reintentarse.


●● Supervise y administre el progreso de los flujos de trabajo de larga duración mediante
la implementación de un patrón como el Patrón programador agente Supervisor.

Plan de recuperación ante desastres

●● Cree un plan aceptado, probado por completo de recuperación ante cualquier tipo de error que
pueda afectar la disponibilidad del sistema.
●● Elija una arquitectura de recuperación ante desastres multisitio para las aplicaciones críticas para
una misión.
●● Identifique un dueño específico del plan de recuperación ante desastres, incluidas la
automatización y las pruebas.
●● Asegúrese de que el plan esté documentado adecuadamente y automatice el proceso tanto
como sea posible.
●● Cree una estrategia de copia de seguridad para todos los datos de referencia y transaccionales,
y pruebe con regularidad la restauración de estas copias de seguridad.
●● Imparta cursos al personal de operaciones para que pongan en práctica el plan y realicen
simulaciones de desastres de forma periódica con el fin de validarlo y mejorarlo.

275 CAPÍTULO 7 | Listas de comprobación de revisión del diseño


Lista de comprobación de escalabilidad
Diseño de servicios
Partición de la carga de trabajo

Diseñe algunas partes del proceso para que sean discretas y se puedan descomponer.
Reduzca al mínimo el tamaño de cada pieza, siguiendo las normas habituales de separación
de asuntos y el principio de responsabilidad única.

Diseño pensando en la escalabilidad

Diseñe aplicaciones para que reaccionen a cargas variables aumentando y disminuyendo el número
de instancias de roles, colas y otros servicios que utilicen.
Implemente la configuración o la autodetección de instancias conforme se agreguen y quiten, para que
el código de la aplicación pueda realizar el enrutamiento necesario.

Dimensionar como una unidad

Planifique recursos adicionales para permitir el crecimiento.


Por cada recurso, conozca los límites de escalado máximo y utilice particionamiento
o descomposición para ir más allá de estos límites.
Determine las unidades de escalado para el sistema en cuanto a conjuntos de recursos bien
definidos.
Diseñe la aplicación para que se amplíe fácilmente agregando una o más unidades de escalado.

Evite la afinidad del cliente

Siempre que sea posible, asegúrese de que la aplicación no exija afinidad.


Enrute las solicitudes a cualquier instancia y el número de instancias es irrelevante.

Aproveche las características de autoescalado de plataforma

Opte por una capacidad de autoescalado, como Azure Autoscale, para mecanismos personalizados
o externos, a menos que el mecanismo incorporado no atienda sus requisitos.
Utilice reglas de escalado programado siempre que sea posible para garantizar la disponibilidad de
recursos sin demora del arranque, pero utilice las reglas de autoescalado reactivo cuando sea necesario,
para hacer frente a cambios inesperados en la demanda.

276 CAPÍTULO 7 | Listas de comprobación de revisión del diseño


Descargue las tareas que hagan uso intensivo de la CPU/IO como tareas en segundo plano

Si está previsto que una solicitud a un servicio tarde mucho en ejecutarse o absorba una cantidad
considerable de recursos, descargue el procesamiento de esta solicitud en una tarea separada.
Utilice roles de trabajadores o trabajos en segundo plano (dependiendo de la plataforma de hosting)
para ejecutar estas tareas.

Distribuya la carga de trabajo de las tareas en segundo plano

Cuando haya muchas tareas en segundo plano, o las tareas exijan una cantidad considerable de
tiempo o recursos, distribuya el trabajo entre varias unidades de proceso (como roles de trabajador
o trabajos en segundo plano).

277 CAPÍTULO 7 | Listas de comprobación de revisión del diseño


Lista de comprobación de resistencia
Diseñar la aplicación para que ofrezca resistencia exige planificación y reducción de los diversos modos
de error que se pueden producir. Coteje los elementos de esta lista de comprobación con el diseño de la
aplicación para mejorar su resistencia.

Requisitos
Defina los requisitos de disponibilidad de su cliente

●● Obtenga el acuerdo de su cliente para los objetivos de disponibilidad de cada pieza de sus
aplicaciones. Para obtener más información, consulte Definición de los requisitos de resistencia.

Diseño de aplicaciones
Realice un análisis en modo de error (FMA) de su aplicación

●● Identifique los tipos de errores que puede experimentar una aplicación.


●● Capture los posibles efectos y el impacto de cada tipo de error en la aplicación.
●● Identifique estrategias de recuperación.

Implemente varias instancias de servicios

●● Disponga de varias instancias para mejorar la resistencia y la capacidad de ampliación.


●● Para Azure App Service, seleccione un Plan de servicios de aplicaciones que ofrezca varias
instancias.
●● Para Azure Cloud Services, configure cada uno de sus roles con varias instancias.
●● Para Azure Virtual Machines (VMs), asegúrese de que la arquitectura de VM incluya más de una
VM y que cada VM esté incluida en un conjunto de disponibilidad.

Utilice autoescalado para responder a los aumentos de carga

●● Configure la aplicación para que se amplíe automáticamente a medida que aumente la carga.

Utilice el equilibrio de cargas para distribuir solicitudes

●● Si la aplicación utiliza Azure VMs, disponga de un equilibrador de carga.

278 CAPÍTULO 7 | Listas de comprobación de revisión del diseño


Configure Azure Application Gateways para que utilice varias instancias

●● Disponga de más de una instancia mediana o de mayor tamaño de Application Gateway para
garantizar la disponibilidad del servicio en virtud de los términos del SLA.

Utilice conjuntos de disponibilidad para cada capa de aplicación

Considere implementar la aplicación en varias regiones

●● Utilice un patrón activo-activo (que distribuya solicitudes entre varias instancias activas) o un
patrón activo-pasivo (que mantenga una instancia en reserva, en caso de error de la instancia
principal).
●● Implemente varias instancias de los servicios de la aplicación en pares regionales.

Utilice Azure Traffic Manager para enrutar el tráfico de la aplicación a regiones diferentes

●● Especifique un método de enrutamiento de tráfico para su aplicación.

Configure y pruebe los sondeos de estado de los equilibradores de carga y administradores


de tráfico

●● Asegúrese de que la lógica de estado compruebe las partes críticas del sistema y responda
adecuadamente a los sondeos de mantenimiento.
●● Para un sondeo de Traffic Manager, el punto de conexión de estado debe comprobar las
dependencias críticas que se implementen en la misma región y cuyo fallo debería dar lugar
una conmutación por error a otra región.
●● Para un equilibrador de carga, el punto de conexión de estado debe notificar el estado de la
máquina virtual.
●● No incluya otras capas o servicios externos. · Para obtener orientación sobre la implementación
de un seguimiento de estado en la aplicación, consulte Patrón de supervisión de puntos de
conexión de mantenimiento.

Supervise los servicios externos

●● Si su aplicación tiene dependencias con servicios externos, identifique dónde y cómo puede
producir errores, y el efecto que estos tienen en su aplicación.
●● Registre sus invocaciones de supervisión y diagnóstico, y correlaciónelas con el registro de
estado y diagnóstico de la aplicación mediante un identificador exclusivo.

279 CAPÍTULO 7 | Listas de comprobación de revisión del diseño


Asegúrese de los servicios externos que consume ofrezcan un SLA

Implemente modelos de resistencia para operaciones remotas cuando corresponda

●● Si la aplicación depende de la comunicación entre servicios remotos, siga patrones de diseño


para afrontar errores transitorios, como el Patrón de reintento y el Patrón de interruptor de
circuito.

Implemente operaciones asincrónicas siempre que sea posible

●● Diseñe cada parte de la aplicación para permitir operaciones asincrónicas, siempre que sea posible.

Administración de datos

Comprenda los métodos de réplica de los orígenes de datos de la aplicación

●● Evalúe los métodos de réplica de cada tipo de almacenamiento en Azure, incluyendo Azure
Storage Replication y SQL Database Active Geo-Replication para garantizar que la aplicación
atienda los requisitos de datos.

Asegúrese de que ninguna cuenta de usuario tenga acceso a los datos de producción y de
copia de seguridad

●● Diseñe la aplicación para limitar los permisos de cuenta de usuario, de manera que solamente
los usuarios que requieran acceso de escritura lo tengan únicamente para producción o para
copia de seguridad, y no para ambos.

Documente y pruebe los procesos de conmutación por error y conmutación por recuperación

●● De forma regular, pruebe los pasos documentados para verificar si un operador que los esté
siguiendo puede conmutar correctamente el origen de datos tanto por error como
por recuperación.

Valide las copias de seguridad de datos

●● De forma regular, verifique si los datos de copia de seguridad son los previstos mediante
la ejecución de un script para validar la integridad de los datos, el esquema y las consultas.
●● Registre y notifique las incoherencias para se pueda reparar el servicio de copia de seguridad.

280 CAPÍTULO 7 | Listas de comprobación de revisión del diseño


Considere el uso de un tipo de cuenta de almacenamiento con redundancia geográfica

●● Elija una estrategia de réplica cuando se aprovisiona una cuenta de almacenamiento.


●● Seleccione Azure Read-Access Geo Redundant Storage (RA-GRS) para proteger los datos
de las aplicaciones frente al caso poco probable de que no se disponga de toda una región.
●● Para las VM, no dependa de la réplica RA GRS para restaurar los discos de las VM (archivos
VHD). En su lugar, utilice Azure Backup.

Seguridad

Implemente protección de las aplicaciones frente a los ataques de denegación de servicios


distribuidos (DDoS)

Implemente el principio de privilegio mínimo para acceder a los recursos de la aplicación

●● El valor predeterminado de acceso a los recursos de la aplicación debe ser lo más restrictivo
posible.
●● Conceda permisos de nivel superior previa aprobación.
●● Compruebe los permisos de privilegio mínimo de otros recursos que tengan sus propios
sistemas de permisos, como SQL Server.

Pruebas

Haga pruebas de la conmutación por error y por recuperación

●● Asegúrese de que los servicios dependientes de la aplicación se conmuten por error y por
recuperación en el orden correcto.

Realice pruebas de inserción de errores

●● Pruebe la aplicación en un entorno lo más cercano posible a la producción, mediante la


simulación o provocación de errores reales.
●● Verifique la capacidad de recuperación de la aplicación con respecto a todos los tipos de errores,
por sí solos y combinados.
●● Asegúrese de que los errores no se propaguen o distribuyan en cascada a todo el sistema.

Realice pruebas en producción con datos tanto sintéticos como de usuarios reales

●● Utilice una implementación azul/verde o de valores controlados, y pruebe la aplicación


en la producción.

281 CAPÍTULO 7 | Listas de comprobación de revisión del diseño


Implementación

Documente el proceso de lanzamiento de la aplicación

●● Defina y documente de manera clara el proceso de lanzamiento y asegúrese de que esté


a disposición de todo el equipo de operaciones.

Automatice el proceso de implementación de la aplicación

Diseñe el proceso de lanzamiento para potenciar al máximo la disponibilidad de la aplicación

●● Utilice la técnica de implementación azul/verde o de valores controlados para implementar


la aplicación en la producción.

Registre y audite las implementaciones de la aplicación

●● Implemente una estrategia de registro sólida para capturar tanta información específica de una
versión como sea posible.

Disponga de un plan de reversión para la implementación

●● Diseñe un proceso de reversión para volver a la última versión buena conocida y reducir
al mínimo el tiempo de inactividad.

Operaciones

Implemente procedimientos recomendados de supervisión y alerta en la aplicación

Mida las estadísticas de llamada remota y ponga la información a disposición del equipo
de la aplicación

●● Resuma la métrica, como latencia, rendimiento y errores, de las llamadas remotas en los
percentiles 95 y 99.
●● Realice un análisis estadístico de la métrica para revelar los errores que se producen dentro
de cada percentil.

282 CAPÍTULO 7 | Listas de comprobación de revisión del diseño


Siga el número de excepciones y reintentos transitorios durante un período de tiempo adecuado

Implemente un sistema de advertencia precoz que alerte a un operador

●● Identifique los indicadores de rendimiento clave del estado de la aplicación, como excepciones
transitorias y latencia de llamadas remotas, y establezca valores de umbral adecuados para cada
uno de ellos.
●● Envíe una alerta a las operaciones cuando se alcance el valor de umbral.
●● Establezca estos umbrales en niveles que permitan identificar problemas antes de que
se vuelvan críticos y exijan una respuesta de recuperación.

Asegúrese de que el equipo cuente con más de una persona capacitada para supervisar
la aplicación y realizar los pasos de recuperación manual

●● Imparta un curso de detección y recuperación a varias personas, y asegúrese de que por


lo menos una de ellas esté activa en cualquier momento.

Asegúrese de que la aplicación no alcance los límites de suscripción de Azure

●● Si los requisitos de la aplicación superan los límites de suscripción de Azure, cree otra suscripción
de Azure y dótela de recursos suficientes.

Asegúrese de que la aplicación no alcance los límites por servicio

●● Escale verticalmente (eligiendo, por ejemplo, otro plan de tarifa) o escale horizontalmente
(agregando instancias nuevas) para evitar los límites por servicio.

Diseñe los requisitos de almacenamiento de la aplicación para que se encuentren dentro de los
objetivos de capacidad de ampliación y rendimiento de Azure

●● Diseñe la aplicación para que utilice el almacenamiento dentro de esos objetivos.


●● Aprovisione cuentas de almacenamiento adicional si se superan los objetivos de almacenamiento.
●● Aprovisione suscripciones de Azure adicionales y, a continuación, aprovisione cuentas de
almacenamiento adicionales si se alcanza el límite de la cuenta de almacenamiento.

Determine si la carga de trabajo de la aplicación es estable o fluctúa en el tiempo

●● Si la carga de trabajo fluctúa en el tiempo, utilice los conjuntos de escalado de Azure VM para
establecer automáticamente el número de instancias de las VM.

Seleccione el nivel de servicio adecuado para Azure SQL Database

●● Si la aplicación utiliza Azure SQL Database, asegúrese de que haya seleccionado el nivel
de servicio adecuado.

283 CAPÍTULO 7 | Listas de comprobación de revisión del diseño


Cree un proceso para interactuar con el soporte técnico de Azure

●● Incluya un proceso de contacto con el soporte técnico y de remisión de problemas a una


instancia superior como parte de la resistencia de la aplicación desde el principio.

Asegúrese de que la aplicación no sobrepase el número máximo de cuentas de


almacenamiento por suscripción

●● Si la aplicación exige más de 200 cuentas de almacenamiento, deberá crear una suscripción y,
a su vez, crear cuentas de almacenamiento adicionales en ella.

Asegúrese de que la aplicación no sobrepase los objetivos de capacidad de ampliación de


los discos de la máquina virtual

●● Si la aplicación excede los objetivos de capacidad de ampliación de los discos de la máquina virtual,
aprovisione cuentas de almacenamiento adicionales y cree allí los discos de la máquina virtual.

Telemetría

Registre los datos de telemetría en el entorno de producción

●● Capture información de telemetría sólida mientras se ejecuta la aplicación en el entorno


de producción.

Implemente el registro mediante un patrón asincrónico

●● Asegúrese de que las operaciones de registro se implementen como operaciones asincrónicas.

Correlacione los datos de registro entre los límites de servicio

●● Asegúrese de que el sistema de registro correlacione las llamadas entre los límites de servicio,
para que pueda seguir la solicitud en toda la aplicación.

Recursos de Azure

Utilice las plantillas de Azure Resource Manager para aprovisionar recursos

Dé nombres significativos a los recursos

284 CAPÍTULO 7 | Listas de comprobación de revisión del diseño


Utilice un control de acceso basado en roles (RBAC, por sus siglas en inglés).

Utilice bloqueos de recursos para los recursos críticos, como las VM

Elija pares regionales

●● Cuando implemente en dos regiones, elija regiones del mismo par regional.

Organice grupos de recursos por función y ciclo de vida

●● Cree grupos de recursos diferentes para los entornos de producción, desarrollo y prueba.
●● En una implementación de varias regiones, coloque los recursos de cada región en grupos de
recursos diferentes. Esto facilita volver a implementar una región sin que ello afecte a las demás.

285 CAPÍTULO 7 | Listas de comprobación de revisión del diseño


Servicios de Azure
Los elementos siguientes de la lista de comprobación son válidos para
determinados servicios de Azure.

App Service

Utilice el nivel Estándar o Premium

Evite aumentar o reducir

●● Seleccione un tamaño de nivel e instancia que cumpla los requisitos de rendimiento para una carga
normal y, a continuación, amplíe las instancias para gestionar los cambios de volumen de tráfico.

Almacene la configuración como configuración de la aplicación

●● Utilice la configuración de la aplicación para almacenar los ajustes de configuración de la


aplicación.
Defina la configuración de las plantillas de Resource Manager, o mediante PowerShell, para que
pueda aplicarla como parte de un proceso automatizado de actualización/implementación.

Cree planes de App Service diferentes para los entornos de producción y de pruebas

●● No utilice ranuras en la implementación de producción para las pruebas.

Separe las aplicaciones web de las API web.

●● Si la solución tiene un front-end web y una API web, considere la posibilidad de descomponerlas
en aplicaciones de App Service diferentes.
●● Si inicialmente no necesita ese nivel de capacidad de ampliación, puede implementar las
aplicaciones en el mismo plan y moverlas a planes diferentes más adelante.

Evite utilizar la característica de copia de seguridad de App Service para hacer copias
de seguridad de bases de datos de Azure SQL

●● Utilice las copias de seguridad automatizadas de SQL Database.

Implemente en una ranura de ensayo

●● Cree una ranura de implementación para el almacenamiento provisional. Implemente las


actualizaciones de la aplicación en la ranura de ensayo y compruebe la implementación antes
de cambiarla a producción.

Cree una ranura de implementación para retener la última implementación buena conocida (LKG,
por sus siglas en inglés)

●● Al implementar una actualización en la producción, mueva la implementación de producción


anterior a la ranura LKG.

286 CAPÍTULO 7 | Listas de comprobación de revisión del diseño


Habilite el registro de diagnóstico

●● Incluya el registro de aplicación y el registro de servidor web.

Registre en almacenamiento de blobs

Cree una cuenta de almacenamiento diferente para los registros

●● No utilice la misma cuenta de almacenamiento para los registros y los datos de aplicaciones.

Supervise el rendimiento

●● Utilice un servicio de supervisión del rendimiento como New Relic o Application Insights para
supervisar el rendimiento y el comportamiento de una aplicación bajo carga.

Application Gateway

Aprovisione por lo menos dos instancias

●● Implemente Application Gateway con dos instancias por lo menos. Para poder optar al SLA,
debe proporcionar dos o más instancias medianas o grandes.

Búsqueda de Azure

Aprovisione más de una réplica

●● Utilice por lo menos dos réplicas para una alta disponibilidad de lectura, o tres para una alta
disponibilidad de lectura y escritura.

Configure los indizadores para implementaciones de varias regiones

●● Si el origen de datos se replica geográficamente, apunte cada indexador de cada servicio


Búsqueda de Azure regional a su réplica de origen de datos local.
●● En cambio, para los grandes conjuntos de datos almacenados en Azure SQL Database apunte
todos los indizadores a la réplica principal. Después de una conmutación por error, apunte los
indizadores de Búsqueda de Azure a la nueva réplica principal.
●● Si el origen de datos no se replica geográficamente, apunte varios indizadores en el mismo
origen de datos, para que los servicios de Búsqueda de Azure de varias regiones se indicen
de forma continua e independiente desde el origen de datos.

287 CAPÍTULO 7 | Listas de comprobación de revisión del diseño


Azure Storage

Para los datos de aplicaciones, utilice el almacenamiento con redundancia geográfica


de acceso de lectura (RA-GRS)

Para discos de VM, utilice Managed Disks

Para Queue Storage, cree una cola de copia de seguridad en otra región

●● Cree una cola de copia de seguridad en una cuenta de almacenamiento de otra región.

Cosmos DB

Replique la base de datos en distintas regiones.

SQL Database

Utilice el nivel Estándar o Premium

Habilite la auditoría de SQL Database

Utilice la réplica geográfica activa

●● Si la base de datos principal genera algún error, o es necesario desconectarla,


realice una conmutación por error manual a la base de datos secundaria.

Utilice particionamiento

Utilice la restauración a un momento dado para recuperarse de un error humano

Utilice la restauración geográfica para recuperarse de una interrupción del servicio

SQL Server (ejecutado en una máquina virtual)

Replique la base de datos

●● Utilice los grupos de disponibilidad Always On de SQL Server para replicar la base de datos.

Haga una copia de seguridad de la base de datos

●● Si ya utiliza Azure Backup para las copias de seguridad de las VM, considere el uso de
Azure Backup para las cargas de trabajo de SQL Server con DPM.
●● De lo contrario, utilice Copia de seguridad administrada de SQL Server en Microsoft Azure.

288 CAPÍTULO 7 | Listas de comprobación de revisión del diseño


Traffic Manager
Realice una conmutación por recuperación manual

●● Después de una conmutación por error de Traffic Manager, haga una conmutación por
recuperación manual, en lugar de conmutar por recuperación de forma automática.
●● Antes conmutar por recuperación, asegúrese de que todos los subsistemas de aplicación
estén en buen estado.

Cree un punto de conexión de sondeo de estado

●● Cree un punto de conexión personalizado que informe sobre el estado general de la aplicación.
●● Sin embargo, no informe acerca de errores de servicios que no sean críticos.

Máquinas virtuales
Evite una carga de trabajo de producción en una sola máquina virtual

●● Incluya varias VM en un conjunto de disponibilidad o conjunto de escalado de VM,


con un equilibrador de carga delante.

Especifique un conjunto de disponibilidad cuando aprovisiones la VM

●● Al agregar una máquina virtual nueva a un conjunto de disponibilidad existente, asegúrese


de crear un NIC para la VM y de agregar el NIC al grupo de direcciones de back-end del
equilibrador de carga.

Coloque cada nivel de aplicación en un conjunto de disponibilidad diferente

●● En una aplicación de n niveles, no coloque las VM de niveles diferentes en el mismo conjunto


de disponibilidad.
●● Para beneficiarse de la redundancia de FD y UD cada VM del conjunto de la disponibilidad debe
ser capaz de gestionar las mismas solicitudes de cliente.

Elija el tamaño de VM correcto en función de los requisitos de rendimiento

●● Al mover una carga de trabajo existente a Azure, comience por el tamaño de VM más
parecido al de los servidores locales.
●● A continuación, mida el rendimiento de la carga de trabajo real con respecto a la CPU,
la memoria y los IOPS del disco, y ajuste el tamaño si es necesario.
●● Si necesita varios NIC, tenga en cuenta el límite de NIC por cada tamaño.

Utilice Managed Disks para los discos duros virtuales

Instale aplicaciones en un disco de datos, no el disco de sistema operativo

Utilice Azure Backup para hacer copias de seguridad de las VM

289 CAPÍTULO 7 | Listas de comprobación de revisión del diseño


Habilite los registros de diagnóstico

●● Incluya las métricas básicas de estado, registros de infraestructura y diagnóstico de arranque.

Utilice la extensión AzureLogCollector

Para incluir direcciones IP públicas en la lista blanca o bien bloquearlas, agregue un NSG
a la subred

●● Bloquee el acceso de usuarios malintencionados o bien permita el acceso solo a usuarios con
privilegio para acceder a la aplicación.

Cree un sondeo de estado personalizado

●● Para un sondeo de HTTP, utilice un punto de conexión personalizado que informe acerca del
estado general de la aplicación, incluyendo todas las dependencias críticas.

No bloquee el sondeo de estado

●● No bloquee el tráfico hacia o desde esta IP en ninguna política de firewall o regla de grupo
de seguridad de red (NSG, por sus siglas en inglés).

Habilite el registro de equilibrador de carga

290 CAPÍTULO 7 | Listas de comprobación de revisión del diseño


8

Resumen
En esta guía ha aprendido cómo elegir el estilo de arquitectura adecuado
para la aplicación y las tecnologías de cálculo y almacenamiento de datos
más adecuadas, y a aplicar los principios y los pilares de diseño a la hora
de desarrollar sus aplicaciones.

En el futuro, las nuevas tendencias, demandas de usuarios y capacidades seguirán creando incluso más
oportunidades de mejorar las arquitecturas. Para jugar con ventaja, le animamos a mantenerse al tanto
de los recursos y la orientación siguientes:

● Agregue Architecture Center como favorito en aka.ms/architecturecenter.


● Visite Azure Documentation Center para obtener guías detalladas, guías rápidas y descargas.
● Obtenga cursos Azure gratuitos online, incluyendo Pluralsight y rutas de aprendizaje guiadas.

Comience a construir con una cuenta


gratuita de Azure Obtenga
Si aún no lo ha hecho, abra una cuenta de Azure gratuita para beneficiarse ayuda de los
de muchas ventajas, incluyendo:
• Un crédito de 200 dólares para utilizar con cualquier producto de
expertos
Azure durante 30 días.
Póngase en contacto
• Acceso gratuito durante 12 meses a la mayoría de nuestros
productos más populares de distintas categorías, como proceso, con nosotros en
almacenamiento, redes y bases de datos. aka.ms/azurespecialist
• Más de 25 productos que siempre son gratuitos.

291
9

Arquitecturas de
referencia de Azure
Nuestras arquitecturas de referencia se organizan por escenario, con
arquitecturas relacionadas agrupadas. Cada arquitectura incluye prácticas
recomendadas, junto con consideraciones de capacidad de ampliación,
disponibilidad, facilidad de gestión y seguridad. Además, casi todas
incluyen una solución implementable.

Administración de identidades ….................................................................................................... 293

Red híbrida …......................................................................................…........................................… 298

Red DMZ ….....................................................................................................................................… 303


Aplicación web administrada …............................................................…....................................... 306
Ejecución de cargas de trabajo en máquina virtual de Linux …...............................................… 310
Ejecución de cargas de trabajo en máquina virtual de Windows …............................................ 315

292 CAPÍTULO 8 | Arquitecturas de referencia de Azure


Administración
de identidades
Estas arquitecturas de referencia muestran opciones para la integración
del entorno Active Directory (AD) local con una red Azure.

For this scenario Consider this architecture

Una parte de la aplicación se aloja de Integre AD local Azure AD


forma local y otra parte en Azure.

Necesita utilizar las funciones de Amplíe AD DS a Azure


AD DS que no estén implementadas
actualmente por Azure AD.

Debe mantener la separación de Cree un bosque de AD DS en Azure


seguridad para objetos e identidades que
se conserven en el cloud, o bien migrar
dominios individuales locales al cloud.

Debe: Ampliar AD FS a Azure

• Autenticar y autorizar usuarios


de organizaciones asociadas.
• Permitir a los usuarios autenticarse
desde la web
navegadores que se ejecuten fuera del
firewall de la organización.
• Permitir a los usuarios conectarse desde
dispositivos externos autorizados, como
los dispositivos móviles.

293 CAPÍTULO 8 | Arquitecturas de referencia de Azure


Azure
Integrate on-premises Active Directory domains with Azure Active Directory Architecture Components
This architecture integrates Azure Active Directory (Azure AD) with an on-premises Active Directory domain. Your on-premises AD directories are replicated to Azure AD. This allows your
applications that run in Azure to authenticate users with their on-premises identities, and enables a single sign-on (SSO) experience. Azure AD tenant
An instance of Azure AD created by your organization. It acts
as a directory service for cloud applications by storing
objects copied from the on-premises Active Directory and
provides identity services.
Web tier subnet
Virtual network This subnet holds VMs that run a web application. Azure AD
can act as an identity broker for this application.
WEB TIER
AVAILABLITY
MANAGEMENT SUBNET On-premise AD DS server
SET
Domain Azure AD An on-premise directory and identity service. The AD DS
Controller Connect directory can be synchronized with Azure AD to enable it to
Sync authenticate on-premise users.
NSG
NSG VM VM
Jumpbox Azure AD Connect sync server
Domain
An on-premises computer that runs the Azure AD Connect
Controller
VM sync service. This service synchronizes information held in
PIP the on-premises Active Directory to Azure AD. For example,
if you provision or deprovision groups and users
on-premises, these changes propagate to Azure AD.
VM
PIP
Note:
For simplicity, this diagram only shows the connections
Authenticated request directly related to Azure AD, and does not show
protocol-related traffic that may occur as part of
AD synchronization authentication and identity federation. For example, a web
application may redirect the web browser to authenticate the
request through Azure AD. Once authenticated, the request
Requests from on-prem users Azure Active
can be passed back to the web application, with the
Directory
appropriate identity information.
Requests from external users tenant
Recommendations
• If you have multiple on-premises domains in a forest, store and synchronize information for the entire forest to a single Azure AD tenant. Filter identities to avoid
duplication.
• To achieve high availability for the AD Connect sync service, run a secondary staging server.

294
• If you are likely to synchronize more than 100,000 objects from your local directory, use a production version of SQL Server, and use SQL clustering to achieve high
availability.
• Protect on-premises applications that can be accessed externally. Use the Azure AD Application Proxy to provide controlled access to on-premises web applications for
external users.
• Actively monitor Azure AD for signs of suspicious activity.
• Use conditional access control to deny authentication requests from unexpected sources.
Azure
Extend Active Directory Domain Services (AD DS) to Azure Architecture Components
This architecture extends an on-premises Active Directory environment to Azure using Active Directory Domain Services (AD DS). This architecture can reduce the latency caused by sending
authentication and local authorization requests from the cloud back to AD DS running on-premises. Consider this option if you need to use AD DS features that are not currently On-premises network
implemented by Azure AD.
The on-premises network includes local Active Directory
servers that can perform authentication and authorization
for components located on-premises.
On-premises nework Virtual network
Active Directory servers
These are domain controllers implementing directory
GATEWAY SUBNET services (AD DS) running as VMs in the cloud. These servers
UDR PRIVATE DMZ IN AVAILABLITY PRIVATE DMZ OUT can provide authentication of components running in your
SET Azure virtual network.
AD Server AD Server
NIC

NIC

contoso�com contoso�com
NSG NVA Active Directory subnet
The AD DS servers are hosted in a separate subnet. Network
NSG security group (NSG) rules protect the AD DS servers and
NIC

NIC

MANAGEMENT SUBNET provide a firewall against traffic from unexpected sources.


Gateway NVA
NSG Internal Load
Balancer Azure Gateway and Active
Jump Box
Directory synchronization
The Azure gateway provides a connection between the
Web app request on-premises network and the Azure VNet. This can be a VPN
WEB TIER
AVAILABLITY PUBLIC DMZ IN AVAILABLITY PUBLIC DMZ OUT AD DS SUBNET
connection or Azure ExpressRoute. All synchronization
AVAILABLITY
Authentication request SET SET (CONTOSO�COM)
SET requests between the Active Directory servers in the cloud
NIC and on-premises pass through the gateway. User-defined

NIC
NSG
NVA
routes (UDRs) handle routing for on-premises traffic that
VM passes to Azure. Traffic to and from the Active Directory
NSG NSG VM servers does not pass through the network virtual appliances
(NVAs) used in this scenario.
NSG

NIC

NIC
VM
NVA
VM VM
PIP
Recommendations
• Deploy at least two VMs running AD DS as domain controllers and add them to an availability set.
• For VM size, use the on-premises AD DS machines as a starting point, and pick the closest Azure VM sizes.
• Create a separate virtual data disk for storing the database, logs, and SYSVOL for Active Directory.
• Configure the VM network interface (NIC) for each AD DS server with a static private IP address for full domain name service (DNS) support.
• Monitor the resources of the domain controller VMs as well as the AD DS Services and create a plan to quickly correct any problems.

295
• Perform regular AD DS backups. Don't simply copy the VHD files of domain controllers, because the AD DS database file on the VHD may not be in a consistent state
when it's copied.
• Do not shut down a domain controller VM using Azure portal. Instead, shut down and restart from the guest operating system.
• Use either BitLocker or Azure disk encryption to encrypt the disk hosting the AD DS database.
• Azure disk encryption to encrypt the disk hosting the AD DS database.
Azure
Create an Active Directory Domain Services (AD DS) resource forest in Azure Architecture Components
This architecture shows an AD DS forest in Azure with a one-way trust relationship with an on-premises AD domain. The forest in Azure contains a domain that does not exist on-premises.
This architecture maintains security separation for objects and identities held in the cloud, while allowing on-premises identities to access your applications running in Azure. On-premises network
The on-premises network contains its own Active Directory
forest and domains.
Active Directory servers
These are domain controllers implementing domain services
running as VMs in the cloud. These servers host a forest
containing one or more domains, separate from those
located on-premises.
On-premises nework Virtual network One-way trust relationship
The example in the diagram shows a one-way trust from the
domain in Azure to the on-premises domain. This
GATEWAY SUBNET
relationship enables on-premises users to access resources
UDR PRIVATE DMZ IN AVAILABLITY PRIVATE DMZ OUT in the domain in Azure, but not the other way around. It is
SET
possible to create a two-way trust if cloud users also require
AD Server AD Server access to on-premises resources.
NIC

NIC

contoso�com contoso�com
NSG NVA
Active Directory subnet
The AD DS servers are hosted in a separate subnet. Network
NSG
security group (NSG) rules protect the AD DS servers and
NIC

NIC
MANAGEMENT SUBNET
Gateway NVA provide a firewall against traffic from unexpected sources.
NSG Internal Load
Balancer Azure gateway
Jump Box
The Azure gateway provides a connection between the
on-premises network and the Azure VNet. This can be a VPN
Web app request connection or Azure ExpressRoute. For more information,
WEB TIER
AVAILABLITY PUBLIC DMZ IN AVAILABLITY PUBLIC DMZ OUT AD DS SUBNET see Implementing a secure hybrid network architecture in
Authentication request SET (CONTOSO�COM)
AVAILABLITY
SET SET Azure.
ADDS trust relationship

NIC

NIC
NSG
NVA
VM
NSG NSG VM
NSG

NIC

NIC
VM
NVA
VM VM
PIP

296
Recommendations
• Provision at least two domain controllers for each domain. This enables automatic replication between servers.
• Create an availability set for the VMs acting as Active Directory servers handling each domain. Put at least two servers in this availability set.
• Consider designating one or more servers in each domain as standby operations masters in case connectivity to a server acting as a flexible single master operation
(FSMO) role fails.
Azure
Active Directory Federation Services (AD FS) Architecture Components
This architecture extends an on-premises network to Azure and uses Active Directory Federation Services (AD FS) to perform federated authentication and authorization. AD FS can be hosted on-premises,
but for applications running in Azure, it may be more efficient to replicate AD FS in the cloud.
Use this architecture to authenticate users from partner organizations, allow users to authenticate from outside of the organizational firewall, or allow users to connect from authorized mobile devices.
AD DS subnet
The AD DS servers are contained in their own subnet with network
security group (NSG) rules acting as a firewall.
On-premises nework Virtual network
AD DS servers
Domain controllers running as VMs in Azure. These servers provide
GATEWAY SUBNET PRIVATE DMZ IN AVAILABLITY PRIVATE DMZ OUT authentication of local identities within the domain.
MANAGEMENT SUBNET SET
UDR
Gateway AD FS subnet
NIC

NSG NVA NIC


NSG The AD FS servers are located within their own subnet with NSG rules
NSG acting as a firewall.
Jump Box
NIC

NIC
Web app request NVA AD FS servers
Authentication request Internal Load The AD FS servers provide federated authorization and authentication.
Balancer In this architecture, they perform the following tasks:
Federated authentication
request Receiving security tokens containing claims made by a partner
Partner network
federation server on behalf of a partner user. AD FS verifies that
WEB TIER
AVAILABLITY the tokens are valid before passing the claims to the web applica-
SET tion running in Azure to authorize requests.
The web application running in Azure is the relying party. The
NSG partner federation server must issue claims that are understood
Federation
VM by the web application. The partner federation servers are
server
referred to as account partners, because they submit access
requests on behalf of authenticated accounts in the partner
VM organization. The AD FS servers are called resource partners
because they provide access to resources (the web application).
VM
Authenticating and authorizing incoming requests from external
users running a web browser or device that needs access to web
applications, by using AD DS and the Active Directory Device
Registration Service.
PUBLIC DMZ IN AVAILABLITY PUBLIC DMZ OUT
PIP
SET The AD FS servers are configured as a farm accessed through an Azure
load balancer. This implementation improves availability and scalabili -
ty. The AD FS servers are not exposed directly to the Internet. All

NIC

NIC
NSG
NVA Internet traffic is filtered through AD FS web application proxy servers
NSG and a DMZ (also referred to as a perimeter network).

NIC

NIC
NVA AD FS proxy subnet
The AD FS proxy servers can be contained within their own subnet,
PIP with NSG rules providing protection. The servers in this subnet are
exposed to the Internet through a set of network virtual appliances
that provide a firewall between your Azure virtual network and the
Internet.
Recommendations
WEB TIER
AVAILABLITY
SET AD FS web application proxy (WAP) servers
• For VM size, use the on-premises AD FS machines as a starting point, and These VMs act as AD FS servers for incoming requests from partner
pick the closest Azure VM sizes. organizations and external devices. The WAP servers act as a filter,
NSG
shielding the AD FS servers from direct access from the Internet. As
VM with the AD FS servers, deploying the WAP servers in a farm with load
• Create separate Azure availability sets for the AD FS and WAP VMs, with at balancing gives you greater availability and scalability than deploying
least two update domains and two fault domains. a collection of stand-alone servers.
VM

297
• Place AD FS servers and WAP servers in separate subnets with their own
firewalls. Use NSG rules to define firewall rules.
VM Partner organization
A partner organization running a web application that requests access
to a web application running in Azure. The federation server at the
• Configure the network interface for each of the VMs hosting AD FS and
partner organization authenticates requests locally, and submits secu-
WAP servers with static private IP addresses. rity tokens containing claims to AD FS running in Azure. AD FS in
Azure validates the security tokens, and if valid can pass the claims to
• Prevent direct exposure of the AD FS servers to the Internet. the web application running in Azure to authorize them.
• Do not join the WAP servers to the domain.
Red híbrida
Estas arquitecturas de referencia muestran prácticas probadas de creación
de conexiones sólidas entre una red local y Azure.

For this scenario Consider this architecture

Hay aplicaciones híbridas con tráfico VPN


ligero entre el hardware local y el cloud.

Las aplicaciones híbridas ejecutan cargas ExpressRoute


de trabajo a gran escala, críticas para una
misión, que exigen una gran capacidad
de ampliación.

ExpressRoute con conmutación por


Hay aplicaciones híbridas que necesitan
recuperación a VPN
el ancho de banda más amplio de
ExpressRoute y exigen una conectividad
de red de alta disponibilidad.

298 CAPÍTULO 8 | Arquitecturas de referencia de Azure


Azure
Connect an on-premises network to Azure using a VPN gateway Architecture Components
This architecture extends an on-premises network to Azure, using a site-to-site virtual private network (VPN). Traffic flows between the on-premises network and the Azure Virtual Network
(VNet). On-premises network
A private local-area network running within an organization.
VPN appliance
A device or service that provides external connectivity to the
on-premises network. The VPN appliance may be a hardware
device, or it can be a software solution such as the Routing
and Remote Access Service (RRAS) in Windows Server 2012.
On-premises nework Virtual network For a list of supported VPN appliances and information on
configuring them to connect to an Azure VPN gateway, see
the instructions for the selected device in the article About
VPN devices for Site-to-Site VPN Gateway connections.
GATEWAY SUBNET WEB TIER
AVAILABLITY
BUSINESS TIER DATA TIER
AVAILABLITY AVAILABLITY
SET SET SET
Virtual network (VNet)
VPN Gateway
The cloud application and the components for the Azure
VPN gateway reside in the same VNet.
NSG VM NSG VM NSG VM
Gateway Azure VPN gateway
The VPN gateway service enables you to connect the VNet
VM VM VM
MANAGEMENT SUBNET to the on-premises network through a VPN appliance. For
more information, see Connect an on-premises network to a
Microsoft Azure virtual network. The VPN gateway includes
NSG
the following elements:
VM VM VM
Jump Box Virtual network gateway
A resource that provides a virtual VPN appliance for the
VNet. It is responsible for routing traffic from the
on-premises network to the VNet.
Local network gateway
An abstraction of the on-premises VPN appliance.
Network traffic from the cloud application to the
on-premises network is routed through this gateway.
Recommendations Connection
• Create an Azure VNet with an address space large enough for all of your required resources, with room for growth in case more VMs are needed in the future. The The connection has properties that specify the
address space of the VNet must not overlap with the on-premises network. connection type (IPSec) and the key shared with the
on-premises VPN appliance to encrypt traffic.
• The virtual network gateway requires a subnet named GatewaySubnet. Do not deploy any VMs to the gateway subnet. Also, do not assign an NSG to this subnet, as it will
cause the gateway to stop functioning. Gateway subnet
The virtual network gateway is held in its own subnet,
• Create a policy-based gateway if you need to closely control how requests are routed based on policy criteria such as address prefixes. Create a route-based gateway if which is subject to various requirements, described in
you connect to the on-premises network using RRAS, support multi-site or cross-region connections, or implement VNet-to-VNet connections. the Recommendations section below.
• Ensure that the on-premises routing infrastructure is configured to forward requests intended for addresses in the Azure VNet to the VPN device. Cloud Application
• Open any ports required by the cloud application in the on-premises network. The application hosted in Azure. It might include multiple
tiers, with multiple subnets connected through Azure load

299
• If you need to ensure that the on-premises network remains available to the Azure VPN gateway, implement a failover cluster for the on-premises VPN gateway. balancers. For more information about the application
infrastructure, see Running Windows VM workloads and
• If your organization has multiple on-premises sites, create multi-site connections to one or more Azure VNets. This approach requires dynamic (route-based) routing, so Running Linux VM workloads.
make sure that the on-premises VPN gateway supports this feature.
Internal load balancer
• Generate a different shared key for each VPN gateway. Use a strong shared key to help resist brute-force attacks. Network traffic from the VPN gateway is routed to the cloud
application through an internal load balancer. The load
• If you need higher bandwidth than a VPN connection supports, consider using an Azure ExpressRoute connection instead. balancer is located in the front-end subnet of the
application.
Azure
Connect an on-premises network to Azure using ExpressRoute Architecture Components
This architecture extends an on-premises network to Azure, using Azure ExpressRoute. ExpressRoute connections use a private, dedicated connection through a third-party connectivity
provider. The private connection extends your on-premises network into Azure. On-premises network
A private local-area network running within an organization.
Azure public services
ExpressRoute circuit
A layer 2 or layer 3 circuit supplied by the connectivity
provider that joins the on-premises network with Azure
Public peering through the edge routers. The circuit uses the hardware
infrastructure managed by the connectivity provider.
Local edge routers
Routers that connect the on-premises network to the circuit
On-premises nework
managed by the provider. Depending on how your
corporate network
connection is provisioned, you may need to provide the
public IP addresses used by the routers.
Azure Virtual networks
Local Microsoft
edge edge Microsoft edge routers
routers routers
Two routers in an active-active highly available configuration.
Private peering These routers enable a connectivity provider to connect their
ExpressRoute circuit circuits directly to their datacenter. Depending on how your
connection is provisioned, you may need to provide the
public IP addresses used by the routers.
Azure virtual networks (VNets)
Each VNet resides in a single Azure region, and can host
multiple application tiers. Application tiers can be
segmented using subnets in each VNet.
Azure public services
Office 365 services
Azure services that can be used within a hybrid application.
Microsoft peering These services are also available over the Internet, but
accessing them using an ExpressRoute circuit provides low
latency and more predictable performance, because traffic
does not go through the Internet. Connections are
performed using public peering, with addresses that are
either owned by your organization or supplied by your
connectivity provider.
Recommendations
Office 365 services
• Ensure that your organization has met the ExpressRoute prerequisite requirements for connecting to Azure. See ExpressRoute prerequisites & checklist.
The publicly available Office 365 applications and services
• Create an Azure VNet with an address space large enough for all of your required resources, with room for growth in case more VMs are needed in the future. The provided by Microsoft. Connections are performed using
address space of the VNet must not overlap with the on-premises network. Microsoft peering, with addresses that are either owned by
your organization or supplied by your connectivity provider.
• The virtual network gateway requires a subnet named GatewaySubnet. Do not deploy any VMs to the gateway subnet. Also, do not assign an NSG to this subnet, as it will You can also connect directly to Microsoft CRM Online
cause the gateway to stop functioning. through Microsoft peering.
• Although some providers allow you to change your bandwidth, make sure you pick an initial bandwidth that surpasses your needs and provides room for growth. Connectivity providers (not shown)

300
• Consider the following options for high availability: Companies that provide a connection either using layer 2 or
layer 3 connectivity between your datacenter and an Azure
datacenter.
If you're using a layer 2 connection, deploy redundant routers in your on-premises network in an active-active configuration. Connect the primary circuit to one router, and the secondary
circuit to the other.
If you're using a layer 3 connection, verify that it provides redundant BGP sessions that handle availability for you.
Connect the VNet to multiple ExpressRoute circuits, supplied by different service providers. This strategy provides additional high-availability and disaster recovery capabilities.
Configure a site-to-site VPN as a failover path for ExpressRoute. This option only applies to private peering. For Azure and Office 365 services, the Internet is the only failover path.
Azure
Connect an on-premises network to Azure using ExpressRoute with VPN failover Architecture Components
This architecture extends an on-premises network to Azure by using ExpressRoute, with a site-to-site virtual private network (VPN) as a failover connection. Traffic flows between the
on-premises network and the Azure VNet through an ExpressRoute connection. If there is a loss of connectivity in the ExpressRoute circuit, traffic is routed through an IPSec VPN tunnel. On-premises network
A private local-area network running within an organization.
VPN appliance
A device or service that provides external connectivity to the
On-premises nework on-premises network. The VPN appliance may be a hardware
device, or it can be a software solution such as the Routing
and Remote Access Service (RRAS) in Windows Server 2012.
Local Microsoft
For a list of supported VPN appliances and information on
Edge edge
Routers routers configuring selected VPN appliances for connecting to
Azure, see About VPN devices for Site-to-Site VPN Gateway
connections.
ExpressRoute circuit ExpressRoute circuit
A layer 2 or layer 3 circuit supplied by the connectivity
Gateway provider that joins the on-premises network with Azure
through the edge routers. The circuit uses the hardware
infrastructure managed by the connectivity provider.
Virtual network
ExpressRoute virtual network gateway
The ExpressRoute virtual network gateway enables the VNet
GATEWAY WEB TIER
AVAILABLITY
BUSINESS TIER
AVAILABLITY
DATA TIER
AVAILABLITY to connect to the ExpressRoute circuit used for connectivity
SUBNET
SET SET SET with your on-premises network.
VPN virtual network gateway
NSG VM NSG VM NSG VM
The VPN virtual network gateway enables the VNet to
connect to the VPN appliance in the on-premises network.
The VPN virtual network gateway is configured to accept
VM VM VM requests from the on-premises network only through the
ExpressRoute
VPN appliance. For more information, see Connect an
Gateway on-premises network to a Microsoft Azure virtual network.
VM VM VM
VPN connection
The connection has properties that specify the connection
MANAGEMENT SUBNET type (IPSec) and the key shared with the on-premises VPN
appliance to encrypt traffic.
NSG
Azure Virtual Network (VNet)
VM
VPN Gateway Each VNet resides in a single Azure region, and can host
Jumpbox
multiple application tiers. Application tiers can be
segmented using subnets in each VNet.
Gateway subnet
The virtual network gateways are held in the same subnet.

301
Recommendations Cloud application
• The recommendations from the previous two architectures apply to this architecture. The application hosted in Azure. It might include multiple
tiers, with multiple subnets connected through Azure load
balancers. For more information about the application
• After you establish the virtual network gateway connections, test the environment. First make sure you can connect from your on-premises network to your Azure VNet.
infrastructure, see Running Windows VM workloads and
This connection will use ExpressRoute. Then contact your provider to stop ExpressRoute connectivity for testing, and verify that you can still connect using the VPN
Running Linux VM workloads.
connection.
Azure
Implement a hub-spoke network topology in Azure Architecture Components
In this architecture, the hub is an Azure virtual network (VNet) that acts as a central point of connectivity to your on-premises network. The spokes are VNets that peer with the hub, and can
be used to isolate workloads. On-premises network
Reasons to consider this architecture : A private local-area network running within an organization.
Reduce costs by centralizing services shared services such as network virtual appliances (NVAs) and DNS servers.
Overcome subscriptions limits by peering VNets from different subscriptions to the central hub.
Separate concerns between central IT (SecOps, InfraOps) and workloads (DevOps). VPN Device
A device or service that provides external connectivity to the
Spoke1 Virtual Network on-premises network. The VPN device may be a hardware device, or
a software solution such as the Routing and Remote Access Service
(RRAS) in Windows Server 2012. For a list of supported VPN applianc-
es and information on configuring selected VPN appliances for con-
WEB TIER necting to Azure, see About VPN devices for Site-to-Site VPN Gate-
AVAILABLITY
way connections.
SET
On-premise network Hub Virtual Network VPN virtual network gateway
NSG VM
or ExpressRoute gateway
The virtual network gateway enables the VNet to connect to the VPN
PERIMETER CLOUD ACCESS GATEWAY SUBNET SHARED SERVICES
device, or ExpressRoute circuit, used for connectivity with your
SERVICES POINT
VM on-premises network. For more information, see Connect an
NSG
on-premises network to a Microsoft Azure virtual network.
AVAILABLITY
SET VM Note:
The deployment scripts for this reference architecture use a VPN
gateway for connectivity, and a VNet in Azure to simulate your
Peering
on-premises network.
VM Peering Spoke2 Virtual Network
Backend Systems DNS Services VPN Device ExpressRoute Hub Vnet
Gateway
Azure VNet used as the hub in the hub-spoke topology. The hub is
the central point of connectivity to your on-premises network, and a
VM WEB TIER
AVAILABLITY place to host services that can be consumed by the different work-
SET loads hosted in the spoke VNets.
DNS
Gateway subnet
NSG VM
The virtual network gateways are held in the same subnet.
VM Shared services subnet
A subnet in the hub VNet used to host services that can be shared
among all spokes, such as DNS or AD DS.
VM
Spoke Vnets
One or more Azure VNets that are used as spokes in the hub-spoke
Recommendations topology. Spokes can be used to isolate workloads in their own
VNets, managed separately from other spokes. Each workload might
• The hub VNet, and each spoke VNet, can be implemented in different resource groups, and even different subscriptions, as long as they belong to the same Azure Active include multiple tiers, with multiple subnets connected through
Directory (Azure AD) tenant in the same Azure region. This allows for a decentralized management of each workload, while sharing services maintained in the hub VNet. Azure load balancers. For more information about the application
infrastructure, see Running Windows VM workloads and Running
Linux VM workloads.

302
• A hub-spoke topology can be used without a gateway, if you don't need connectivity with your on-premises network.
• If you require connectivity between spokes, consider implementing an NVA for routing in the hub, and using user defined routes (UDRs) in the spoke to forward traffic to
the hub. Vnet peering
Two VNets in the same Azure region can be connected using a peer-
• Make sure to consider the limitation on the number of VNet peerings per VNet in Azure. If you need more spokes than this limit, consider creating a ing connection. Peering connections are non-transitive, low latency
hub-spoke-hub-spoke topology, where the first level of spokes also act as hubs. connections between VNets. Once peered, the VNets exchange traffic
by using the Azure backbone, without the need for a router. In a
• Consider what services are shared in the hub, to ensure the hub scales to the number of spokes. hub-spoke network topology, you use VNet peering to connect the
hub to each spoke.
Red DMZ
Estas arquitecturas de referencia muestran prácticas probadas de creación
de redes DMZ que protejan el límite entre una red virtual Azure y una red
local o Internet.

303 CAPÍTULO 8 | Arquitecturas de referencia de Azure


Azure
DMZ between Azure and your on-premises datacenter Architecture Components
This architecture implements a DMZ (also called a perimeter network) between an on-premises network and an Azure virtual network. The DMZ includes highly available network virtual
appliances (NVAs) to implement security functionality such as firewalls and packet inspection. All outgoing traffic from the VNet is force-tunneled to the Internet through the on-premises On-premise Nework
network, so that it can be audited.
A private local-area network implemented in an
organization.
On-premises nework Virtual network
Azure Virtual Network (VNet)
GATEWAY SUBNET The VNet hosts the application and other resources running
10�0�255�224/27
PRIVATE DMZ IN PRIVATE DMZ OUT in Azure.
10�0�0�0/27 10�0�0�32/27
AVAILABLITY
UDR SET
Gateway
The gateway provides connectivity between the routers in
NIC

NIC

NVA the on-premises network and the VNet.


NSG
Gateway
Network Virtual Appliance (NVA)
NSG
NVA is a generic term that describes a VM performing tasks
NIC

NIC
MANAGEMENT SUBNET
10�0�0�128/25 NVA such as allowing or denying access as a firewall, optimizing
wide area network (WAN) operations (including network
Internal Load
NSG Balancer
compression), custom routing, or other network
functionality.
Jump Box
Web Tier, Business Tier, and Data Tier Subnets
Subnets hosting the VMs and services that implement an
example 3-tier application running in the cloud. See Running
WEB TIER
AVAILABLITY
BUSINESS TIER DATA TIER Windows VMs for an N-tier architecture on Azure for more
10�0�1�0/24 AVAILABLITY AVAILABLITY
SET
10�0�2�0/24
SET
10�0�3�0/24
SET information.
User Defined Routes
NSG VM NSG VM NSG VM User defined routes define the flow of IP traffic within Azure
VNets.
NOTE: Depending on the requirements of your VPN
VM VM VM connection, you can configure Border Gateway Protocol
(BGP) routes instead of using UDRs to implement the
forwarding rules that direct traffic back through the
on-premises network.
VM VM VM
Management Subnet
This subnet contains VMs that implement management and
monitoring capabilities for the components running in the
Recommendations VNet.
• Use Role-Based Access Control (RBAC) to manage the resources in your application.
• On-premises traffic passes to the VNet through a virtual network gateway. We recommend an Azure VPN gateway or an Azure ExpressRoute gateway.
• Create a network security group (NSG) for the inbound NVA subnet, and only allow traffic originating from the on-premises network.

304
• Create NSGs for each subnet to provide a second level of protection in case of an incorrectly configured or disabled NVA.
• Force-tunnel all outbound Internet traffic through your on-premises network using the site-to-site VPN tunnel, and route to the Internet using network address
translation (NAT).
• Don't completely block Internet traffic from the application tiers, as this will prevent these tiers from using Azure PaaS services that rely on public IP addresses, such as VM
diagnostics logging.
• Perform all application and resource monitoring through the jumpbox in the management subnet.
Azure
DMZ between Azure and the Internet Architecture Components
This architecture implements a DMZ (also called a perimeter network) that accepts Internet traffic to an Azure virtual network. It also includes a DMZ that handles traffic from an on-premises
network. Network virtual appliances (NVAs) implement security functionality such as firewalls and packet inspection. Public IP Address (PIP)
The IP address of the public endpoint. External users
connected to the Internet can access the system through this
On-premises nework Virtual network address.
GATEWAY SUBNET
Network Virtual Appliance (NVA)
10�0�255�224/27
PRIVATE DMZ IN AVAILABLITY PRIVATE DMZ OUT This architecture includes a separate pool of NVAs for traffic
UDR 10�0�0�0/27 SET 10�0�0�32/27 originating on the Internet.
Azure Load Balancer
NIC

NIC

NVA
NSG All incoming requests from the Internet pass through the
Gateway NSG
load balancer and are distributed to the NVAs in the public
DMZ.
MANAGEMENT SUBNET
NIC

NIC
10�0�0�128/25 NVA
Public DMZ Inbound Subnet
Internal Load This subnet accepts requests from the Azure load balancer.
NSG
Balancer
Incoming requests are passed to one of the NVAs in the
Jump Box public DMZ.
Web app request
Public DMZ Outbound Subnet
WEB TIER BUSINESS TIER DATA TIER
AVAILABLITY AVAILABLITY AVAILABLITY
10�0�1�0/24
SET
10�0�2�0/24
SET
10�0�3�0/24
SET
Requests that are approved by the NVA pass through this
subnet to the internal load balancer for the web tier.
NSG NSG NSG
VM VM VM
VM VM VM
VM VM VM
PUBLIC DMZ IN AVAILABLITY PUBLIC DMZ OUT
10�0�0�0/27 SET 10�0�0�32/27
Azure Load

NIC

NIC
Balancer NVA
PIP NSG NSG
Recommendations

NIC

NIC
NVA
• Use one set of NVAs for traffic originating on the Internet, and another for
traffic originating on-premises.
• Include a layer-7 NVA to terminate application connections at the NVA level
and maintain compatibility with the backend tiers.

305
• For scalability and availability, deploy the public DMZ NVAs in an availability
set with a load balancer to distribute requests across the NVAs.
• Even if your architecture initially requires a single NVA, we recommend
putting a load balancer in front of the public DMZ from the beginning. That
makes it easier to scale to multiple NVAs in the future.
• Log all incoming requests on all ports. Regularly audit the logs.
Aplicación web
administrada
Estas arquitecturas de referencia muestran prácticas probadas de
aplicaciones web que utilizan Azure App Service y otros servicios
administrados en Azure.

306 CAPÍTULO 8 | Arquitecturas de referencia de Azure


Azure
Basic web application Architecture Components
This architecture shows a baseline deployment for a web application that uses Azure App Service and Azure SQL Database.
Resource Group
A resource group is a logical container for Azure resources.
App Service Plan Azure SQL Database
App Service App
Azure App Service is a fully managed platform for creating
and deploying cloud applications.
Instances Logical Server
App Service Plan
An App Service plan provides the managed virtual machines
(VMs) that host your app. All apps associated with a plan run
on the same VM instances.
Access Token Database Database
Deployment Slot
A deployment slot lets you stage a deployment and then
Azure Active App Service App swap it with the production deployment. That way, you avoid
Directory deploying directly into production. See the Manageability
section for specific recommendations.
Storage Account IP Address
Last-known good The App Service app has a public IP address and a domain
Authenticate name. The domain name is a subdomain of
IP Address Blob Container azurewebsites.net, such as contoso.azurewebsites.net. To use
Production a custom domain name, such as contoso.com, create domain
name service (DNS) records that map the custom domain
name to the IP address. For more information, see Configure
Staging a custom domain name in Azure App Service.
Validate App Logs Web Server
Deployment Logs
Deployment Slots
Azure SQL Database
Deploy SQL Database is a relational database-as-a-service in the
cloud.
Source
Resource Logical Server
Control
Group
In Azure SQL Database, a logical server hosts your databases.
Recommendations You can create multiple databases per logical server.
• Use the Standard or Premium tiers, because they support scale out, autoscale, and secure sockets layer (SSL).
• Provision the App Service plan and the SQL Database in the same region to minimize network latency. Azure Storage
Create an Azure storage account with a blob container to
• Enable autoscaling. If your application has a predictable, regular workload, schedule the instance counts ahead of time. If the workload is not predictable, use rule-based store diagnostic logs.
autoscaling to react to changes in load.
• Create a staging slot to deploy updates. By using a staging slot, you can verify the deployment succeeded, before swapping it into production. Using a staging slot also Azure Active Directory (Azure AD)
ensures that all instances are warmed up before being swapped into production. Use Azure AD or another identity provider for
authentication.
• Don't use slots on your production deployment for testing, because all apps within the same App Service plan share the same VM instances. Instead, put test

307
deployments into a separate App Service plan to isolate them from the production version.
• Store configuration settings as app settings. Define the app settings in your Resource Manager templates, or using PowerShell. Never check passwords, access keys, or
connection strings into source control. Instead, pass these as parameters to a deployment script that stores these values as app settings.
• Consider using App Service authentication to implement authentication with an identity provider such as Azure Active Directory, Facebook, Google, or Twitter.
• Use SQL Database point-in-time restore to recover from human error by returning the database to an earlier point in time. Use geo-restore to recover from a service
outage by restoring a database from a geo-redundant backup.
Azure
Improved scalability in a web application Architecture Components
This architecture builds on the one shown in “Basic web application” and adds elements to improve scalability and performance.
Resource Group
A resource group is a logical container for Azure resources.
Web App and API App
A typical modern application might include both a website
Web Front End and one or more RESTful web APIs. A web API might be
consumed by browser clients through AJAX, by native client
App Service App Service Plan Data Storage applications, or by server-side applications. For
Authentication Plan considerations on designing web APIs, see API design
guidance.
{}
Azure Active SQL Database Document DB WebJob
Directory Web App API App WebJob Redis Cache Use Azure WebJobs to run long-running tasks in the
background. WebJobs can run on a schedule, continously, or
in response to a trigger, such as putting a message on a
queue. A WebJob runs as a background process in the
context of an App Service app.
Queue
Logs Queue Static Azure Search In the architecture shown here, the application queues
Content background tasks by putting a message onto an Azure
Queue storage queue. The message triggers a function in the
WebJob. Alternatively, you can use Service Bus queues. For a
Blob comparison, see Azure Queues and Service Bus queues -
compared and contrasted.
Storage Storage Storage Content Delivery
Account Account Account Network
Cache
Store semi-static data in Azure Redis Cache.
Resource
Group CDN
Use Azure Content Delivery Network (CDN) to cache publicly
Email/SMS available content for lower latency and faster delivery of
Service content.
Edge Servers
Data Storage
Use Azure SQL Database for relational data. For
Recommendations non-relational data, consider a NoSQL store, such as Cosmos
DB.
• Use Azure WebJobs to run long-running tasks in the background. WebJobs can run on a schedule, continuously, or in response to a trigger, such as putting a message
on a queue.
Azure Search
• Consider deploying resource intensive WebJobs to a separate App Service plan. This provides dedicated instances for the WebJob.
Use Azure Search to add search functionality such as search
• Use Azure Redis Cache to cache semi-static transaction data, session state, and HTML output. suggestions, fuzzy search, and language-specific search.
Azure Search is typically used in conjunction with another
• Use Azure CDN to cache static content. The main benefit of a CDN is to reduce latency for users, because content is cached at an edge server that is geographically close data store, especially if the primary data store requires strict
consistency. In this approach, store authoritative data in the
to the user. CDN can also reduce load on the application, because that traffic is not being handled by the application.

308
other data store and the search index in Azure Search. Azure
Search can also be used to consolidate a single search index
• Choose a data store based on application requirements. Depending on the scenario, you might use multiple stores. For more guidance, see Choose the right data store.
from multiple data stores.
• If you are using Azure SQL Database, consider using elastic pools. Elastic pools enable you to manage and scale multiple databases that have varying and unpredictable
usage demands. Email/SMS
Use a third-party service such as SendGrid or Twilio to send
• Also consider using Elastic Database tools to shard the database. Sharding allows you to scale out the database horizontally. email or SMS messages instead of building this functionality
directly into the application.
• Use Transparent Data Encryption to encrypt data at rest in Azure SQL Database.
Azure
Run a web application in multiple regions Architecture Components
This architecture shows a web application running on Azure App Service in two regions to achieve high availability. If an outage occurs in the primary region, the application can fail over to the secondary region.
Primary and Secondary Regions
Region 1 (Active)
This architecture uses two regions to achieve higher
availability. The application is deployed to each region.
App Service App Service Plan Data Storage During normal operations, network traffic is routed to the
Plan primary region. If the primary region becomes unavailable,
{} traffic is routed to the secondary region.
SQL Database Document DB
Web App API App WebJob Redis Cache Azure Traffice Manager
Traffic Manager routes incoming requests to the primary
region. If the application running that region becomes
Internet
unavailable, Traffic Manager fails over to the secondary
region.
Logs Queue Static
Content Azure Search
Geo-replication
of SQL Database and Cosmos DB.
Storage Storage Storage Content Delivery
Account Account Account Network (CDN)
should content below be included?
Region 2 (Standby) Read-only
replica Geo-replication A multi-region architecture can provide higher availability
than deploying to a single region. If a regional outage affects
the primary region, you can use Traffic Manager to fail over
to the secondary region. This architecture can also help if an
App Service App Service Plan Data Storage individual subsystem of the application fails.
Plan
{} There are several general approaches to achieving high
availability across regions:
SQL Database Document DB
Web App API App WebJob Redis Cache Active/passive with hot standby. Traffic goes to one region,
while the other waits on hot standby. Hot standby means the
VMs in the secondary region are allocated and running at all
times.
Logs Queue Active/passive with cold standby. Traffic goes to one region,
Azure Search while the other waits on cold standby. Cold standby means
the VMs in the secondary region are not allocated until
needed for failover. This approach costs less to run, but will
generally take longer to come online during a failure.
Storage Storage
Account Account Active/active. Both regions are active, and requests are load
balanced between them. If one region becomes unavailable,
Recommendations it is taken out of rotation.
• Each Azure region is paired with another region within the same geography. In general, choose regions from the same regional pair. If there is a broad outage, recovery of at least one region out of every This reference architecture focuses on active/passive with hot
pair is prioritized. standby, using Traffic Manager for failover.
• Configure Traffic Manager to use priority routing, which routes all requests to the primary region. If the primary region becomes unreachable, Traffic Manager automatically fails over to the secondary
region.
• Traffic Manager uses an HTTP (or HTTPS) probe to monitor the availability of each region. Create a health probe endpoint that reports the overall health of the application.

309
• Traffic Manager is a possible failure point in the system. Review the Traffic Manager SLA, and determine whether using Traffic Manager alone meets your business requirements for high availability. If not,
consider adding another traffic management solution as a failback.
• For Azure SQL Database, use Active Geo-Replication to create a readable secondary replica in a different region. Fail over to a secondary database if your primary database fails or needs to be taken offline.
• Cosmos DB also supports geo-replication across regions. One region is designated as writable and the others are read-only replicas. If there is a regional outage, you can fail over by selecting another
region to be the write region.
• For Azure Storage, use read-access geo-redundant storage (RA-GRS).
Ejecución de cargas
de trabajo en máquina
virtual de Linux
Estas arquitecturas de referencia muestran prácticas probadas
de ejecución de máquinas virtuales de Linux en Azure.

310 CAPÍTULO 8 | Arquitecturas de referencia de Azure


Azure
Run a Linux VM on Azure Architecture Components
This architecture shows a Linux virtual machine (VM) running on Azure, along with associated networking and storage components. This architecture can be used to run a single instance,
and is the basis for more complex architectures such as n-tier applications. Resource Group
A resource group is a container that holds related resources.
You usually create resource groups for different resources in
a solution based on their lifetime, and who will manage the
resources. For a single VM workload, you may create a single
Virtual network resource group for all resources.
Resource VM
SUBNET
Group VHD Azure supports running various popular Linux distributions,
including CentOS, Debian, Red Hat Enterprise, Ubuntu, and
FreeBSD. For more information, see Azure and Linux. You can
provision a VM from a list of published images or from a
virtual hard disk (VHD) file that you upload to Azure Blob
OS storage.
VHD OS disk
VM Storage
Account The OS disk is a VHD stored in Azure Storage. That means it
persists even if the host machine goes down. The OS disk is
/dev/sda1.
Data 1
NIC

Public IP Address Temporary disk


VHD
The VM is created with a temporary disk. This disk is stored
VM on a physical drive on the host machine. It is not saved in
Internet
Azure Storage, and might be deleted during reboots and
other VM lifecycle events. Use this disk only for temporary
Data 2 data, such as page or swap files. The temporary disk is
/dev/sdb1 and is mounted at /mnt/resource or /mnt.
Temp Data disks
A data disk is a persistent VHD used for application data.
Diagnostics Data disks are stored in Azure Storage, like the OS disk.
Logs
Logs Storage Virtual network (VNet) and subnet
Physical SSD
Account Every VM in Azure is deployed into a VNet that is further
on Host
divided into subnets.
Public IP address
Recommendations A public IP address is needed to communicate with the VM
— for example over SSH.
• For best disk I/O performance, we recommend Premium Storage, which stores data on solid-state drives (SSDs).
Network interface (NIC)
• Use Managed disks, which do not require a storage account. You simply specify the size and type of disk and it is deployed in a highly available way.
The NIC enables the VM to communicate with the virtual
network.
• Attach a data disk for persistent storage of application data.
• Enable monitoring and diagnostics, including health metrics, diagnostics infrastructure logs, and boot diagnostics. Network security group (NSG)
The NSG is used to allow/deny network traffic to the subnet.
• Add an NSG to the subnet to allow/deny network traffic to the subnet. To enable SSH, add a rule to the NSG that allows inbound traffic to TCP port 22. You can associate an NSG with an individual NIC or with a
subnet. If you associate it with a subnet, the NSG rules apply
to all VMs in that subnet.

311
• Reserve a static IP address if you need a fixed IP address that won't change — for example, if you need to create an A record in DNS, or need the IP address to be added
to a safe list.
Diagnostics
• For higher availability, deploy multiple VMs behind a load balancer. See [Load balanced VMs reference architecture] Diagnostic logging is crucial for managing and
troubleshooting the VM.
• Use Azure Security Center to get a central view of the security state of your Azure resources. Security Center monitors potential security issues and provides a
comprehensive picture of the security health of your deployment.
• Consider Azure Disk Encryption if you need to encrypt the OS and data disks.
Azure
Run load-balanced VMs for scalability and availability Architecture Components
This architecture shows running several Linux virtual machines (VMs) running behind a load balancer, to improve availability and scalability. This architecture can be used for any stateless
workload, such as a web server, and is a building block for deploying n-tier applications. Availability Set
The availability set contains the VMs, making the VMs
eligible for the availability service level agreement (SLA) for
Azure VMs. For the SLA to apply, the availability set must
include a minimum of two VMs. Availability sets are implicit
in scale sets. If you create VMs outside a scale set, you need
to create the availability set independently.
Virtual Network (VNet) and Subnet
Every VM in Azure is deployed into a VNet that is further
divided into subnets.
Azure Load Balancer
Virtual network
The load balancer distributes incoming Internet requests to
the VM instances. The load balancer includes some related
SUBNET
resources:
VM 1 Storage VHDs
Account
Public IP Address
AVAILABLITY
SET A public IP address is needed for the load balancer to
receive Internet traffic.
Front-end Configuration
Public IP
VM 2 Storage VHDs Associates the public IP address with the load balancer.
Account
Azure Load Back-end Address Pool
Internet Balancer Contains the network interfaces (NICs) for the VMs that
Diagnostics will receive the incoming traffic.
VM Scaleset
Logs
Logs Storage
Load Balancer Rules
Account Used to distribute network traffic among all the VMs in the
back-end address pool.
Network Address Translation (NAT) Rules
Used to route traffic to a specific VM. For example, to enable
remote desktop protocol (RDP) to the VMs, create a separate
NAT rule for each VM.
Storage
If you are not using managed disks, storage accounts hold
the VM images and other file-related resources, such as VM
Recommendations diagnostic data captured by Azure.
• Consider using a VM scale set if you need to quickly scale out VMs, or need to autoscale. If you don’t use a scale set, place the VMs in an availability set. VM Scale set
A VM scale set is a set of identical VMs used to host a
• Use Managed disks, which do not require a storage account. You simply specify the size and type of disk and it is deployed in a highly available way. workload. Scale sets allow the number of VMs to be scaled in
or out manually, or based on predefined rules.
• Place the VMs within the same subnet. Do not expose the VMs directly to the Internet, but instead give each VM a private IP address. Clients connect using the public IP

312
address of the load balancer.
• For incoming Internet traffic, the load balancer rules define which traffic can reach the back end. However, load balancer rules don't support IP whitelisting, so if you want
to add certain public IP addresses to a whitelist, add an NSG to the subnet.
• The load balancer uses health probes to monitor the availability of VM instances. If your VMs run an HTTP server, create an HTTP probe. Otherwise create a TCP probe.
• Virtual networks are a traffic isolation boundary in Azure. VMs in one VNet cannot communicate directly to VMs in a different VNet. VMs within the same VNet can
communicate, unless you create network security groups (NSGs) to restrict traffic.
Azure
Run Linux VMs for an N-tier Application Architecture Components
This architecture shows how to deploy Linux virtual machines (VMs) to run an N-tier application in Azure. For the data tier, this architecture shows Apache Cassandra, which provides
replication and failover. However you could easily replace Cassandra in this architecture with another database, such as SQL Server. Availability Sets
Create an availability set for each tier, and provision at least
two VMs in each tier. This makes the VMs eligible for a
higher service level agreement (SLA) for VMs.
Subnets
VIRTUAL NETWORK Create a separate subnet for each tier. Specify the address
range and subnet mask using CIDR notation.
WEB TIER BUSINESS TIER DATA TIER
SUBNET
AVAILABLITY SUBNET
AVAILABLITY SUBNET
AVAILABLITY
SET SET SET Load Balancers
Use an Internet-facing load balancer to distribute incoming
VM
Internet traffic to the web tier, and an internal load balancer
NSG VM NSG VM to distribute network traffic from the web tier to the business
VM VM
tier.
Cassandra
NSG
Cluster
VM VM
Jumpbox
Azure Load Also called a bastion host. A secure VM on the network that
VM VM administrators use to connect to the other VMs. The
Internet Balancer
jumpbox has an NSG that allows remote traffic only from
VM VM VM public IP addresses on a safe list. The NSG should permit
secure shell (SSH) traffic.
Monitoring
MANAGEMENT SUBNET
Monitoring software such as Nagios, Zabbix, or Icinga can
give you insight into response time, VM uptime, and the
overall health of your system. Install the monitoring software
on a VM that's placed in a separate management subnet.
NSG
VM
NSGs
DevOps Jumpbox Use network security groups (NSGs) to restrict network
traffic within the VNet. For example, in the 3-tier architecture
shown here, the database tier does not accept traffic from
the web front end, only from the business tier and the
management subnet.
Apache Cassandra Database
Provides high availability at the data tier, by enabling
replication and failover.
Recommendations
• When you create the VNet, determine how many IP addresses your resources in each subnet require.
• Choose an address range that does not overlap with your on-premises network, in case you need to set up a gateway between the VNet and your on-premises network later.
• Design subnets with functionality and security requirements in mind. All VMs within the same tier or role should go into the same subnet, which can be a security boundary.

313
• Use NSG rules to restrict traffic between tiers. For example, in the 3-tier architecture shown above, the web tier should not communicate directly with the database tier.
• Do not allow SSH access from the public Internet to the VMs that run the application workload. Instead, all SSH access to these VMs must come through the jumpbox.
• The load balancers distribute network traffic to the web and business tiers. Scale horizontally by adding new VM instances. Note that you can scale the web and business
tiers independently, based on load.
• At the database tier, having multiple VMs does not automatically translate into a highly available database. For a relational database, you will typically need to use replication
and failover to achieve high availability.
Azure
Run Linux VMs in multiple regions for high availability Architecture Components
This architecture shows an N-tier application deployed in two Azure regions. This architecture can provide higher availability than a single region. If an outage occurs in the primary region,
the application can fail over to the secondary region. However, you must consider issues such as data replication and managing failover. Primary and Secondary Regions
Use two regions to achieve higher availability. One is the
primary region.The other region is for failover.
WEB TIER BUSINESS TIER CASSANDRA
Azure Traffic Manager
Traffic Manager routes incoming requests to one of the
VM
VM regions. During normal operations, it routes requests to the
VM primary region. If that region becomes unavailable, Traffic
VM VM
Manager fails over to the secondary region. For more
VM information, see the section Traffic Manager configuration.
VM
VM Resource Group
VM VM
Create separate resource groups for the primary region, the
VM
VM secondary region, and for Traffic Manager. This gives you the
flexibility to manage each region as a single collection of
resources. For example, you could redeploy one region,
VM Jumpbox without taking down the other one. Link the resource
groups, so that you can run a query to list all the resources
for the application.
VNets
WEB TIER BUSINESS TIER CASSANDRA
Create a separate VNet for each region. Make sure the
address spaces do not overlap.
VM
VM
VM
VM VM Apache Cassandra
VM Deploy Cassandra in data centers across Azure regions for
VM high availability. Within each region, nodes are configured in
rack-aware mode with fault and upgrade domains, for
VM
VM VM resiliency inside the region.
VM
VM
VM Jumpbox
Recommendations
• Each Azure region is paired with another region within the same geography. In general, choose regions from the same regional pair. If there is a broad outage, recovery of at
least one region out of every pair is prioritized.
• Configure Traffic Manager to use priority routing, which routes all requests to the primary region. If the primary region becomes unreachable, Traffic Manager automatically
fails over to the secondary region.
• If Traffic Manager fails over, we recommend performing a manual failback rather than implementing an automatic failback. Verify that all application subsystems are healthy
before failing back.
• Traffic Manager uses an HTTP (or HTTPS) probe to monitor the availability of each region. Create a health probe endpoint that reports the overall health of the application.

314
• Traffic Manager is a possible failure point in the system. Review the Traffic Manager SLA, and determine whether using Traffic Manager alone meets your business
requirements for high availability. If not, consider adding another traffic management solution as a failback.
• For the data tier, this architecture shows Apache Cassandra for data replication and failover. Other database systems have similar functionality.
• When you update your deployment, update one region at a time to reduce the chance of a global failure from an incorrect configuration or an error in the application.
• Test the resiliency of the system to failures. Measure the recovery times and verify they meet your business requirements.
Ejecución de cargas
de trabajo en máquina
virtual de Windows
Estas arquitecturas de referencia muestran prácticas probadas
de ejecución de máquinas virtuales de Windows en Azure.

315 CAPÍTULO 8 | Arquitecturas de referencia de Azure


Azure
Run a Windows VM on Azure Architecture Components
This architecture shows a Windows virtual machine (VM) running on Azure, along with associated networking and storage components. This architecture can be used to run a single instance,
and is the basis for more complex architectures such as n-tier applications. Resource Group
A resource group is a container that holds related resources.
Create a resource group to hold the resources for this VM.
Virtual network VM
You can provision a VM from a list of published images or
Resource
SUBNET from a virtual hard disk (VHD) file that you upload to Azure
Group VHD Blob storage.
OS Disk
OS The OS disk is a VHD stored in Azure Storage. That means it
persists even if the host machine goes down.
VHD
VM Storage
Account Temporary Disk
The VM is created with a temporary disk (the D: drive on
Windows). This disk is stored on a physical drive on the host
Data 1 machine. It is not saved in Azure Storage, and might be
NIC

Public IP Address deleted during reboots and other VM lifecycle events. Use
VHD this disk only for temporary data, such as page or swap files.
VM
Internet
Data Disk
Data 2 A data disk is a persistent VHD used for application data.
Data disks are stored in Azure Storage, like the OS disk.
Temp
Diagnostics
Virtual Network (VNet) and Subnet
Logs Every VM in Azure is deployed into a VNet that is further
divided into subnets.
Logs Storage
Physical SSD
Account
on Host Public IP Address
A public IP address is needed to communicate with the
VM—for example over remote desktop (RDP).
Recommendations Network interface (NIC)
• For best disk I/O performance, we recommend Premium Storage, which stores data on solid-state drives (SSDs). The NIC enables the VM to communicate with the virtual
network.
• Use Managed disks, which do not require a storage account. You simply specify the size and type of disk and it is deployed in a highly available way.
• Attach a data disk for persistent storage of application data. Network security group (NSG)
The NSG is used to allow/deny network traffic to the subnet.
• Enable monitoring and diagnostics, including health metrics, diagnostics infrastructure logs, and boot diagnostics.
You can associate an NSG with an individual NIC or with a
subnet. If you associate it with a subnet, the NSG rules apply
• Add an NSG to the subnet to allow/deny network traffic to the subnet. To enable remote desktop (RDP), add a rule to the NSG that allows inbound traffic to TCP port 3389. to all VMs in that subnet.

316
• Reserve a static IP address if you need a fixed IP address that won't change — for example, if you need to create an A record in DNS, or need the IP address to be added to
a safe list. Diagnostics
Diagnostic logging is crucial for managing and
• For higher availability, deploy multiple VMs behind a load balancer. See [Load balanced VMs reference architecture] troubleshooting the VM.
• Use Azure Security Center to get a central view of the security state of your Azure resources. Security Center monitors potential security issues and provides a
comprehensive picture of the security health of your deployment.
• Consider Azure Disk Encryption if you need to encrypt the OS and data disks.
Azure
Run load-balanced VMs for scalability and availability Architecture Components
This architecture shows running several Windows virtual machines (VMs) running behind a load balancer, to improve availability and scalability. This architecture can be used for any stateless
workload, such as a web server, and is a building block for deploying n-tier applications. Resource group
Resource groups are used to group resources so they can be
managed by lifetime, owner, and other criteria.
Virtual Network (VNet) and Subnet
Every VM in Azure is deployed into a VNet that is further
divided into subnets.
Azure Load Balancer
The load balancer distributes incoming Internet requests to
the VM instances. The load balancer includes some related
resources:
Virtual network
Public IP Address
A public IP address is needed for the load balancer to
SUBNET VM 1 Storage VHDs receive Internet traffic.
Account
AVAILABLITY
SET Front-end Configuration
Associates the public IP address with the load balancer.
Back-end Address Pool
Public IP
VM 2 Storage VHDs
Contains the network interfaces (NICs) for the VMs that
Account
will receive the incoming traffic.
Azure Load
Internet Balancer Load Balancer Rules
VM Scaleset
Diagnostics Used to distribute network traffic among all the VMs in the
Logs back-end address pool.
Logs Storage
Account VM Scale set
A VM scale set is a set of identical VMs used to host a
workload. Scale sets allow the number of VMs to be scaled in
or out manually, or based on predefined rules.
Availability Set
The availability set contains the VMs, making the VMs
eligible for the availability service level agreement (SLA) for
Azure VMs. In order for the SLA to apply, the availability set
must include a minimum of two VMs. Availability sets are
implicit in scale sets. If you create VMs outside a scale set,
Recommendations you need to create the availability set independently.
• Consider using a VM scale set if you need to quickly scale out VMs, or need to autoscale. If you don’t use a scale set, place the VMs in an availability set.
Storage
If you are not using managed disks, storage accounts hold
• Use Managed disks, which do not require a storage account. You simply specify the size and type of disk and it is deployed in a highly available way.
the VM images and other file-related resources, such as VM
diagnostic data captured by Azure.
• Place the VMs within the same subnet. Do not expose the VMs directly to the Internet, but instead give each VM a private IP address. Clients connect using the public IP
address of the load balancer.

317
• For incoming Internet traffic, the load balancer rules define which traffic can reach the back end. However, load balancer rules don't support IP whitelisting, so if you want
to add certain public IP addresses to a whitelist, add an NSG to the subnet.
• The load balancer uses health probes to monitor the availability of VM instances. If your VMs run an HTTP server, create an HTTP probe. Otherwise create a TCP probe.
• Virtual networks are a traffic isolation boundary in Azure. VMs in one VNet cannot communicate directly to VMs in a different VNet. VMs within the same VNet can
communicate, unless you create network security groups (NSGs) to restrict traffic.
Azure
Run Windows VMs for an N-tier application Architecture Components
This architecture shows how to deploy Windows virtual machines (VMs) to run an N-tier application in Azure. For the data tier, this architecture uses SQL Server Always On Availability
Groups, which provide replication and failover. Availability Sets
Create an availability set for each tier, and provision at least
two VMs in each tier. This makes the VMs eligible for a
higher service level agreement (SLA) for VMs.
Subnets
VIRTUAL NETWORK
Create a separate subnet for each tier. Specify the address
range and subnet mask using CIDR notation.
WEB TIER BUSINESS TIER BUSINESS TIER
SUBNET
AVAILABLITY SUBNET
AVAILABLITY SUBNET
AVAILABLITY
SET SET SET
Load Balancers
Use an Internet-facing load balancer to distribute incoming
Internet traffic to the web tier, and an internal load balancer
NSG VM NSG VM NSG to distribute network traffic from the web tier to the
business tier.
SQL Server
(Primary)
Jumpbox
VM VM
Also called a bastion host. A secure VM on the network that
Azure Load
administrators use to connect to the other VMs. The
Internet Balancer
jumpbox has an NSG that allows remote traffic only from
public IP addresses on a safe list. The NSG should permit
VM VM
remote desktop (RDP) traffic.
SQL Server Monitoring
(Secondary)
Monitoring software such as Nagios, Zabbix, or Icinga can
MANAGEMENT SUBNET ACTIVE DIRECTORY SUBNET
give you insight into response time, VM uptime, and the
overall health of your system. Install the monitoring software
on a VM that's placed in a separate management subnet.
NSG NSG
VM VM NSGs
Use network security groups (NSGs) to restrict network
DevOps Jumpbox AD DS AD DS File share
Server Server witness traffic within the VNet. For example, in the 3-tier architecture
shown here, the database tier does not accept traffic from
the web front end, only from the business tier and the
management subnet.
SQL Server Always On Availability Group
Provides high availability at the data tier, by enabling
replication and failover.
Recommendations
Active Directory Domain Services
• When you create the VNet, determine how many IP addresses your resources in each subnet require. (AD DS) Servers
• Choose an address range that does not overlap with your on-premises network, in case you need to set up a gateway between the VNet and your on-premises network later. Prior to Windows Server 2016, SQL Server Always On
Availability Groups must be joined to a domain. This is
because Availability Groups depend on Windows Server
• Design subnets with functionality and security requirements in mind. All VMs within the same tier or role should go into the same subnet, which can be a security boundary.
Failover Cluster (WSFC) technology. Windows Server 2016
introduces the ability to create a Failover Cluster without
• Use NSG rules to restrict traffic between tiers. For example, in the 3-tier architecture shown above, the web tier should not communicate directly with the database tier. Active Directory, in which case the AD DS servers are not

318
required for this architecture. For more information, see
• Do not allow remote desktop (RDP) access from the public Internet to the VMs that run the application workload. Instead, all RDP access to these VMs must come through What's new in Failover Clustering in Windows Server 2016.
the jumpbox.
• The load balancers distribute network traffic to the web and business tiers. Scale horizontally by adding new VM instances. Note that you can scale the web and business
tiers independently, based on load.
• We recommend Always On Availability Groups for SQL Server high availability. When a SQL client tries to connect, the load balancer routes the connection request to the
primary replica. If there is a failover to another replica, the load balancer automatically starts routing requests to the new primary replica.
Azure
Run Windows VMs in multiple regions for high availability Architecture Components
This architecture shows an N-tier application deployed in two Azure regions. This architecture can provide higher availability than a single region. If an outage occurs in the primary region,
the application can fail over to the secondary region. However, you must consider issues such as data replication and managing failover. Primary and Secondary Regions
Use two regions to achieve higher availability. One is the
WEB TIER
primary region. The other region is for failover.
BUSINESS TIER SQL SERVER ALWAYS ON
AVAILABILITY GROUP
VM
Azure Traffic Manager
VM Traffic Manager routes incoming requests to one of the
VM regions. During normal operations, it routes requests to the
VM primary region. If that region becomes unavailable, Traffic
Manager fails over to the secondary region. For more
VM
VM VM information, see the section Traffic Manager configuration.
ACTIVE DIRECTORY
Resource Groups
JUMPBOX GATEWAY SUBNET
Create separate resource groups for the primary region, the
secondary region, and for Traffic Manager. This gives you the
flexibility to manage each region as a single collection of
VM resources. For example, you could redeploy one region,
VPN Gateway
without taking down the other one. Link the resource
groups, so that you can run a query to list all the resources
for the application.
JUMPBOX ACTIVE DIRECTORY GATEWAY SUBNET VNets
Create a separate VNet for each region. Make sure the
address spaces do not overlap.
VM
VPN Gateway
SQL Server Always On Availability Group
WEB TIER BUSINESS TIER SQL SERVER ALWAYS ON
AVAILABILITY GROUP If you are using SQL Server, we recommend SQL Always On
Availability Groups for high availability. Create a single
VM availability group that includes the SQL Server instances in
VM both regions.
VM
Note
VM
VM Also consider Azure SQL Database, which provides a
VM relational database as a cloud service. With SQL Database,
VM you don't need to configure an availability group or manage
failover.
Recommendations
• Each Azure region is paired with another region within the same geography. In general, choose regions from the same regional pair. If there is a broad outage, recovery of at least one region out of every VPN Gateways
pair is prioritized. Create a VPN gateway in each VNet, and configure a
VNet-to-VNet connection, to enable network traffic between
• Configure Traffic Manager to use priority routing, which routes all requests to the primary region. If the primary region becomes unreachable, Traffic Manager automatically fails over to the secondary
the two VNets. This is required for the SQL Always On
region.
Availability Group.
• If Traffic Manager fails over, we recommend performing a manual failback rather than implementing an automatic failback. Verify that all application subsystems are healthy before failing back.
• Traffic Manager uses an HTTP (or HTTPS) probe to monitor the availability of each region. Create a health probe endpoint that reports the overall health of the application.
• Traffic Manager is a possible failure point in the system. Review the Traffic Manager SLA, and determine whether using Traffic Manager alone meets your business requirements for high availability. If not,

319
consider adding another traffic management solution as a failback.
• Create a SQL Server Always On Availability Group that includes the SQL Server instances in both the primary and secondary regions. Configure the replicas in the secondary region to use asynchronous
commit, for performance reasons.
• If all of the SQL Server database replicas in the primary region fail, you can manually fail over the availability group. With forced failover, there is a risk of data loss. Once the primary region is back online,
take a snapshot of the database and use tablediff to find the differences.
• When you update your deployment, update one region at a time to reduce the chance of a global failure from an incorrect configuration or an error in the application.
• Test the resiliency of the system to failures. Measure the recovery times and verify they meet your business requirements.

Potrebbero piacerti anche