Вы находитесь на странице: 1из 329

Guía de

arquitectura de
aplicaciones
en la nube
PUBLICADO POR
Microsoft Press
Una división de Microsoft Corporation
One Microsoft Way
Redmond, Washington 98052-6399

Copyright © 2017 por Microsoft Corporation

Todos los derechos reservados. Ninguna parte del contenido de este libro se podrá reproducir ni
transmitir de ninguna forma ni por ningún medio sin el consentimiento previo por escrito del editor.

Los libros de Microsoft Press están disponibles en librerías y a través de distribuidores en todo el
mundo. Si necesita asistencia relacionada con este libro, envíe un correo electrónico al soporte de
Microsoft Press en mspinput@microsoft.com. Envíenos su opinión acerca de este libro a
http://aka.ms/tellpress.

Este libro se entrega "tal cual" y expresa las visiones y opiniones de los autores. Las visiones, las
opiniones y la información expresadas en este libro, incluidas las direcciones URL y otras referencias
a sitios web de Internet, están sujetas a cambios sin previo aviso.

Algunos ejemplos mencionados aquí tienen un carácter únicamente ilustrativo y son ficticios. No
debe suponerse ni derivarse ninguna asociación o conexión reales.

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


"Marcas Registradas" son marcas comerciales del grupo de empresas de Microsoft. Todas las demás
marcas son propiedad de sus titulares correspondientes.

Editor de adquisiciones:
Christopher Bennage

Editores de desarrollo:
Mike Wasson, Masashi Narumoto y el equipo de Microsoft Patterns and Practices

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
Un recorrido rápido por los estilos �������������������������������������������������������������������������������������������������������������������������� 2
Estilos de arquitectura a modo de restricciones ������������������������������������������������������������������������������������������������� 4
Desafíos y ventajas por considerar ��������������������������������������������������������������������������������������������������������������������������� 5
Capítulo 1a: Estilo de arquitectura de n niveles ������������������������������������������������������������������������������� 6
Cuándo utilizar esta arquitectura ������������������������������������������������������������������������������������������������������������������������������ 7
Ventajas ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� 7
Desafíos ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� 7
Procedimientos recomendados ��������������������������������������������������������������������������������������������������������������������������������� 8
Arquitectura de n niveles en máquinas virtuales ������������������������������������������������������������������������������������������������ 8
Consideraciones adicionales ��������������������������������������������������������������������������������������������������������������������������������������� 9
Capítulo 1b: Estilo de arquitectura web-cola-trabajador �������������������������������������������������������������� 10
Cuándo utilizar esta arquitectura ��������������������������������������������������������������������������������������������������������������������������� 11
Ventajas ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������� 11
Desafíos ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������� 11
Procedimientos recomendados ������������������������������������������������������������������������������������������������������������������������������ 11
Web-cola-trabajador 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
Desafíos ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������� 16
Procedimientos recomendados ������������������������������������������������������������������������������������������������������������������������������ 17
Microservicios que utilizan Azure Service Fabric ��������������������������������������������������������������������������������������������� 19
Capítulo 1d: Estilo de arquitectura CQRS ���������������������������������������������������������������������������������������� 20
Cuándo utilizar esta arquitectura ��������������������������������������������������������������������������������������������������������������������������� 21
Ventajas ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������� 21
Desafíos ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������� 22
Procedimientos recomendados ������������������������������������������������������������������������������������������������������������������������������ 22
CQRS en microservicios ��������������������������������������������������������������������������������������������������������������������������������������������� 22

ii Contenido
Capítulo 1e: Estilo de arquitectura basada en eventos ������������������������������������������������������������������ 24
Cuándo utilizar esta arquitectura����������������������������������������������������������������������������������������������������������������������������� 25
Ventajas������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ 25
Desafíos������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ 25
Arquitectura de IoT ������������������������������������������������������������������������������������������������������������������������������������������������������ 26
Capítulo 1f: Estilo de arquitectura de Big Data ������������������������������������������������������������������������������ 27
Ventajas ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������� 29
Desafíos ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������� 29
Procedimientos recomendados ������������������������������������������������������������������������������������������������������������������������������ 30
Capítulo 1g: Estilo de arquitectura de Big Compute ���������������������������������������������������������������������� 31
Cuándo utilizar esta arquitectura ��������������������������������������������������������������������������������������������������������������������������� 32
Ventajas ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������� 32
Desafíos ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������� 32
Big compute con Azure Batch ��������������������������������������������������������������������������������������������������������������������������������� 33
Ejecución de Big Compute en máquinas virtuales ������������������������������������������������������������������������������������������ 33
Capítulo 2: Elección de tecnologías para procesos y almacenes de datos ���������������������������������� 35
Capítulo 2a: Información general sobre las opciones de proceso ������������������������������������������������ 37
Capítulo 2b: Comparación de procesos ������������������������������������������������������������������������������������������� 39
Modelo de hospedaje ������������������������������������������������������������������������������������������������������������������������������������������������ 39
DevOps ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������� 40
Escalabilidad ������������������������������������������������������������������������������������������������������������������������������������������������������������������� 41
Disponibilidad ���������������������������������������������������������������������������������������������������������������������������������������������������������������� 41
Seguridad ������������������������������������������������������������������������������������������������������������������������������������������������������������������������ 42
Otro ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ 42
Capítulo 2c: Información general sobre los almacenes de datos ������������������������������������������������� 43
Sistemas de administración de bases de datos relacionales ����������������������������������������������������������������������� 44
Almacenes de claves/valores ����������������������������������������������������������������������������������������������������������������������������������� 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 claves/valores ����������������������������������������������������������������������������������������������������������������������������������� 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
Almacén de datos �������������������������������������������������������������������������������������������������������������������������������������������������������� 57
Bases de datos de series temporales �������������������������������������������������������������������������������������������������������������������� 58
Almacenamiento de objetos ������������������������������������������������������������������������������������������������������������������������������������ 58
Archivos compartidos ������������������������������������������������������������������������������������������������������������������������������������������������� 59
Capítulo 3: Diseñe su aplicación de Azure: principios del diseño ������������������������������������������������� 60
Capítulo 3a: Diseño para recuperación automática ����������������������������������������������������������������������� 62
Recomendaciones �������������������������������������������������������������������������������������������������������������������������������������������������������� 62
Capítulo 3b: Hacer redundantes todas las cosas ���������������������������������������������������������������������������� 64
Recomendaciones �������������������������������������������������������������������������������������������������������������������������������������������������������� 64
Capítulo 3c: Minimizar la coordinación ������������������������������������������������������������������������������������������� 66
Recomendaciones �������������������������������������������������������������������������������������������������������������������������������������������������������� 67
Capítulo 3d: Diseño para escalado horizontal �������������������������������������������������������������������������������� 69
Recomendaciones �������������������������������������������������������������������������������������������������������������������������������������������������������� 69
Capítulo 3e: Partición alrededor de límites ������������������������������������������������������������������������������������� 71
Recomendaciones �������������������������������������������������������������������������������������������������������������������������������������������������������� 72
Capítulo 3f: Diseño para 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 evolución �������������������������������������������������������������������������������������������������� 78
Recomendaciones �������������������������������������������������������������������������������������������������������������������������������������������������������� 78
Capítulo 3j: Creación para las necesidades del negocio ���������������������������������������������������������������� 80
Recomendaciones �������������������������������������������������������������������������������������������������������������������������������������������������������� 80
Capítulo 3k: Diseño de aplicaciones resistentes para Azure ��������������������������������������������������������� 82
¿Qué es la resistencia? ������������������������������������������������������������������������������������������������������������������������������������������������ 82
Proceso para lograr resistencia ������������������������������������������������������������������������������������������������������������������������������� 83
Definición de los requisitos de resistencia ��������������������������������������������������������������������������������������������������������� 83
Diseño para resistencia ���������������������������������������������������������������������������������������������������������������������������������������������� 87
Estrategias de resistencia ����������������������������������������������������������������������������������������������������������������������������������������� 87
Implementación resistente ��������������������������������������������������������������������������������������������������������������������������������������� 91
Supervisión y diagnóstico ����������������������������������������������������������������������������������������������������������������������������������������� 92
Respuestas a errores manuales ������������������������������������������������������������������������������������������������������������������������������� 93
Resumen �������������������������������������������������������������������������������������������������������������������������������������������������������������������������� 94
Capítulo 4: Diseñe su aplicación de Azure: use estos pilares de calidad ������������������������������������� 95
Escalabilidad ������������������������������������������������������������������������������������������������������������������������������������������������������������������� 96
Disponibilidad ���������������������������������������������������������������������������������������������������������������������������������������������������������������� 98
Resistencia ����������������������������������������������������������������������������������������������������������������������������������������������������������������������� 99

iv Contenido
Administración y DevOps ���������������������������������������������������������������������������������������������������������������������������������������� 100
Seguridad ���������������������������������������������������������������������������������������������������������������������������������������������������������������������� 101
Capítulo 5: Diseñar su aplicación de Azure: patrones de diseño ������������������������������������������������ 103
Desafíos en el desarrollo de la nube ������������������������������������������������������������������������������������������������������������������ 103
Administración de datos ����������������������������������������������������������������������������������������������������������������������������������������� 104
Diseño e implementación ��������������������������������������������������������������������������������������������������������������������������������������� 104
Mensajes ������������������������������������������������������������������������������������������������������������������������������������������������������������������������ 105
Administración y monitoreo ���������������������������������������������������������������������������������������������������������������������������������� 106
Rendimiento y escalabilidad ���������������������������������������������������������������������������������������������������������������������������������� 107
Resistencia ��������������������������������������������������������������������������������������������������������������������������������������������������������������������� 108
Seguridad ���������������������������������������������������������������������������������������������������������������������������������������������������������������������� 109
Capítulo 6: Catálogo de patrones ��������������������������������������������������������������������������������������������������� 110
Patrón de embajador ������������������������������������������������������������������������������������������������������������������������������������������������ 110
Patrón de capa contra la corrupción ������������������������������������������������������������������������������������������������������������������ 112
Backends para el patrón de frontends ��������������������������������������������������������������������������������������������������������������� 114
Patrón de cierre ����������������������������������������������������������������������������������������������������������������������������������������������������������� 116
Patrón Cache-Aside ��������������������������������������������������������������������������������������������������������������������������������������������������� 119
Patrón de interruptor ������������������������������������������������������������������������������������������������������������������������������������������������ 124
Patrón CQRS (segregación de responsabilidad de consultas y comandos) ���������������������������������������� 132
Patrón de transacción compensatoria ���������������������������������������������������������������������������������������������������������������� 139
Patrón de consumidores competitivos �������������������������������������������������������������������������������������������������������������� 143
Patrón de consolidación de recursos informáticos ��������������������������������������������������������������������������������������� 148
Patrón de abastecimiento de eventos ���������������������������������������������������������������������������������������������������������������� 156
Patrón del almacén de configuración externa ������������������������������������������������������������������������������������������������ 162
Patrón de identidad federada �������������������������������������������������������������������������������������������������������������������������������� 170
Patrón del equipo selector (gatekeeper) ����������������������������������������������������������������������������������������������������������� 174
Patrón de agregación de gateway ����������������������������������������������������������������������������������������������������������������������� 176
Patrón de descarga de gateway ��������������������������������������������������������������������������������������������������������������������������� 180
Patrón de enrutamiento de gateway ������������������������������������������������������������������������������������������������������������������ 182
Patrón de supervisión de punto 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 para 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 ���������������������������������������������������������������������������������������������������������������� 278
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
DMZ de red ������������������������������������������������������������������������������������������������������������������������������������������������������������������� 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
la nube
Esta guía presenta un enfoque estructurado para el diseño de aplicaciones en
la nube que sean escalables, resistentes y altamente disponibles. La orientación
de este eBook pretende lograr que tome las mejores decisiones arquitectónicas
independientemente de la plataforma de nube que tenga; sin embargo, vamos
a usar Azure para poder compartir los procedimientos recomendados que
hemos aprendido gracias a muchos años de interacciones con los clientes.

En los capítulos siguientes, lo guiaremos por una selección de importantes


consideraciones y recursos que le permitirán determinar el mejor enfoque para
su aplicación en la nube:

1.  Elección del estilo de arquitectura adecuado para su aplicación en función del
tipo de solución que creará.

2. Elección de las tecnologías más adecuadas para procesos y almacenes de


datos.

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


que su aplicación sea escalable, resistente y administrable.

4. Utilización de los cinco pilares de calidad de software para crear una


aplicación exitosa.

5. Aplicación de patrones de diseño específicos del problema que intenta


solucionar.

vii Introducción
Introducción
La nube 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 las
API o mediante mensajes o eventos asincrónicos. Las aplicaciones se escalan
horizontalmente, agregando nuevas instancias en función de la demanda.
Estas tendencias traen nuevos desafíos. 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 resistente cuando se
producen errores. Las implementaciones deben ser automatizadas y predecibles. La supervisión y la
telemetría son fundamentales para obtener información sobre el sistema. La Guía de arquitectura de
aplicaciones de Azure está diseñada para ayudarlo a navegar por estos cambios.

Entorno local tradicional Nube moderna


• Monolítico, centralizado • Descompuesta, descentralizada
• Diseño para escalabilidad predecible • Diseño para escala elástica
• Base de datos relacional • Persistencia políglota (combinación de
• Coherencia fuerte tecnologías de almacenamiento)
• Procesamiento sincronizado y en serie • Coherencia final
• Diseño para evitar fallas (MTBF) • Procesamiento asincrónico y en paralelo
• Actualizaciones grandes ocasionales • Diseño para errores (MTTR)
• Administración manual • Pequeñas actualizaciones frecuentes
• Servidores "en copo de nieve" • Autoadministración automatizada
• Infraestructura inmutable

La nube 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 las API o mediante mensajes o eventos asincrónicos. Las aplicaciones se
escalan horizontalmente, agregando nuevas instancias en función de la demanda.

Estas tendencias traen nuevos desafíos. 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 resistente cuando se
producen errores. Las implementaciones deben ser automatizadas y predecibles. La supervisión y la
telemetría son fundamentales para obtener información sobre el sistema. La Guía de arquitectura de
aplicaciones en la nube está diseñada para ayudarlo a navegar por estos cambios.

Cómo está estructurada esta guía


La Guía de arquitectura de aplicaciones en la nube está organizada como una serie de pasos, desde
la arquitectura y el diseño hasta la implementación. En cada paso, hay orientaciones de apoyo que lo
ayudarán con el diseño de la arquitectura de su aplicación.

viii
Estilos de arquitectura. El primer punto de decisión es el más fundamental. ¿Qué tipo de
arquitectura creará? Podría ser una arquitectura de microservicios, una aplicación de n niveles más
tradicional o una solución de Big Data. Hemos identificado siete estilos de arquitectura distintos.
Cada uno tiene sus ventajas y desafíos.

• Las arquitecturas de referencia de Azure muestran implementaciones recomendadas en Azure,


junto con consideraciones de escalabilidad, disponibilidad, capacidad de administración
y seguridad. La mayoría también incluye plantillas de Resource Manager que se pueden
implementar.

Opciones de tecnología. Se deben decidir dos opciones de tecnología desde un primer momento,
porque afectan a toda la arquitectura. Esto se refiere a la elección de tecnologías para procesos y
almacenamiento. El término "procesos" hace referencia al modelo de hospedaje para los recursos
informáticos en que se ejecutan sus aplicaciones. El almacenamiento incluye no solo bases de
datos, sino también almacenamiento para colas de mensajes, memorias caché, datos de IoT, datos
de registro no estructurados y cualquier otra cosa que una aplicación pueda persistir para su
almacenamiento.

• Las opciones de procesos y las opciones de almacenamiento proporcionan criterios de


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

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

• Para ver artículos con 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,
consulte https://docs.microsoft.com/en-us/azure/architecturebest-practices/index.

Pilares. Una aplicación exitosa en la nube se centrará en estos cinco pilares de calidad de software:
escalabilidad, disponibilidad, resistencia, administración y seguridad.

• Use nuestras listas de comprobación para revisión del diseño para revisar su diseño de acuerdo
con estos pilares de calidad.

Patrones de diseño en la nube. Estos patrones de diseño resultan útiles para crear aplicaciones
confiables, escalables y seguras en Azure. 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 en la nube.

Antes de comenzar Obtenga


Si no todavía no lo ha hecho, cree una cuenta gratuita de Azure para que ayuda de los
expertos
pueda practicar con este eBook.
• Un crédito de USD 200 para utilizar en cualquier producto Azure por 30 días.
• Acceso gratuito por 12 meses a nuestros productos más populares, incluidos
los procesos, las redes de almacenamiento y las bases de datos. Póngase en contacto con
nosotros en
• Más de 25 productos que son siempre gratuitos.
aka.ms/azurespecialist

ix Introducción
1

Elección de un estilo
de arquitectura
La primera decisión que debe tomar a la hora de diseñar una aplicación
en la nube es la arquitectura. Seleccione la mejor arquitectura para la
aplicación que creará sobre la base de su complejidad, el tipo de dominio,
si es una aplicación IaaS o PaaS y lo que hará la aplicación. También debe
considerar las habilidades de los equipos de desarrolladores y de DevOps,
y si la aplicación dispone de una arquitectura existente.
Un estilo de arquitectura impone restricciones sobre el diseño, las cuales guían la "forma" de un
estilo de arquitectura ya que restringen las opciones. Estas restricciones proporcionan ventajas
y desafíos para el diseño. Utilice la información de esta sección para entender cuáles son las
compensaciones a la hora de adoptar cualquiera de estos estilos.

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

Hemos identificado un conjunto de estilos de arquitectura que se encuentran comúnmente en las


aplicaciones en la nube. El artículo sobre cada estilo incluye:

• Una descripción y un diagrama lógico del estilo.


• Recomendaciones para cuándo elegir este estilo.
• Ventajas, desafíos y procedimientos recomendados.
• Una implementación recomendada con los servicios de Azure pertinentes.

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


Un recorrido rápido por los estilos
Esta sección brinda un recorrido rápido por los estilos de arquitectura que hemos identificado, junto
con algunas consideraciones de alto nivel para su uso. Lea más detalles en los temas vinculados.

Arquitectura de n niveles
La arquitectura de n niveles es una arquitectura tradicional para aplicaciones empresariales. Las
dependencias se administran mediante la división de la aplicación en niveles que realizan funciones lógicas,
como presentación, lógica de negocios y acceso a datos. Un nivel solo puede convocar a los niveles que se
encuentran por debajo. Sin embargo, esta nivelación horizontal puede ser una desventaja, ya que podría
resultar difícil introducir cambios en una parte de la aplicación sin tocar el resto de la aplicación. Esto hace
que las actualizaciones frecuentes sean un desafío, pues limitan la rapidez con la que se pueden agregar
nuevas funciones.
La arquitectura de n niveles es una elección natural para migrar las aplicaciones existentes que ya utilizan
una arquitectura de capas. Por esa razón, la arquitectura de n niveles se ve más a menudo en las soluciones
de infraestructura como servicio (IaaS) o en las aplicaciones que utilizan una combinación de IaaS y servicios
administrados.

Web-cola-trabajador
Para obtener una solución puramente PaaS, considere una arquitectura web-cola-trabajador. En este estilo,
la aplicación cuenta con un front-end web que controla las solicitudes de HTTP y un trabajador de back-end
que realiza las tareas con uso intensivo de CPU o las operaciones de ejecución prolongada. El front-end se
comunica con el trabajador a través de una cola de mensaje asincrónico.
La arquitectura web-cola-trabajador es adecuada para los dominios relativamente simples con algunas tareas
de uso intensivo de recursos. Al igual que la arquitectura de n niveles, esta arquitectura también es fácil de
entender. El uso de servicios administrados simplifica la implementación y las operaciones. Pero en el caso
de los dominios complejos, puede resultar difícil administrar las dependencias. El front-end y el trabajador
pueden convertirse fácilmente en componentes grandes y monolíticos que son difíciles de mantener y
actualizar. De la misma forma que sucede con la arquitectura de n niveles, este tipo de arquitectura puede
reducir la frecuencia de 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, considere la posibilidad de migrar a una arquitectura
de microservicios. Una aplicación de microservicios se compone de muchos servicios pequeños e
independientes. Cada servicio implementa una funcionalidad de negocio única. Los servicios se combinan
libremente y se comunican a través de contratos de API.
Cada servicio lo puede crear un equipo de desarrollo pequeño y enfocado. Los servicios individuales
se pueden implementar sin mucha coordinación entre los equipos, lo que fomenta las actualizaciones
frecuentes. Una arquitectura de microservicios es más compleja de crear y administrar que las arquitecturas
de n niveles o de web-cola-trabajador. Requiere un desarrollo maduro y una cultura de DevOps. Pero bien
hecho, este estilo puede conducir a una mayor velocidad de lanzamiento, una innovación más rápida y una
arquitectura más resistente.

CQRS
El estilo CQRS (segregación de responsabilidad de consultas y comandos) separa las operaciones de lectura
y escritura en modelos diferentes. Esto aísla las partes del sistema que actualizan los datos de las partes
que leen los datos. Por otra parte, las lecturas pueden ejecutarse contra una vista materializada que está
físicamente separada de la base de datos de escritura. Esto le permite escalar las cargas de trabajo de lectura
y escritura de forma independiente y optimizar la vista materializada para consultas.
El estilo CQRS tiene más sentido cuando se aplica a un subsistema de una arquitectura más grande. Por
lo general, no debe imponerlo en toda la aplicación, ya que eso solo crearía complejidad innecesaria.
Considérelo para los dominios de colaboración en donde muchos usuarios acceden a los mismos datos.

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


Arquitectura basada en eventos
Las arquitecturas basadas en eventos utilizan un modelo de publicación/suscripción (pub/sus),
donde los productores publican eventos y los consumidores se suscriben a ellos. Los productores
son independientes de los consumidores y los consumidores son independientes uno del otro.

Considere una arquitectura basada en eventos para las aplicaciones que ingieren y procesan un gran
volumen de datos con una latencia muy baja, como las soluciones de IoT. Este estilo también resulta
útil cuando diferentes subsistemas deben realizar diferentes tipos de procesamiento en los mismos
datos de eventos.

Big Data, Big Compute


Big Data y Big Compute son estilos de arquitectura especializados para cargas de trabajo que
se ajustan a ciertos perfiles específicos. Big Data divide un conjunto de datos muy grande en
fragmentos y realiza un procesamiento en paralelo en todo el conjunto, para generar análisis e
informes. Big Compute, también llamado informática de alto rendimiento (HPC), realiza cómputos
paralelos en una gran cantidad (miles) de núcleos. Los dominios incluyen simulaciones, modelado y
representación 3D.

Estilos de arquitectura a modo de restricciones


Un estilo de arquitectura impone restricciones sobre el diseño, incluido el conjunto de elementos
que pueden aparecer y las relaciones permitidas entre esos elementos. Las restricciones guían la
"forma" de una arquitectura ya que restringen el universo de opciones. Cuando una arquitectura se
ajusta a las restricciones de un estilo determinado, emergen ciertas propiedades deseables.

Por ejemplo, las restricciones en microservicios incluyen:

• Un servicio representa una responsabilidad única.


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

Al cumplir con estas restricciones, lo que emerge es un sistema donde los servicios pueden
implementarse de forma independiente, los errores se aíslan, las actualizaciones frecuentes son
posibles y es fácil introducir nuevas tecnologías en la aplicación.

Antes de elegir un estilo de arquitectura, asegúrese de comprender los principios subyacentes y las

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


restricciones de ese estilo. De lo contrario, puede terminar con un diseño que se ajusta al estilo en
un nivel superficial, pero que no logra todo el potencial de ese estilo. También es importante ser
pragmático. A veces es mejor mitigar una restricción, en lugar de insistir en la pureza arquitectónica.

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

Estilo de arquitectura Administración de dependencias Tipo de dominio


De n niveles Niveles horizontales divididos por subred. Dominio de negocio tradicional. La frecuencia de
actualizaciones es baja.

Web-cola-trabajador Trabajos de front-end y back-end, Dominio relativamente simple con algunas


desacoplados por mensajería asincrónica. tareas de uso intensivo de recursos.
Servicios descompuestos verticalmente (de
Microservicios Dominio complicado. Actualizaciones frecuentes.
manera funcional) que se llaman entre sí a
través de las API.
CQRS Segregación de lectura/escritura. El esquema Dominio de colaboración donde muchos
y la escala están optimizados por separado. usuarios acceden a los mismos datos.

Arquitectura basada en Productor/consumidor. Vista independiente IoT y sistemas en tiempo real.


eventos por subsistema.

Un conjunto enorme de datos se divide Análisis de datos por lotes y en tiempo real.
Big Data en fragmentos pequeños. Procesamiento Análisis predictivo con ML.
paralelo en conjuntos de datos locales.
Dominios que requieren gran cantidad de
Big Compute Asignación de datos a miles de núcleos. recursos informáticos, como la simulación.

Desafíos y ventajas por considerar


Las restricciones también crean desafíos, por lo que es importante entender las compensaciones a
la hora de adoptar cualquiera de estos estilos. ¿Las ventajas del estilo de arquitectura superan los
desafíos, para este subdominio y contexto acotado?
Estos son algunos de los tipos de desafíos por considerar a la hora de seleccionar un estilo de
arquitectura:
● Complejidad. ¿Se justifica la complejidad de la arquitectura por su dominio? Por el contrario,
¿es demasiado simplista el estilo para su dominio? En ese caso, corre el riesgo de terminar con
una "pelota de barro", porque la arquitectura no lo ayudará a administrar las dependencias de
forma limpia.

● Mensajería asincrónica y coherencia final. La mensajería asincrónica permite desacoplar


servicios y aumentar la confiabilidad (porque los mensajes se pueden reintentar) y la
escalabilidad. Sin embargo, esto también crea desafíos, como la semántica de "siempre una vez"
y la coherencia final.

● Comunicación entre servicios. A medida que se descompone una aplicación en servicios


separados, existe el riesgo de que la comunicación entre los servicios provoque una latencia
inaceptable o genere congestión en la red (por ejemplo, en una arquitectura de microservicios).

● Capacidad de administración. ¿Qué tan difícil es administrar la aplicación, supervisar,


implementar actualizaciones, y así sucesivamente?

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


1a

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

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

Los niveles están separados físicamente y se ejecutan en máquinas separadas. Un nivel puede llamar
directamente a otro nivel o bien utilizar la mensajería asincrónica (cola de mensajes). Aunque cada capa
podría alojarse en su propio nivel, no es algo 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 latencia debido a
la comunicación adicional de red.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 de arriba muestra una aplicación con dos niveles intermedios,
que encapsulan diferentes áreas de funcionalidad.

6 CAPÍTULO 1a | Estilo de arquitectura de n niveles


Una aplicación de n niveles 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 que está
inmediatamente debajo.
• En una arquitectura de capa abierta, una capa puede llamar a cualquiera de las capas que se
encuentran debajo.
Una arquitectura de capa cerrada limita las dependencias entre las capas. Sin embargo, podría crear
tráfico de red innecesario, si una capa simplemente pasa las solicitudes hacia la siguiente capa.

Cuándo utilizar esta arquitectura


Las arquitecturas de n niveles se implementan normalmente como aplicaciones de infraestructura
como servicio (IaaS), en que cada nivel se ejecuta en un conjunto separado de máquinas virtuales. Sin
embargo, no es necesario que una aplicación de n niveles sea de IaaS pura. Con frecuencia, resulta
conveniente utilizar servicios administrados para algunas partes de la arquitectura, especialmente
para el almacenamiento en caché, la mensajería y el almacenamiento de datos.

Considere una arquitectura de n niveles para:

• Aplicaciones web simples.


• Migración de una aplicación local a Azure con refactorización mínima.
• Desarrollo unificado de aplicaciones locales y en la nube.
Las arquitecturas de n niveles son muy comunes en las aplicaciones locales tradicionales, de modo
que es una elección natural para migrar cargas de trabajo existentes a Azure.

Ventajas
• Portabilidad entre la nube y los entornos locales y entre las plataformas de nube.
• Menor curva de aprendizaje para la mayoría de los desarrolladores.
• Evolución natural del modelo tradicional de aplicaciones.
• Abierta a entornos heterogéneos (Windows/Linux)

Desafíos
• Es fácil terminar con un nivel intermedio que simplemente realiza las operaciones CRUD en la
base de datos, agregando latencia adicional sin hacer ningún trabajo útil.
• El diseño monolítico impide la implementación independiente de características.
• La administración de una aplicación IaaS genera más trabajo que una aplicación que utiliza
solamente servicios administrados.
• Puede resultar difícil administrar la seguridad de la red en un sistema grande.

7 CAPÍTULO 1a | Estilo de arquitectura de n niveles


Procedimientos recomendados
• Utilice escalado automático para manejar los cambios en la carga. Consulte Procedimientos
recomendados de escalado automático.
• Utilice la mensajería asincrónica para desacoplar niveles.
• Almacene en caché los datos semiestáticos. Consulte Procedimientos recomendados para el
almacenamiento en caché.
• Configure el nivel de base de datos para alta disponibilidad, utilizando una solución como los
Grupos de disponibilidad Always On 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 las subredes como límites de seguridad.
• Restrinja el acceso al nivel de datos, permitiendo solicitudes que solo provengan de los niveles
intermedios.

Arquitectura de n niveles en máquinas virtuales


Esta sección describe una arquitectura de n niveles recomendada que se ejecuta en máquinas virtuales.

Esta sección describe una arquitectura de n niveles recomendada que se ejecuta en máquinas virtuales. Cada
nivel consta de dos o más máquinas virtuales, colocadas en un conjunto de disponibilidad o en un conjunto
de escala de VM. Múltiples máquinas virtuales proporcionan resistencia en caso de que una de ellas presente
errores. Se utilizan equilibradores de carga para distribuir las solicitudes entre las VM de un nivel. Un nivel puede
escalarse horizontalmente añadiendo más máquinas virtuales al grupo.

Cada nivel se coloca también dentro de su propia subred, lo que significa que las direcciones IP internas se
encuentran en el mismo rango de direcciones. Esto facilita la aplicación de reglas de grupo de seguridad de red
(NSG) y la redirección de tablas a niveles individuales.

Los niveles de web y de negocio no tienen estado. Cualquier VM puede manejar cualquier solicitud para ese
nivel. El nivel de datos debe consistir en una base de datos replicada. Para Windows, recomendamos SQL Server,
utilizando Grupos de disponibilidad Always On para alta disponibilidad. Para Linux, elija una base de datos que
admita 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 de negocio.

8 CAPÍTULO 1a | Estilo de arquitectura de n niveles


Para obtener más detalles y una plantilla de Resource Manager que se pueda implementar, consulte
las siguientes arquitecturas de referencia:

• Ejecutar máquinas virtuales Windows para una aplicación de n niveles


• Ejecutar máquinas virtuales Linux para una aplicación de n niveles

Consideraciones adicionales
• Las arquitecturas de n niveles no están restringidas a tres niveles. Para aplicaciones más
complejas, es común tener más niveles. En ese caso, considere el uso del enrutamiento de capa 7
para redirigir las solicitudes a un nivel específico.

• Los niveles son los límites de escalabilidad, confiabilidad y seguridad. Considere la posibilidad de
tener niveles separados para los servicios con diversos requisitos en esas áreas.

• Utilice los conjuntos de escala de VM para el escalado automático.

• Busque lugares en la arquitectura donde pueda utilizar un servicio administrado sin


refactorización significativa. En particular, fíjese en la memoria caché, la mensajería, el
almacenamiento y las bases de datos.

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

• Para alta disponibilidad, coloque dos o más NVA en un conjunto de disponibilidad, con un
equilibrador de carga externo para distribuir las solicitudes de Internet entre las instancias.
Para obtener más información, consulte 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 su lugar, los operadores deben iniciar sesión en un jumpbox, también llamado
host bastión. Se trata de una máquina virtual en la red que los administradores utilizan para
conectarse a las otras VM. El jumpbox tiene una NSG que permite RDP o SSH solo desde
direcciones IP públicas aprobadas.

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

• Si su organización utiliza Active Directory para administrar la identidad, conviene extender el


entorno de Active Directory a Azure VNet. Para obtener más información, consulte Arquitectura
de referencia de administración de identidad.

• Si necesita una mayor disponibilidad de la que ofrece Azure SLA para VM, replique la aplicación
en dos regiones y utilice Azure Traffic Manager para la conmutación por error. Para obtener más
información, consulte Ejecutar máquinas virtuales Windows en múltiples regiones o Ejecutar
máquinas virtuales Linux en múltiples regiones.

9 CAPÍTULO 1a | Estilo de arquitectura de n niveles


1b

Estilo de
arquitectura
web-cola-trabajador
Los componentes básicos de esta arquitectura son un front-end web que atiende las
solicitudes de los clientes y un trabajador que ejecuta las tareas con uso intensivo de
recursos, los flujos de trabajo de larga duración o los trabajos por lotes. El front-end
web se comunica con el trabajador a través de una cola de mensajes.

Otros componentes que habitualmente se incorporan en 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 atender el contenido estático.

10 CAPÍTULO 1b | Estilo de arquitectura web-cola-trabajador


Tanto la web como el trabajador no tienen estado. El estado de la sesión se puede almacenar en
una memoria caché distribuida. El trabajador realiza cualquier trabajo de larga duración de forma
asincrónica. El trabajador se puede activar mediante mensajes en la cola o ejecutarse en una
programación para procesamiento por lotes. El trabajador es un componente opcional. Si no hay
ninguna operación de larga duración, el trabajador puede omitirse.

El front-end puede ser una API web. En el lado del cliente, la API web puede ser consumida por una
aplicación de una sola página que hace las llamadas AJAX o por una aplicación cliente nativa.

Cuándo utilizar esta arquitectura


La arquitectura web-cola-trabajador normalmente se implementa con servicios de procesos
administrados, ya sea Azure App Service o Azure Cloud Services.

Tenga en cuenta este estilo de arquitectura para:


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

Ventajas
• Arquitectura relativamente simple que es fácil de entender.
• Fácil de implementar y administrar.
• Clara separación de las inquietudes.
• El front-end se desacopla de trabajador por medio de mensajería asincrónica.
• El front-end y el trabajador pueden escalarse de forma independiente.

Desafíos
• Sin un diseño cuidadoso, el font-end y el trabajador pueden convertirse en componentes
grandes y monolíticos que son difíciles de mantener y actualizar.
• Puede haber dependencias ocultas, si el front-end y el trabajador comparten esquemas de datos
o módulos de código.

Procedimientos recomendados
• Utilice persistencia políglota cuando sea apropiado. Consulte Uso del mejor almacén de datos
para el trabajo.
• Para ver artículos con 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,
consulte https://docs.microsoft.com/en-us/azure/architecture/best-practices/index.

11 CAPÍTULO 1b | Estilo de arquitectura web-cola-trabajador


Web-cola-trabajador en Azure App Service
Esta sección describe una arquitectura web-cola-trabajador recomendada que utiliza Azure App Service.

El front-end se implementa como una aplicación web de Azure App Service y el trabajador se
implementa como un WebJob. La aplicación web y el WebJob están asociados con un plan del
Servicio de aplicaciones que proporciona las instancias de VM.

Para la cola de mensajes, puede utilizar colas de Azure Service Bus o de Azure Storage. (El diagrama
muestra una cola de Azure Storage).

Azul 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é el 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. Podría 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, consulte Arquitectura de referencia de aplicación web administrada.

Consideraciones adicionales
• No todas las transacciones deben pasar por la cola y el trabajador para su almacenamiento. El
front-end web puede realizar operaciones simples de lectura/escritura de forma directa. Los
trabajadores están diseñados para tareas con uso intensivo de recursos o para flujos de trabajo
de larga duración. En algunos casos, es posible que no necesite un trabajador en absoluto.
• Utilice la función de escalado automático incorporada en App Service para realizar un escalado
horizontal del número de instancias de VM. 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-cola-trabajador


• Considere la posibilidad de poner la aplicación web y el WebJob en planes independientes de
App Service. De esa forma, quedan alojados en instancias diferentes de VM 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 las
máquinas virtuales de producción.
• Utilice ranuras de implementación para administrar las implementaciones. Esto permite
implementar una versión actualizada en una ranura de ensayo, entonces puede cambiar a la
nueva versión. También permite volver a cambiar a la versión anterior, si se produjo un problema
con la actualización.

13 CAPÍTULO 1b | Estilo de arquitectura web-cola-trabajador


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 debe implementar
una funcionalidad de negocio única.

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

• En una arquitectura de microservicios, los servicios son pequeños, independientes y con una
conexión flexible.
• Cada servicio es una base de código diferente, que puede ser administrada por un pequeño
equipo de desarrollo.
• Los servicios pueden implementarse de forma independiente. Un equipo puede actualizar un
servicio existente sin tener que reconstruir ni volver a implementar 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 separada maneja la persistencia de los datos.

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


• Los servicios se comunican entre sí mediante el uso de API bien definidas. Los detalles internos
de la implementación de cada servicio quedan ocultos de los otros servicios.
• No es necesario que los servicios compartan la misma pila tecnológica, las mismas bibliotecas o
los mismos marcos.

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

Administración. El componente de administración es responsable de colocar los servicios en nodos,


identificar errores, reequilibrar los servicios entre los nodos, y así sucesivamente.

Detección de servicios. Mantiene una lista de servicios y en qué nodos se ubican. Permite búsqueda
de servicio para encontrar el punto de conexión de un servicio.

Gateway de API. El gateway de API es el punto de entrada para los clientes. Los clientes no llaman
directamente a los servicios. En cambio, llaman al gateway de API, que remite la llamada a los
servicios apropiados en el back-end. El gateway de API podría agregar las respuestas de varios
servicios y devolver la respuesta agregada.

Las ventajas de utilizar un gateway de API incluyen:

• Desacopla clientes de los servicios. Los servicios se pueden versionar o rediseñar sin necesidad
de actualizar todos los clientes.
• Los servicios pueden utilizar protocolos de mensajería que no son aptos para la web, como
AMQP.
• El gateway de API puede realizar otras funciones transversales como autenticación, registro,
terminación de SSL y equilibrio de carga.

Cuándo utilizar esta arquitectura


Tenga en cuenta este estilo de arquitectura para:

• Aplicaciones de gran tamaño que requieren una alta velocidad de lanzamiento.


• Aplicaciones complejas que necesitan ser altamente escalables.
• Aplicaciones con dominios enriquecidos o muchos subdominios.
• Una organización que está conformada por pequeños equipos de desarrollo.

Ventajas
● Implementaciones independientes. Puede actualizar un servicio sin volver a implementar toda la
aplicación y revertir o aplazar una actualización si algo sale mal. Las correcciones de errores y los
lanzamientos de características son más manejables y menos riesgosos.
● Desarrollo independiente. Un único equipo de desarrollo puede crear, probar e implementar un
servicio. El resultado es la innovación continua y una cadencia más rápida de lanzamientos.

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


● Equipos pequeños y enfocados. Los equipos pueden centrarse en un solo servicio. El alcance más
pequeño de cada servicio facilita la comprensión de la base de código, con lo cual a los nuevos
miembros de los equipos les resulta más fácil acelerar el ritmo.
● Aislamiento de errores. Si un servicio se cae, no se eliminará toda la aplicación. Sin embargo, eso no
quiere decir que obtenga resistencia de forma gratuita. Debe seguir cumpliendo los procedimientos
recomendados y los patrones de diseño para resistencia. Consulte Diseño de aplicaciones resistentes
para Azure.
● Pilas tecnológicas mixtas. Los equipos pueden elegir la tecnología que mejor se adapte a su servicio.
● Escalado granular. Los servicios pueden escalarse de forma independiente. Al mismo tiempo,
la mayor densidad de servicios por VM indica que los recursos de máquinas virtuales se utilizan
plenamente. Si se emplean restricciones de colocación, un servicio puede ser emparejado con un
perfil de VM (gran CPU, alta memoria, y así sucesivamente).

Desafíos
● Complejidad. Una aplicación de microservicios tiene más partes móviles que la aplicación
monolítica equivalente. Cada servicio es más simple, pero todo el sistema en su conjunto es más
complejo.
● Desarrollo y pruebas. El desarrollo contra las dependencias de servicio requiere un enfoque
diferente. Las herramientas existentes no están diseñadas necesariamente para funcionar con
dependencias de servicio. Puede resultar difícil la refactorización en los límites del servicio.
También es un desafío probar las dependencias de servicio, especialmente cuando la aplicación
evoluciona rápidamente.
● Falta de gobernanza. El enfoque descentralizado para la creación de microservicios tiene sus
ventajas, pero también puede conducir a problemas. Es posible que termine con tantos lenguajes
y marcos diferentes que la aplicación se hace difícil de mantener. Puede resultar útil establecer
algunas normas en todo el proyecto, sin restringir demasiado la flexibilidad de los equipos. Esto
se aplica especialmente a una funcionalidad transversal como el registro.
● Congestión y latencia de la red. La utilización de muchos servicios pequeños y granulares
puede dar como resultado más comunicación entre los servicios. Además, si la cadena de
dependencias de servicio se vuelve demasiado larga (el servicio A llama al B, que llama al C...), la
latencia adicional puede llegar a ser un problema. Deberá diseñar las API cuidadosamente. Evite
las API demasiado locuaces, piense en los formatos de serialización y busque lugares para usar
patrones de comunicación asincrónica.
● Integridad de los datos. Con cada microservicio responsable de su propia persistencia de datos.
Como resultado, la coherencia de los datos puede ser un desafío. Acepte la coherencia final
siempre que sea posible.
● Administración. Para tener éxito con los microservicios se requiere una cultura madura de
DevOps. El registro correlacionado entre los servicios puede ser difícil. Por lo general, el registro
debe correlacionar múltiples llamadas de servicio para una sola operación del usuario.
● Control de versiones. Las actualizaciones de un servicio no deben quebrantar los servicios que
dependen de él. Se podrían actualizar múltiples servicios en cualquier momento, de modo que
sin un diseño cuidadoso, puede tener problemas con la compatibilidad con versiones anteriores
o posteriores.
● Conjunto de habilidades. Los microservicios son sistemas altamente distribuidos. Evalúe
cuidadosamente si el equipo cuenta con las habilidades y la experiencia para tener éxito.

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


Procedimientos recomendados
• Servicios de modelo en torno al dominio de negocio.

• Descentralice todo. Los equipos individuales son responsables del diseño y la creación de
servicios. Evitar compartir esquemas de código o de datos.

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

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

• Evite el acoplamiento entre servicios. Entre las causas de acoplamiento se incluyen los esquemas
compartidos de bases de datos y los protocolos de comunicación rígidos.

• Descargue las inquietudes transversales, como la autenticación y la terminación de SSL, en el


gateway.
• Mantenga el conocimiento del dominio fuera del gateway. El gateway debería manejar y
redirigir las solicitudes de los clientes sin ningún conocimiento de las reglas de negocio ni de la
lógica de dominio. De lo contrario, el gateway se convierte en una dependencia y puede causar
acoplamiento entre los servicios.

• Los servicios deben tener acoplamiento flexible y alta cohesión funcional. Las funciones que
posiblemente cambian juntas se deben empaquetar e implementar en conjunto. Si residen en
diferentes servicios, esos servicios terminarán acoplados estrechamente, porque un cambio en
un servicio requerirá la actualización del otro servicio. La comunicación demasiado locuaz entre
dos servicios puede ser un síntoma de acoplamiento estrecho y baja cohesión.

• Aísle los errores. Utilice estrategias de resistencia para impedir que los errores dentro de un
servicio caigan en cascada. Consulte Diseño de aplicaciones resistentes.

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

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


Microservicios que utilizan Azure Container
Service
Puede utilizar Azure Container Service para configurar y aprovisionar un clúster Docker. Azure
Container Services admite varios organizadores de contenedores populares, entre ellos, Kubernetes,
DC/OS y Docker Swarm.

Nodos públicos. Se puede acceder a estos nodos a través de un equilibrador de carga de acceso
público. El gateway de API se aloja en estos nodos.

Nodos de back-end. Estos nodos ejecutan servicios a los que los clientes pueden llegar mediante el
gateway de API. Estos nodos no reciben directamente el tráfico de Internet. Los nodos de back-end
pueden incluir más de un grupo de máquinas virtuales, cada uno con un perfil de hardware diferente.
Por ejemplo, se podrían crear grupos separados para cargas de trabajo de procesos generales, altas
cargas de trabajo de CPU y altas cargas de trabajo de memoria.

Máquinas virtuales de administración. Estas máquinas virtuales 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
colocan en subredes independientes dentro de la misma red virtual (VNet).

Equilibradores de carga. Un equilibrador de carga orientado externamente se sitúa frente a


los nodos públicos. Distribuye las solicitudes de internet a los nodos públicos. Se coloca otro
equilibrador de carga frente a las máquinas virtuales de administración, para permitir el tráfico de
shell seguro (ssh) hacia estas máquinas, utilizando reglas de NAT.

Para obtener confiabilidad y escalabilidad, cada servicio se replica en múltiples máquinas virtuales.
Sin embargo, debido a que los servicios son también relativamente ligeros (en comparación con una
aplicación monolítica), generalmente se empaquetan múltiples servicios en una sola máquina virtual.
Una mayor densidad permite la mejor utilización de los recursos. Si un determinado servicio no usa
muchos recursos, no necesita dedicar una VM completa para ejecutar dicho servicio.

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


El siguiente diagrama muestra tres nodos que ejecutan cuatro servicios diferentes (indicados por
diferentes formas). Observe que cada servicio tiene al menos dos instancias.

Microservicios que utilizan Azure Service Fabric


El siguiente diagrama muestra una arquitectura de microservicios que utiliza Azure Service Fabric.

El clúster de Service Fabric se implementa en uno o más conjuntos de escala de VM. Puede tener
más de un conjunto de escala de VM en el clúster, para así tener una combinación de tipos de VM.
Se coloca un gateway de API frente al clúster de Service Fabric, con un equilibrador de carga externo
para recibir las solicitudes de los clientes.

El tiempo de ejecución de Service Fabric realiza la administración del clúster, lo que incluye la
colocación del servicio, la conmutación por error de los nodos y la supervisión del estado. El tiempo
de ejecución se implementa en los nodos mismos del clúster. No existe un conjunto independiente
de máquinas virtuales para la administración del clúster.

Los servicios se comunican entre sí utilizando al proxy invertido que está integrado en Service Fabric.
Service Fabric proporciona un servicio de detección que puede resolver el punto de conexión de un
servicio nombrado.

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


1d

Estilo de
arquitectura CQRS
CQRS (segregación de responsabilidad de consultas y comandos) es un estilo de
arquitectura que separa las operaciones de lectura de las operaciones de escritura.

En las arquitecturas tradicionales, se utiliza el mismo modelo de datos para consultar y actualizar
una base de datos. Es un proceso simple y funciona bien para las operaciones básicas de CRUD. En
las aplicaciones más complejas, sin embargo, este enfoque puede ser difícil de manejar. Por ejemplo,
en el lado de lectura, la aplicación puede realizar muchas consultas diferentes y devolver objetos
de transferencia de datos (DTO) con diferentes formas. La asignación de objetos puede volverse
complicada. En el lado de escritura, el modelo puede implementar validación compleja y lógica
de negocio. Como resultado, puede terminar con un modelo excesivamente complejo que hace
demasiado.
Otro problema potencial es que las cargas de trabajo de lectura y escritura suelen ser asimétricas,
con requisitos muy diferentes de rendimiento y escala.
CQRS aborda estos problemas al separar las lecturas y escrituras en modelos diferentes, usando
comandos para actualizar los datos y consultas para leerlos.

• Los comandos deben estar basados en tareas, en lugar de estar centrados en los datos. Los
comandos ("Book hotel room", no "set ReservationStatus to Reserved".) pueden colocarse en una
cola de procesamiento asincrónico, en lugar de ser procesados de forma sincrónica.
• 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 que está optimizado para
consultas. Por ejemplo, se puede almacenar una vista materializada de los datos a fin de evitar uniones
complejas o asignaciones complejas de O/RM. Incluso se podría utilizar un tipo diferente de almacén de
datos. Por ejemplo, la base de datos de escritura podría ser relacional, mientras que la base de datos de
lectura es una base de datos de documentos.

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


Normalmente esto se logra al pedir al modelo de escritura que publique un evento cada vez que actualiza
la base de datos. La actualización de la base de datos y la publicación del evento deben ocurrir 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 en los datos. El estado actual se construye mediante la reproducción de los eventos. En un
contexto de CQRS, una de las ventajas del abastecimiento de eventos es que los mismos eventos pueden
utilizarse para notificar a otros componentes, en particular, para notificar al 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. Sin embargo, el abastecimiento de eventos agrega complejidad al diseño.

Cuándo utilizar esta arquitectura


Considere 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 nivel superior que se aplica a todo un sistema. Aplique CQRS solo a
aquellos subsistemas en los que se produzca un claro valor con la separación de lecturas y escrituras.
De lo contrario, estará creando mayor complejidad sin ningún beneficio.

Ventajas
● Escalado independiente. CQRS permite que las cargas de trabajo de lectura y escritura se
escalen de forma independiente, lo que puede dar como resultado menos contenciones de
bloqueo.
● Esquemas de datos optimizados. El lado de lectura puede utilizar un esquema optimizado para
consultas, mientras que el lado de escritura utiliza un esquema optimizado para actualizaciones.
● Seguridad. Es más fácil garantizar que solo las entidades de dominio adecuadas realicen las
escrituras en los datos.

21 CAPÍTULO 1d | Estilo de arquitectura CQRS


● Separación de inquietudes. La segregación de los lados de lectura y escritura puede dar como
resultado modelos que son más sostenibles y flexibles. La mayor parte de la lógica de negocio
compleja se destina al modelo de escritura. El modelo de lectura puede ser relativamente simple.
● Consultas simplificadas. Al almacenar una vista materializada en la base de datos de lectura, la
aplicación puede evitar uniones complejas cuando se realizan consultas.

Desafíos
● Complejidad. La idea básica de CQRS es simple. Pero puede llevar a un diseño más complejo de
la aplicación, especialmente si se incluye el patrón de abastecimiento de eventos.
● Envío de mensajes. Aunque CQRS no requiere la mensajería, es común utilizarla para procesar
comandos y publicar eventos de actualización. En ese caso, la aplicación debe manejar errores de
mensaje o mensajes duplicados.
● Coherencia final. 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, consulte
https://docs.microsoft.com/en-us/azure/architecture/patterns/cqrs.
• Para obtener información acerca de cómo utilizar el patrón de abastecimiento de eventos para
evitar conflictos de actualización, consulte https://docs.microsoft.com/en-us/azure/architecture/
patterns/event-sourcing.
• Para obtener información acerca de cómo utilizar el patrón de vista materializada para el modelo
de lectura a fin de optimizar el esquema de consultas, visite https://docs.microsoft.com/en-us/
azure/architecture/patterns/materialized-view.

CQRS en microservicios
CQRS puede resultar 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
basada en eventos
Una arquitectura basada en eventos está conformada por productores de
eventos que generan un flujo de eventos y por consumidores de eventos
que identifican los eventos.

Los eventos se entregan en tiempo casi real, por lo que los consumidores pueden responder
inmediatamente a los eventos a medida que se producen. Los productores están desacoplados de
los consumidores; un productor no sabe qué consumidores están escuchando. Los consumidores
también están desacoplados entre sí, y cada consumidor ve todos los eventos. Esto difiere de un
patrón de consumidores competitivos, donde los consumidores extraen mensajes de una cola y un
mensaje se procesa solo una vez (suponiendo que no hay errores). En algunos sistemas, tales como
IoT, los eventos deben ser ingeridos a volúmenes muy altos.

Una arquitectura basada en eventos puede utilizar un modelo de publicación/suscripción o un


modelo de flujo de eventos.

● Publicación/suscripción: la infraestructura de mensajería lleva un control de las suscripciones.


Cuando se publica un evento, envía el evento a cada suscriptor. Después de recibir un evento,
este no se puede reproducir y los nuevos suscriptores no lo ven.
● Transmisión 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 la
transmisión; por el contrario, un cliente puede leer desde cualquier parte de la transmisión.
El cliente es responsable de hacer avanzar su posición en la transmisión. Esto significa que un
cliente puede unirse en cualquier momento y reproducir eventos.

24 CAPÍTULO 1e | Estilo de arquitectura basada en 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, se podría utilizar Azure Functions con un desencadenador de Service
Bus, para que una función se ejecute cada vez que se publica un mensaje en un tema de Service
Bus.
● Procesamiento complejo de eventos. Un consumidor procesa una serie de eventos, busca
patrones en los datos de los eventos y utiliza una tecnología como Azure Stream Analytics o
Apache Storm. Por ejemplo, podría agregar lecturas desde un dispositivo incrustado sobre una
ventana de tiempo y generar una notificación si la media móvil cruza cierto umbral.
● Procesamiento de flujo de eventos. Utilice una plataforma de transmisión de datos, por
ejemplo Azure IoT Hub o Apache Kafka, como canalización para ingerir eventos e introducirlos
en los procesadores de flujo. Los procesadores de flujo actúan para procesar o transformar la
transmisión. Puede haber múltiples procesadores de flujo para diferentes subsistemas de la
aplicación. Este enfoque es una buena opción para las cargas de trabajo de IoT.
La fuente de los eventos puede ser externa al sistema, como los dispositivos físicos de una solución
de IoT. En ese caso, el sistema debe ser capaz de ingerir los datos en el volumen y rendimiento que
requiere la fuente de datos.
En el diagrama lógico anterior, cada tipo de consumidor se muestra como un solo cuadro. En la
práctica, es común tener varias instancias de un consumidor para evitar que este se convierta en un
punto único de falla en el sistema. También pueden ser necesarias varias instancias para manejar
el volumen y la frecuencia de los eventos. Además, un solo consumidor podría procesar eventos
en varios subprocesos. Esto puede crear desafíos si los eventos deben procesarse en orden o si
requieren la semántica de "exactamente una vez". Consulte Minimizar la coordinación.

Cuándo utilizar esta arquitectura


• Múltiples subsistemas deben procesar los mismos eventos.
• Procesamiento en tiempo real con mínimo desfase temporal.
• Procesamiento complejo de eventos, como la coincidencia de patrones o la agregación sobre
ventanas de tiempo.
• Alto volumen y alta velocidad de datos, como IoT.

Ventajas
• Los productores y los consumidores están desacoplados.
• Sin integraciones de punto a punto. Es fácil añadir nuevos consumidores al 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.

Desafíos
• Entrega garantizada. En algunos sistemas, especialmente en escenarios de IoT, es crucial
garantizar que los eventos se entregan.
• Procesamiento de eventos en orden o exactamente una vez. Cada tipo de consumidor
normalmente se ejecuta en múltiples instancias, para obtener resistencia y escalabilidad.
Esto puede crear un desafío 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 basada en eventos


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

El gateway de nube ingiere eventos de dispositivo en el límite de la nube, mediante un sistema de


mensajería confiable y de baja latencia.
Los dispositivos pueden enviar eventos directamente al gateway de nube o a través de un gateway de
campo. Un gateway de campo es un dispositivo o software especializado, colocado generalmente
con los dispositivos, que recibe los eventos y los reenvía al gateway de nube. El gateway de campo
también puede preprocesar los eventos de dispositivos sin formato y realizar funciones tales como
filtrado, agregación o transformación de protocolo.
Después de la ingesta, los eventos pasan por uno o más procesadores de flujo que pueden redirigir
los datos (por ejemplo, al almacenamiento) o realizar análisis y otros procesos.
Los siguientes son algunos tipos comunes de procesamiento. (Esta lista no es exhaustiva).

• Escritura de los datos de eventos en almacenamiento inactivo, para archivado o análisis por lotes.
• Análisis de rutas activas, que analizan el flujo de eventos en tiempo (casi) real, para detectar
anomalías, reconocer patrones sobre ventanas de tiempo graduales o activar alertas cuando se
produce una situación específica en el flujo.
• Manejo de tipos especiales de mensajes de no telemetría que provienen de los dispositivos, como
notificaciones y alarmas. Aprendizaje automático.

Los cuadros que están 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 aquí para brindar
integridad.

• El registro de dispositivos es una base de datos de los dispositivos aprovisionados, que incluye los
ID de los dispositivos y generalmente sus metadatos, como la ubicación.
• La API de aprovisionamiento es una interfaz externa común para aprovisionar y registrar nuevos
dispositivos.
• Algunas soluciones de IoT permiten el envío de mensajes de comando y control a los dispositivos.
Esta sección ha presentado una visión de IoT de muy alto nivel, y hay muchas sutilezas y desafíos a
tener en cuenta. Para obtener más información y una arquitectura de referencia detallada, consulte
https://azure.microsoft.com/en-us/updates/microsoft-azure-iot-reference-architecture-available/
(PDF download).

26 CAPÍTULO 1e | Estilo de arquitectura basada en eventos


1f

Estilo de arquitectura
de Big Data
Una arquitectura de Big Data está diseñada para manejar la ingesta, el
procesamiento y el análisis de los datos que son demasiado grandes o
complejos para los sistemas de bases 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 fuentes de Big Data en reposo.
• Procesamiento en tiempo real de Big Data en movimiento.
• Exploración interactiva de Big Data.
• Análisis predictivo y aprendizaje automático.

La mayoría de las arquitecturas de Big Data incluyen algunos de los siguientes componentes o todos
ellos:
● Fuentes de datos: todas las soluciones de Big Data comienzan con una o más fuentes de datos.
Algunos ejemplos son:
• Almacenes de datos de aplicaciones, como las bases de datos relacionales.
• Archivos estáticos producidos por las aplicaciones, como los archivos de registro de
servidor web.
• Fuentes de datos en tiempo real, como los 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 altos
volúmenes de archivos de gran tamaño en varios formatos. Este tipo de almacén a menudo se
llama lago de datos. Las opciones para la implementación de este almacenamiento incluyen
Azure Data Lake Store o contenedores de blob en Azure Storage.

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


● Procesamiento por lotes. Debido a que los conjuntos de datos son tan grandes, a menudo una
solución de Big Data debe procesar los archivos de datos mediante trabajos por lotes de larga
duración para filtrar, agregar y preparar los datos para su análisis. Generalmente estos trabajos
implican leer los archivos de origen, procesarlos y escribir el resultado en nuevos archivos. Las
opciones incluyen la ejecución de trabajos de U-SQL en Azure Data Lake Analytics, mediante
trabajos con Hive, Pig o Map/Reduce personalizado en un clúster de HDInsight Hadoop, o
mediante los programas Java, Scala o Python en un clúster de HDInsight Spark.
● Ingesta de mensajes en tiempo real. Si la solución incluye fuentes en tiempo real, la
arquitectura debe incluir una manera de capturar y almacenar los mensajes en tiempo real para el
procesamiento de transmisiones. Esto podría ser un almacén de datos simple, donde los mensajes
entrantes se colocan en una carpeta para su procesamiento. Sin embargo, muchas soluciones
necesitan un almacén de ingesta de mensajes que actúe como búfer para los mensajes y que
admita el procesamiento de escalamiento horizontal, la entrega confiable y otra semántica de cola
de mensajes. Las opciones incluyen Azure Event Hubs, Azure IoT Hubs y Kafka.
● Procesamiento de transmisiones. Después de capturar mensajes en tiempo real, la solución
debe procesarlos mediante el filtrado, la agregación y la preparación de los datos para su análisis.
Los datos de transmisión procesados después se escriben en un receptor de salida. Azure Stream
Analytics proporciona un servicio administrado de procesamiento de transmisiones que se basa
en la ejecución perpetua de las consultas de SQL que operan en transmisiones sin límites. También
puede utilizar tecnologías de transmisión Apache de código abierto como Storm y Spark Streaming
en un clúster de HDInsight.
● Almacén de datos analíticos. Muchas soluciones de Big Data preparan los datos para su
análisis y luego los entregan procesados en un formato estructurado que se puede consultar con
herramientas analíticas. El almacén de datos analíticos utilizado para atender estas consultas puede
ser un almacén de datos relacional estilo Kimball, como se ve en las soluciones de inteligencia
empresarial (BI) más tradicionales. De manera alternativa, podrían presentarse los datos mediante
una tecnología NoSQL de baja latencia como HBase, o mediante una base de datos Hive
interactiva que proporcione una abstracción de los metadatos sobre los archivos de datos en el
almacén de datos distribuidos. Azure SQL Data Warehouse proporciona un servicio administrado
para el almacenamiento de datos a gran escala basados en la nube. HDInsight admite Interactive
Hive, HBase y Spark SQL, que también pueden utilizarse para presentar los datos para su análisis.
● Análisis y generación de informes. El objetivo de la mayoría de las soluciones de Big Data
consiste en proporcionar información sobre los datos a través del análisis y la generación de
informes. Para permitir que los usuarios analicen los datos, la arquitectura puede incluir una capa
de modelado de datos, como un cubo OLAP multidimensional o un modelo de datos tabular en
Azure Analysis Services. También podría admitir la inteligencia empresarial de autoservicio, con las
tecnologías de modelado y visualización de Microsoft Power BI o Microsoft.
● Excel. El análisis y la generación de informes también pueden tomar la forma de exploración
interactiva de los datos por parte de científicos o analistas de datos. Para estos escenarios, muchos
servicios de Azure admiten cuadernos analíticos, tales como Jupyter, que permiten que estos
usuarios aprovechen sus habilidades existentes con Python o R. Para la exploración de datos a gran
escala, puede utilizar Microsoft R Server, ya sea de forma independiente o con Spark.
● Organización. La mayoría de las 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 fuentes y depósitos, cargan los datos procesados en
un almacén de datos analíticos o empujan los resultados directamente a un informe o panel. Para
automatizar estos flujos de trabajo, puede utilizar una tecnología de organización como Azure
Data Factory o Apache Oozie y Sqoop.

Azure incluye muchos servicios que pueden utilizarse en una arquitectura de Big Data. Se encuentran
prácticamente en dos categorías:
• Servicios administrados, como 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, como 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. Se pueden mezclar y combinar servicios administrados de Azure y
tecnologías Apache en clústeres de HDInsight, para capitalizar las habilidades existentes o las
inversiones en tecnología.
● Rendimiento a través del paralelismo. Las soluciones de Big Data aprovechan el paralelismo, lo
que permite que las soluciones de alto rendimiento escalen a grandes volúmenes de datos.
● Escala elástica. Todos los componentes de la arquitectura de Big Data admiten el
aprovisionamiento de escalamiento horizontal, para que pueda ajustar su solución a cargas de
trabajo pequeñas o grandes y pagar solo por los recursos que utiliza.
● Interoperabilidad con soluciones existentes. Los componentes de la arquitectura de Big Data
también se utilizan para las soluciones de procesamiento de IoT y de inteligencia empresarial, lo
que le permite crear una solución integrada en todas las cargas de trabajo de datos.

Desafíos
● Complejidad. Las soluciones de Big Data pueden ser extremadamente complejas, con
numerosos componentes para controlar la ingesta de datos que provienen de múltiples fuentes
de datos. Puede ser difícil crear, probar y solucionar problemas de los procesos de Big Data.
Además, puede haber un gran número de opciones de configuración en múltiples sistemas que
deberán utilizarse para poder optimizar el rendimiento.
● Conjunto de habilidades. Muchas tecnologías de Big Data son altamente especializadas
y utilizan marcos y lenguajes que no son típicos de las arquitecturas de aplicaciones más
generales. Por otra parte, las tecnologías de Big Data hacen evolucionar nuevas API que se basan
en lenguajes más establecidos. Por ejemplo, el lenguaje U-SQL en Azure Data Lake Analytics
se basa en una combinación de Transact-SQL y C#. Asimismo, las API basadas en SQL están
disponibles para Hive, HBase y Spark.
● Madurez de la tecnología. Muchas de las tecnologías utilizadas en Big Data están
evolucionando. Si bien las principales tecnologías de Hadoop como Hive y Pig se han
estabilizado, las tecnologías emergentes como Spark introducen grandes cambios y mejoras
con cada nueva versión. 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 el almacenamiento de todos
los datos estáticos en un lago de datos centralizado. Garantizar el acceso a estos datos puede
ser difícil, especialmente cuando los datos deben ser ingeridos y consumidos por múltiples
plataformas y aplicaciones.

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


Procedimientos recomendados
● Aproveche el paralelismo. La mayoría de las tecnologías de procesamiento de Big Data
distribuyen la carga de trabajo entre múltiples unidades de procesamiento. Esto requiere que los
archivos de datos estáticos se creen y almacenen en un formato divisible. Los sistemas de archivos
distribuidos como HDFS pueden optimizar el rendimiento de lectura y escritura, y el procesamiento
real es realizado por varios nodos del clúster en paralelo, lo que reduce los tiempos de trabajo en
general.
● Cree una partición de datos. El procesamiento por lotes generalmente se produce según una
programación recurrente; por ejemplo, semanal o mensualmente. Particione los archivos de datos
y las estructuras de datos como las tablas, basándose en períodos temporales que coincidan
con la programación del procesamiento. Esto simplifica la ingesta de datos y la programación de
trabajos y, además, facilita la solución de errores. Asimismo, la partición de tablas que se utilizan en
consultas de Hive, U-SQL o SQL pueden mejorar significativamente el rendimiento de las consultas.
● Aplique la semántica de esquema en lectura. Un lago de datos le permite combinar el
almacenamiento de archivos en múltiples formatos, sean estructurados, semiestructurados o no
estructurados. Utilice la semántica de esquema en lectura, que proyecta un esquema sobre los
datos cuando estos se están procesando, no cuando se almacenan. Esto incorpora flexibilidad a la
solución y evita los cuellos de botella durante la ingesta de datos causados por su validación y la
comprobación de tipos.
● Procese los datos en el lugar. Las soluciones tradicionales de BI suelen utilizar un proceso de
extracción, transformación y carga (ETL) para migrar los datos a un almacén de datos. Con datos
de mayor volumen y una mayor variedad de formatos, las soluciones de Big Data generalmente
utilizan variaciones de ETL, como transformación, extracción y carga (TEL). Con este enfoque,
los datos se procesan dentro del almacén de datos distribuidos, transformándolo a la estructura
requerida, antes de migrar los datos transformados a un almacén de datos analíticos.
● Equilibre la utilización y los costos de tiempo. En el caso de los trabajos de procesamiento por
lotes, es importante considerar dos factores: el costo por unidad de los nodos de cálculo y el costo
por minuto de la utilización 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 suceder que el
trabajo utilice los cuatro nodos solo durante las dos primeras horas y que después de eso, solo
se requieran dos nodos. En ese caso, la ejecución de todo el trabajo en dos nodos aumentaría el
tiempo total de trabajo, pero no lo duplicaría, por lo que el costo total sería menor. En algunos
escenarios de negocio, un tiempo de procesamiento más prolongado puede ser preferible ante el
mayor costo que suponen los recursos de los clústeres que se utilizan poco.
● Separe los recursos de los clústeres. Al implementar los clústeres de HDInsight, normalmente
obtendrá un mejor rendimiento si aprovisiona los recursos de los clústeres por separado para
cada tipo de carga de trabajo. Por ejemplo, aunque los clústeres de Spark incluyan a Hive, si
necesita realizar un procesamiento extenso con Hive y Spark, debería considerar la posibilidad
de implementar clústeres de Spark y Hadoop separados y especiales. Del mismo modo, si
utiliza HBase y Storm para el procesamiento de transmisiones de baja latencia y Hive para el
procesamiento por lotes, considere clústeres separados para Storm, HBase y Hadoop.
● Organice la ingesta de datos. En algunos casos, las aplicaciones de negocios existentes pueden
escribir archivos de datos para procesamiento por lotes directamente en los contenedores de
blobs de almacenamiento de Azure, donde pueden ser consumidos por HDInsight o Azure Data
Lake Analytics. Sin embargo, a menudo tendrá que organizar la ingesta de datos locales o de
fuentes de datos externas en el lago de datos. Utilice un flujo de trabajo o una canalización de
organización, como los que admiten Azure Data Factory o Oozie, para lograrlo de una manera
predecible y con administración centralizada.
● Limpie los datos confidenciales en forma temprana. El flujo de trabajo de la ingesta de datos
debe limpiar los datos confidenciales en una etapa temprana del proceso, de modo de evitar que
estos se almacenen 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 las cargas de trabajo a gran escala que
requieren una gran cantidad de núcleos, a menudo cientos o miles. Los
escenarios incluyen la presentación de imágenes, la dinámica de fluidos,
el modelado de riesgo financiero, la exploración petrolera, el diseño de
medicamentos y el análisis de estrés de ingeniería, entre otros aspectos.

Estas son algunas de las características típicas de las aplicaciones de big compute:

• El trabajo se puede dividir en tareas discretas, que se pueden ejecutar en muchos núcleos
simultáneamente.
• Cada tarea es finita. Toma algunas entradas, realiza algo de procesamiento y produce resultados.
Toda la aplicación se ejecuta por un tiempo finito (minutos a días). Un patrón común es
aprovisionar una gran cantidad de núcleos en una irrupción y luego volver a cero una vez que
finaliza la aplicación.
• No es necesario que la aplicación se mantenga activa las 24 horas del día, los 7 días de la
semana. Sin embargo, el sistema debe manejar errores de nodos o fallos de la aplicación.
• Para algunas aplicaciones, las tareas son independientes y se pueden ejecutar en paralelo.
En otros casos, las tareas se acoplan estrechamente, lo que significa que deben interactuar o
intercambiar resultados intermedios. En ese caso, considere el uso de tecnologías de red de alta
velocidad, como InfiniBand y el acceso directo a memoria remota (RDMA).
• Dependiendo de la carga de trabajo, podría usar VM de tamaños que consumen muchos
recursos informáticos (H16r, H16mr y A9).

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


Cuándo utilizar esta arquitectura
• Operaciones intensivas en términos de procesos, como simulación y procesamiento de números.

• Simulaciones que son intensivas en términos de procesos y se deben dividir entre CPU de
múltiples equipos (10 a miles).

• Simulaciones que requieren demasiada memoria para un equipo, por lo que deben dividirse en
múltiples equipos.

• Procesos de larga duración que tardarían demasiado tiempo en completarse en un solo equipo.

• Procesos más pequeños que deben ejecutarse cientos o miles de veces, como las simulaciones
de Monte Carlo.

Ventajas
• Alto rendimiento con procesamiento "vergonzosamente paralelo".
• Puede aprovechar cientos o miles de núcleos de equipos para solucionar problemas grandes
más rápidamente.

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

• Puede aprovisionar VM según las necesite para hacer un trabajo y luego desmantelarlas.

Desafíos
• Administración de la infraestructura de VM.

• Administración del volumen de procesamiento de números.

• Aprovisionamiento de miles de núcleos de manera oportuna.

• Para las tareas que se acoplan estrechamente, agregar más núcleos puede significar menores
retornos. Puede que 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 la ejecución de aplicaciones de informática de alto
rendimiento (HPC) a gran escala.

Con Azure Batch, configure un grupo de VM y cargue las aplicaciones y los archivos de datos.
Entonces, el servicio de Batch aprovisiona las VM, asigna tareas a las VM, ejecuta la tareas y supervisa
el progreso. Batch puede escalar automáticamente las VM en respuesta a la carga de trabajo. Batch
también proporciona programación de trabajos.

Ejecución de Big Compute en máquinas virtuales


Puede usar Microsoft HPC Pack para administrar un clúster de VM, y para programar y administrar
trabajos de HPC. Con este enfoque, usted debe aprovisionar y administrar las VM y la infraestructura
de red. Considere este enfoque si tiene cargas de trabajo de HPC existentes y desea mover una parte
de estas o todas ellas a Azure. Puede mover todo el clúster de HPC a Azure, o bien mantener su
clúster de HPC en las instalaciones, pero use Azure para la capacidad de irrupción. Para obtener más
información, consulte soluciones de Batch y HPC para cargas de trabajo de informática a gran escala.

HPC Pack implementado en Azure


En este escenario, el clúster de HPC se crea completamente en Azure.
El nodo principal proporciona servicios de administración y programación de trabajos al clúster.
Para las tareas que se acoplan estrechamente, use una red de RDMA que proporcione un ancho de
banda muy alto y comunicación de baja latencia entre VM. Para obtener más información, consulte
Implementar un clúster de HPC Pack 2016 en Azure.

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


Irrupción de un clúster de HPC en Azure
En este escenario, una organización ejecuta HPC Pack localmente y usa VM de Azure para la
capacidad de irrupción. El nodo principal del clúster se encuentra en el entorno local. ExpressRoute o
VPN Gateway conecta la red local a Azure VNet.

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


2

Elección de tecnologías
para procesos y
almacenes de datos
Elija las tecnologías adecuadas para las aplicaciones de Azure.
Al diseñar una solución para Azure, hay dos decisiones de tecnología que debe tomar en una etapa
temprana del proceso de diseño, porque afectan a toda la arquitectura. Estas son las decisiones de
tecnologías para procesos y almacenes de datos.

La opción de proceso es el modelo de hosting que elegirá para los recursos informáticos que
ejecuta la aplicación. En términos generales, la elección se realiza entre Infraestructura como servicio
(IaaS), Plataforma como servicio (PaaS) o Funciones como servicio (FaaS), y el espectro entre estos.
Actualmente hay siete opciones principales de proceso disponibles para usted en Azure. Para
tomar su decisión, tenga en cuenta las características y limitaciones del servicio, la disponibilidad y
escalabilidad, el costo y las consideraciones de DevOps. Las tablas de comparación en esta sección le
ayudarán a reducir sus opciones.
El almacén de datos incluye todo tipo de datos que su aplicación necesita para administrar, ingerir
o generar, o que los usuarios crean. Los tipos más comunes son datos profesionales, caché, datos
de IoT, telemetría y datos de registro no estructurados, y las aplicaciones a menudo contienen más
de un tipo de datos. Los diferentes tipos de datos tienen diferentes requisitos de procesamiento,
por lo que debe elegir el almacén adecuado para cada tipo para así obtener los mejores resultados.
Algunas tecnologías para almacenes de datos admiten múltiples modelos de almacenamiento. Use
la información de esta sección para elegir primero qué modelo de almacenamiento es más adecuado
para sus necesidades. Luego, considere un almacén de datos específico dentro de esa categoría, en
función de factores como el conjunto de características, el costo y la facilidad de administración.

Esta sección de la Guía de arquitectura de la aplicación contiene los siguientes temas:

• Información general sobre las opciones de proceso presenta algunas consideraciones generales
para elegir un servicio de proceso en Azure.
• Criterios para elegir una opción de proceso comparan los servicios específicos de proceso de
Azure en varios ejes, incluido el modelo de hosting, DevOps, la disponibilidad y la escalabilidad.

35 CAPÍTULO 2 | Elección de tecnologías para procesos y almacenes de datos


• Elegir el almacén de datos adecuado describe las principales categorías de tecnologías para
almacenes de datos, incluidos RDBMS, almacenamiento de clave-valor, base de datos de
documentos, base de datos de gráficos y otros.
• Criterios de comparación para elegir un almacén de datos describe algunos de los factores a
considerar al elegir un almacén de datos.

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

36
2a

Información general
sobre las opciones
de proceso
El término proceso hace referencia al modelo de hosting para los recursos
informáticos que ejecuta su aplicación.
En un extremo del espectro se encuentra Infraestructura como servicio (IaaS). Con IaaS, usted
aprovisiona las VM que necesita, junto con los componentes de red y almacenamiento asociados.
Luego, implemente el software y las aplicaciones que desee en dichas VM. Este modelo es lo
más cercano a un entorno local tradicional, con la excepción de que Microsoft administra la
infraestructura. Usted aún administra las VM individuales.
Plataforma como servicio (PaaS) ofrece un entorno de hospedaje administrado, donde puede
implementar su aplicación sin necesidad de administrar VM o recursos de red. Por ejemplo, en lugar de
crear VM individuales, usted especifica un recuento de instancias y el servicio aprovisionará, configurará
y administrará los recursos necesarios. Azure App Service es un ejemplo de servicio de PaaS.
Existe un espectro entre IaaS y PaaS puro. Por ejemplo, las VM de Azure pueden escalar
automáticamente mediante el uso de conjuntos de escala de VM. Esta capacidad de escalado
automático no es estrictamente PaaS, pero es el tipo de característica de administración que se
puede encontrar en un servicio de PaaS.
Funciones como servicio (FaaS) incluso va más allá al eliminar la necesidad de preocuparse por
el entorno de hospedaje. En lugar de crear instancias de proceso e implementar código en esas
instancias, usted simplemente implementa su código y el servicio lo ejecuta automáticamente. No
necesita administrar los recursos informáticos. Estos servicios hacen uso de una arquitectura sin
servidor, y se escalan o reducen hasta el nivel necesario para manejar el tráfico. Azure Functions son
un servicio de FaaS.
IaaS entrega el mayor control, flexibilidad y portabilidad. FaaS proporciona simplicidad, escala
elástica y potenciales ahorros de costos, ya que solo paga por el tiempo en que el código se ejecuta.
PaaS se encuentra en algún lugar entre los dos. En general, mientras más flexibilidad ofrezca
un servicio, más responsable será de configurar y administrar los recursos. Los servicios de FaaS
administran automáticamente casi todos los aspectos de la ejecución de una aplicación, mientras que
las soluciones de IaaS requieren que aprovisione, configure y administre las VM y los componentes
de red que crea.

Estas son las principales opciones de proceso disponibles actualmente en Azure:


• Las máquinas virtuales son un servicio de IaaS, lo que le permite implementar y administrar VM
dentro de una red virtual (VNet).

37 CAPÍTULO 1g | Estilo de arquitectura de big compute


• App Service es un servicio administrado para hospedar aplicaciones web, back-ends de
aplicaciones móviles, API de RESTful o procesos de negocio automatizados.
• Service Fabric es una plataforma de sistemas distribuidos que se puede ejecutar en muchos
entornos, incluidos Azure o entornos locales. Service Fabric es un organizador de microservicios
en un clúster de máquinas.
• Azure Container Service le permite crear, configurar y administrar un clúster de VM que están
preconfiguradas para ejecutar aplicaciones en contenedores.
• Azure Functions es un servicio administrado de FaaS.
• Azure Batch es un servicio administrado para la ejecución de aplicaciones de informática de alto
rendimiento (HPC) a gran escala y en paralelo.
• Cloud Services es un servicio administrado para ejecutar aplicaciones en la nube. Utiliza un
modelo de hospedaje de PaaS.

Al seleccionar una opción de proceso, estos son algunos factores a considerar:

• Modelo de hospedaje. ¿Cómo se hospeda el servicio? ¿Qué requisitos y limitaciones impone este
entorno de hospedaje?
• DevOps. ¿Hay soporte integrado para las actualizaciones de la aplicación? ¿Cuál es el modelo de
implementación?
• Escalabilidad. ¿Cómo maneja el servicio la incorporació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?
• Costo. Además del costo del servicio en sí, considere el costo de las operaciones para administrar
una solución basada en ese servicio. Por ejemplo, las soluciones de IaaS podrían tener un costo
de operaciones mayor.
• ¿Cuáles son las limitaciones generales de cada servicio?
• ¿Qué tipos de arquitecturas de aplicaciones son apropiadas para este servicio?

38
2b

Comparación de
procesos
El término procesos hace referencia al modelo de hospedaje para los
recursos informáticos en que se ejecutan sus aplicaciones. Las siguientes
tablas comparan los servicios de procesos de Azure en diversos ejes.
Consulte estas tablas al seleccionar una opción de proceso para su
aplicación.

Modelo de hospedaje
Azure
Máquinas Azure Cloud
Criterios App Service Service Fabric Container Azure Batch
virtuales Functions Services
Services
Servicios,
Composición
archivos Trabajos
de la Independiente Aplicaciones Funciones Contenedores Roles
ejecutables por programados
aplicación
invitados
Múltiples
aplicaciones No hay Múltiples Una instancia Múltiples
Múltiples
Densidad Independiente por instancia a instancias contenedores de rol por contenedores
servicios por VM
través de planes dedicadas por VM VM por VM
de la aplicación
Número 2 3 No hay nodos
1
4
mínimo de 1 1 5 3 2 1
nodos dedicados

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


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

Autohospedaje,
Hospedaje Integrado
Independiente Integrado IIS en N/D Independiente No
web (IIS)
contenedores

Windows, Windows, Linux Windows, Linux Windows, Windows,


SO N/D Windows
Linux (vista previa) (vista previa) Linux Linux

¿Se puede
implementar
Compatible Compatible Compatible No compatible Compatible Compatible6 Compatible
a una VNet
dedicada?

Conectividad
Compatible Compatible Compatible No compatible Compatible Compatible8 Compatible
híbrida

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


Notas:
1. Si utiliza el plan de App Service, las funciones se ejecutan en las VM asignadas para usted
en el 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 mayor con dos o más instancias.
3. Para entornos de producción.
4. Se puede reducir a cero una vez finalizado el trabajo.
5. Requiere un entorno de App Service (ASE).
6. Solo VNet clásica.
7. Requiere ASE o conexiones híbridas de BizTalk.
8. VNet clásica o VNet de administrador de recursos a través del emparejamiento de VNet.

DevOps
Azure
Máquinas Azure Cloud Azure
Criterios App Service Service Fabric Container
virtuales Functions Services Batch
Services

Tiempo de
Depuración Clúster de nodo CLI de Azure ejecución de Emulador No
Independiente IIS Express, otros
local local Functions contenedor local compatible
local
Archivo
Aplicación web, ejecutable por
Aplicación
Modelo de trabajos web invitado, modelo Funciones con Rol web, rol
Independiente Independiente de línea de
programación para tareas en de servicio, desencadenantes del trabajador
comandos
segundo plano modelo de actor,
contenedores

Administrador 2
Compatible Compatible Compatible Compatible Compatible Limitado Compatible
de recursos

Cambio
Actualización Actualización
Sin soporte Ranuras de Sin soporte Depende del de VIP o
de la gradual (por N/D
integrado implementación integrado organizador. actualización
aplicación servicio)
gradual

Notas:

1. Las opciones incluyen IIS Express para ASP.NET o node.js (iisnode); servidor web PHP;
Azure Toolkit para IntelliJ, Azure Toolkit para Eclipse. App Service también es compatible
con 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
Máquinas App Service Azure Cloud
Criterios Container Azure Batch
virtuales Service Fabric Functions Services
Services

Conjuntos
Escalado Conjuntos de Servicio Servicio No Servicio
de escala de N/D
automático escala de VM integrado integrado compatible integrado
VM

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

Imagen de
plataforma: 20 Sin límite Límite predeterminado
1000 nodos por instancias, definido, de 20 núcleos. Póngase
Límite de 100 nodos 1
VMSS, imagen 50 con Infinito 100 máximo en contacto con
escalado por VMSS
personalizada: entorno de recomendado atención al cliente para
100 nodos por App Service de 200 aumentarlo.
VMSS

Notas:

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


azure-functions/functions-scale.

Disponibilidad
Azure
Máquinas Azure Cloud
Criterios App Service Service Fabric Container Azure Batch
virtuales Functions Services
Services
SLA para
SLA para
SLA para App SLA para SLA para Azure SLA para SLA para
SLA máquinas
Service Service Fabric Functions Container Cloud Services Azure Batch
virtuales
Service
Administrador
Conmutación
Administrador Administrador de tráfico, No Administrador Administrador No
por error en
de tráfico de tráfico clúster de varias compatible de tráfico de tráfico compatible
varias regiones
regiones

Notas:

1. Para obtener información acerca de SLA específicos, vaya a https://azure.microsoft.com/


en-us/support/legal/sla/.

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


Seguridad
Azure
Máquinas Service Azure Cloud Azure
Criterios App Service Container
virtuales Fabric Functions Services Batch
Services

Configurado Configurado
SSL Compatible Compatible Compatible Compatible Compatible
en VM en VM

No
RBAC Compatible Compatible Compatible Compatible Compatible Compatible
compatible

Otro
Azure
Máquinas Service Cloud Azure
Criterios App Service Azure Functions Container
virtuales Fabric Services Batch
Services

Precios de
Precios de Precios de
Windows, Precios de servicios Precios de
Costo Service funciones de Compatible
Linux App Service de contenedor Cloud Services
Fabric Azure
de Azure

Adecuado
Microservicios, Web-cola Big
arquitectura Compatible Compatible Compatible Compatible
EDA trabajador compute
estilos

Notas:

1. Para obtener información acerca de costos específicos, vaya a https://azure.microsoft.com/


pricing/details/.

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


2c

Información general
sobre los almacenes
de datos
Elija el almacén de datos adecuado.
Los sistemas de negocio modernos administran volúmenes de datos cada vez más grandes. Los
datos pueden ser ingeridos desde servicios externos, generados por el sistema o creados por
usuarios. Estos conjuntos de datos pueden tener características y requisitos de procesamiento
extremadamente variados. Las empresas usan datos para evaluar tendencias, activar procesos de
negocio, auditar sus operaciones, analizar el comportamiento del cliente y muchas otras cosas.
Esta heterogeneidad significa que un único almacén de datos generalmente no constituye el
mejor enfoque. En cambio, a menudo es mejor almacenar diferentes tipos de datos en diferentes
almacenes de datos, cada uno enfocado hacia una carga de trabajo o un patrón de uso específico.
El término persistencia políglota se usa para describir soluciones que usan una combinación de
tecnologías para almacenes de datos.
Seleccionar el almacén de datos adecuado para sus requisitos es una decisión de diseño clave.
Literalmente tiene cientos de implementaciones entre las cuales elegir, en bases de datos de SQL
y NoSQL. Los almacenes de datos a menudo se clasifican según la forma en que estructuran los
datos y los tipos de operaciones que admiten. Este artículo describe varios de los modelos de
almacenamiento más comunes. Tenga en cuenta que una tecnología para almacenes de datos
específica puede admitir múltiples modelos de almacenamiento. Por ejemplo, los sistemas de
administración de bases de datos relacionales (RDBMS) también pueden admitir almacenamiento
de claves/valores o gráficos. De hecho, existe una tendencia general por la llamada compatibilidad
multimodelo, donde un sistema de base de datos único admite varios modelos. Sin embargo, aún es
útil comprender los diferentes modelos en un nivel alto.
No todos los almacenes de datos en 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 datos. A veces, esta funcionalidad está integrada en el motor de
almacenamiento de datos. En otros casos, las capacidades de almacenamiento y procesamiento de
datos están separadas, y puede haber varias opciones de procesamiento y análisis. Los almacenes de
datos también admiten diferentes interfaces programáticas y de administración.
Generalmente, debe comenzar por considerar qué modelo de almacenamiento es el más adecuado
para sus requisitos. Luego, considere un almacén de datos específico dentro de esa categoría, en
función de factores como el conjunto de características, el costo y la facilidad de administración.

43 CAPÍTULO 2c | Información general sobre los almacenes 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 una base matemática, y la mayoría de los proveedores
proporcionan un dialecto del lenguaje de consulta estructurado (SQL) para recuperar y administrar
datos. Un RDBMS normalmente implementa un mecanismo transaccionalmente coherente que se
ajusta al modelo ACID (atómico, coherente, aislado, durable) para actualizar la información.
Un RDBMS normalmente admite un modelo de esquema en escritura, donde la estructura de datos
se define con anticipación, y todas las operaciones de lectura o escritura deben usar el esquema.
Esto contrasta con la mayoría de los almacenes de datos NoSQL, particularmente los de tipo claves/
valores, donde el modelo de esquema en lectura presupone que el cliente impondrá su propio
esquema interpretativo sobre los datos que provienen de la base de datos, y es independiente del
formato de los datos en escritura.
Un RDBMS es muy útil cuando es importante contar con garantías de coherencia sólidas, donde
todos los cambios son atómicos y las transacciones siempre dejan los datos en un estado coherente.
Sin embargo, las estructuras subyacentes no se prestan para escalado mediante distribución del
almacenamiento y procesamiento entre máquinas. Además, la información almacenada en un
RDBMS debe colocarse en una estructura relacional siguiendo el proceso de normalización. Si bien
este proceso se entiende bien, puede conducir a ineficiencias debido a la necesidad de descompilar
entidades lógicas en filas de tablas separadas, para luego volver a ensamblar los datos cuando se
ejecutan consultas.
Servicio de Azure pertinente:
• 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 claves/valores
Un almacén de claves/valores es esencialmente una gran tabla hash. Usted asocia cada valor
de datos con una clave única, y el almacén de claves/valores usa esta clave para almacenar los
datos mediante el uso de una función de hash apropiada. La función de hash se selecciona para
proporcionar una distribución uniforme de claves de hash a través del almacenamiento de datos.
La mayoría de los almacenes de claves/valores solo admiten operaciones simples de consulta,
inserción y eliminación. Para modificar un valor (parcial o completamente), una aplicación debe
sobrescribir los datos existentes para todo el valor. En la mayoría de las implementaciones, leer o
escribir un solo valor es una operación atómica. Si el valor es grande, la escritura puede tardar un
tiempo.
Una aplicación puede almacenar datos arbitrarios como un conjunto de valores, aunque algunos
almacenes de claves/valores imponen límites al tamaño máximo de los valores. Los valores
almacenados son opacos para el software del sistema de almacenamiento. La aplicación debe
proporcionar e interpretar cualquier información del esquema. Básicamente, los valores son blobs y
el almacén de claves/valores simplemente recupera o almacena el valor por clave.

44 CAPÍTULO 2c | Información general sobre los almacenes de datos


Los almacenes de claves/valores están altamente optimizados para aplicaciones que realizan
búsquedas simples, pero son menos adecuados para aquellos sistemas que necesitan consultar
datos en diferentes almacenes de claves/valores. Los almacenes de claves/valores tampoco están
optimizados para los escenarios donde la consulta por valor es importante, en lugar de realizar
búsquedas basadas únicamente en claves. Por ejemplo, con una base de datos relacional, puede
encontrar un registro utilizando una cláusula WHERE, pero los almacenes de claves/valores
generalmente no tienen este tipo de capacidad de búsqueda para los valores.
Un único almacén de claves/valores puede ser extremadamente escalable, ya que el almacén de
datos puede distribuir fácilmente datos entre múltiples nodos en máquinas independientes.

Servicios de Azure pertinentes:

• 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


Una base de datos de documentos es conceptualmente similar a un almacén de claves/valores, salvo
porque almacena una colección de campos y datos con nombre (conocidos como documentos), cada
uno de los cuales puede ser elementos escalares simples o elementos compuestos tales como listas
y colecciones secundarias. Los datos en los campos de un documento se pueden codificar de varias
maneras, incluyendo XML, YAML, JSON, BSON o incluso se pueden almacenar como texto sin formato. A
diferencia de los almacenes de claves/valores, los campos de los documentos están expuestos al sistema
de administración de almacenamiento, lo que permite que una aplicación consulte y filtre datos utilizando
los valores en estos 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 detalles de un
cliente, una orden o una combinación de ambos. Un solo documento puede contener información que se
distribuirá 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 forma libre proporciona gran flexibilidad. Las aplicaciones pueden almacenar diferentes datos
en los documentos a medida que cambian los requisitos empresariales.
La aplicación puede recuperar documentos con la clave del documento. Este es un identificador único
para el documento, que a menudo es hash, para ayudar a distribuir los datos de manera uniforme.
Algunas bases de datos de documentos crean la clave del documento automáticamente. Otras le permiten
especificar un atributo del documento para usarlo como clave. La aplicación también puede consultar
documentos en función del valor de uno o más campos. Algunas bases de datos de documentos admiten
indexación para facilitar la búsqueda rápida de documentos basados en uno o más campos indexados.
Muchas bases de datos de documentos admiten actualizaciones establecidas, lo que permite que una

45 CAPÍTULO 2c | Información general sobre los almacenes de datos


aplicación modifique los valores de campos específicos en un documento sin necesidad de reescribir todo
el documento. Las operaciones de lectura y escritura en varios campos en un único documento suelen ser
atómicas.

Servicio de Azure pertinente: Cosmos DB

Bases de datos de gráficos


Una base de datos de gráficos almacena dos tipos de información, nodos y bordes. Puede pensar en
los nodos como entidades. Bordes que especifican las relaciones entre nodos. Tanto los nodos como los
bordes pueden tener propiedades que brinden información sobre ese nodo o borde, similar a las columnas
de una tabla. Los bordes también pueden tener una dirección que indique la naturaleza de la relación.

El objetivo de una base de datos de gráficos es permitir que una aplicación realice de manera eficiente
las consultas que atraviesan la red de nodos y bordes, y analizar las relaciones entre las entidades. El
siguiente diagrama 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 informes y
el departamento en el que trabajan los empleados. En este gráfico, las flechas en los bordes muestran la
dirección de las relaciones.

46 CAPÍTULO 2c | Información general sobre los almacenes de datos


Esta estructura facilita la realización de consultas tales como "Buscar a todos los empleados que
son subordinados directos o indirectos de Sarah" o "¿Quién trabaja en el mismo departamento
que John?". Para gráficos grandes con muchas entidades y relaciones, puede realizar análisis muy
complejos de forma muy rápida. Muchas bases de datos de gráficos proporcionan un lenguaje de
consulta que puede usar para recorrer una red de relaciones de manera eficiente.

Servicio de Azure pertinente: 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 parecer muy similar a una base de datos
relacional, al menos conceptualmente. El verdadero poder de una base de datos de familias de
columnas radica en su enfoque no normalizado para estructurar datos escasos.
Puede pensar en una base de datos de familias de columnas que contiene datos tabulares con filas
y columnas, pero las columnas se dividen en grupos conocidos como familias de columnas. Cada
familia de columnas contiene un conjunto de columnas que se relacionan lógicamente entre sí y
que normalmente se recuperan o manipulan como una unidad. Otros datos a los que se accede por
separado se pueden almacenar en familias de columnas independientes. Dentro de una familia de
columnas, las columnas nuevas se pueden agregar de forma dinámica y las filas pueden ser escasas (es
decir, no es necesario que cada columna tenga un valor).

El siguiente diagrama muestra un ejemplo con dos familias de columnas, Identidad e Información de
contacto. Los datos de una única entidad tienen la misma clave de fila en cada familia de columnas.
Esta estructura, donde las filas para cualquier objeto determinado en una familia de columnas pueden
variar de forma dinámica, es una ventaja importante del enfoque de familia de columnas, lo que hace
que esta forma de almacenamiento de datos sea muy adecuada para almacenar datos estructurados
y volátiles.A diferencia de un almacén de claves/valores o de una base de datos de documentos, la
mayoría de las bases de datos de familias de columnas almacenan datos en orden de clave, en lugar
de hacerlo computando un hash. Muchas implementaciones le permiten crear índices sobre columnas
específicas en una familia de columnas. Los índices le permiten recuperar datos por valor de columnas,
en lugar hacerlo por clave de fila.
Las operaciones de lectura y escritura para una fila suelen ser atómicas con una única familia de
columnas, aunque algunas implementaciones proporcionan atomicidad en toda la fila, abarcando
múltiples familias de columnas.
Servicio de Azure pertinente: HBase en HDInsight. Para obtener información, vaya a
https://azure.microsoft.com/services/cosmos-db

47 CAPÍTULO 2c | Información general sobre los almacenes de datos


Análisis de datos
Los almacenes de análisis de datos ofrecen soluciones masivamente paralelas para ingerir, almacenar
y analizar datos. Estos datos se distribuyen entre varios servidores mediante una arquitectura de uso
compartido para maximizar la escalabilidad y minimizar 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 llega en diversos formatos desde múltiples flujos, al mismo tiempo que siguen
procesando nuevas consultas.
Servicios de Azure pertinentes:
• SQL Data Warehouse
• Azure Data Lake

Bases de datos de motores de búsqueda


Una base de datos de motores de búsqueda admite la capacidad de buscar información que se
encuentra almacenes de datos y servicios externos. Una base de datos de motores de búsqueda se
puede usar para indexar volúmenes enormes de datos y proporcionar acceso casi en tiempo real a
estos índices. Aunque las bases de datos de motores de búsqueda suelen considerarse sinónimos
de la web, muchos sistemas a gran escala las usan para proporcionar capacidades de búsqueda
estructuradas y ad hoc sobre sus propias bases de datos.
Las características clave de una base de datos de motor de búsqueda son la capacidad de almacenar
e indexar información muy rápidamente y proporcionar tiempos de respuesta rápidos a las
solicitudes de búsqueda. Los índices pueden ser multidimensionales y pueden admitir búsquedas
de texto libre en grandes volúmenes de datos de texto. La indexación puede realizarse mediante un
modelo pull, activado por la base de datos de motores de búsqueda, o mediante un modelo push,
iniciado por el código de una aplicación externa.
La búsqueda puede ser exacta o aproximada. Una búsqueda aproximada encuentra documentos
que coinciden con un conjunto de términos y calcula el nivel de coincidencia entre estos. Algunos
motores de búsqueda también admiten el análisis lingüístico, el que puede arrojar coincidencias
basadas en sinónimos, expansiones de género (por ejemplo, coincidencias entre perros y mascotas) y
derivaciones (palabras coincidentes que poseen la misma raíz).
Servicio de Azure pertinente: Azure Search

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 optimizada para este tipo de datos. Las bases de
datos de series temporales deben admitir una gran cantidad de escrituras, ya que generalmente
recopilan grandes cantidades de datos en tiempo real de un gran número de fuentes. Las
actualizaciones son poco comunes, y las eliminaciones a menudo se realizan como operaciones
masivas. Aunque los registros escritos en una base de datos de series temporales son generalmente
pequeños, en general hay una gran cantidad de registros y el tamaño total de los datos puede crecer
rápidamente.
Las bases de datos de series temporales son buenas para almacenar datos de telemetría. Los
escenarios incluyen sensores de IoT o contadores de aplicaciones/sistemas.
Servicio de Azure pertinente: Time Series Insights

48 CAPÍTULO 2c | Información general sobre los almacenes de datos


Almacenamiento de objetos
El almacenamiento de objetos está optimizado para almacenar y recuperar objetos binarios grandes
(imágenes, archivos, transmisiones de video y audio, objetos y documentos de datos de aplicaciones
grandes, imágenes de discos de máquinas virtuales). Los objetos en estos tipos de almacenes se
componen de los datos almacenados, algunos metadatos y una ID única para acceder al objeto. Los
almacenes de objetos permiten administrar cantidades muy grandes de datos no estructurados.

Servicio de Azure pertinente: Blob Storage

Archivos compartidos
A veces, el uso de archivos planos simples puede ser el medio más efectivo para almacenar y
recuperar información. El uso compartido de archivos permite acceder a los archivos a través de
una red. Con una seguridad adecuada y mecanismos de control de acceso simultáneos, el uso
compartido de datos puede permitir que los servicios distribuidos proporcionen acceso a datos
altamente escalable para realizar operaciones básicas de bajo nivel, como solicitudes simples de
lectura y escritura.

Servicio de Azure pertinente: File Storage

49 CAPÍTULO 2c | Información general sobre los almacenes de datos


2d

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

Consideraciones generales
Para comenzar su comparación, reúna la mayor cantidad de información posible sobre sus
necesidades de datos. Esta información le ayudará a determinar qué tipos de almacenamiento de
datos satisfarán sus necesidades.

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

● Tamaño de datos. ¿Qué tan grandes son las entidades que necesita almacenar? ¿Se deberán
mantener estas entidades como un único documento, o se pueden dividir en varios documentos,
tablas, colecciones, etc.?

● Escalado y estructura. ¿Cuál es la cantidad total de capacidad de almacenamiento que necesita?


¿Prevé crear particiones de los datos?

● Relaciones de datos. ¿Tendrán sus datos que admitir relaciones de uno a muchos o de muchos
a muchos? ¿Son las relaciones en sí una parte importante de los datos? ¿Tendrá que unirse o
combinar datos del mismo conjunto de datos o de conjuntos de datos externos?

● Modelo de coherencia. ¿Qué tan importante es que las actualizaciones realizadas en un nodo
aparezcan en otros nodos, antes de que se puedan hacer otros cambios? ¿Puede aceptar
coherencia final? ¿Necesita garantías de ACID para las transacciones?

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

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


● Simultaneidad. ¿Qué tipo de mecanismo de simultaneidad desea usar al actualizar y sincronizar
datos? ¿La aplicación realizará muchas actualizaciones que podrían entrar en conflicto? Si es así,
puede que se requiera un bloqueo de registros y un control de concurrencia pesimista. Como
alternativa, ¿puede admitir controles de concurrencia optimistas? Si es así, ¿es suficiente con un
control de concurrencia simple basado en marcas de tiempo o necesita la funcionalidad adicional
del control de concurrencia de varias versiones?
● Movimiento de datos. ¿Su solución tendrá que realizar tareas de ETL para mover datos a otros
almacenes de datos?
● Ciclo de vida de los datos. ¿Los datos se escriben una sola vez y se leen muchas veces? ¿Se
pueden mover a almacenamiento inactivo?
● Otras características admitidas. ¿Necesita alguna otra característica específica, 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 de tasas de ingestión de datos y tasas de procesamiento de datos? ¿Cuáles
son los tiempos de respuesta aceptables para consultar y agregar datos una vez ingeridos? ¿Qué
tan grande deberá ser la escalada del almacén de datos? ¿Su carga de trabajo es más pesada de
leer o de escribir?
● Confiabilidad. ¿Qué SLA general necesita admitir? ¿Qué nivel de tolerancia a errores necesita
proporcionar a los consumidores de datos? ¿Qué tipo de capacidades de copia de seguridad y
restauración necesita?
● Replicación. ¿Sus datos deberán distribuirse entre múltiples réplicas o regiones? ¿Qué tipo de
capacidades de replicación de datos necesita?
● Límites. ¿Los límites de un almacén de datos específicos admiten sus requisitos de escala,
número de conexiones y rendimiento?

Administración y costo
● Servicio administrado. Cuando sea posible, use un servicio de datos administrados, a menos
que requiera capacidades específicas que solo se puedan encontrar en un almacén de datos
hospedado en IaaS.
● Disponibilidad de la región. Para los servicios administrados, ¿el servicio está disponible en
todas las regiones de Azure? ¿Su solución necesita hospedarse en ciertas regiones de Azure?
● Portabilidad. ¿Sus datos deberán migrar a centros de datos locales y externos, o a otros
entornos de hospedaje en la nube?
● Licencias. ¿Tiene una preferencia de tipo de licencia propia versus OSS? ¿Existen otras
restricciones externas sobre qué tipo de licencia puede usar?
● Costo total. ¿Cuál es el costo total de uso del servicio dentro de su solución? ¿Cuántas instancias
deberán ejecutarse para admitir sus requisitos de tiempo de actividad y rendimiento? Al realizar
este cálculo, tenga en cuenta los costos operativos. Una motivo para preferir los servicios
administrados es su reducido costo operativo.
● Rentabilidad. ¿Puede crear particiones de los datos para almacenarlos de forma más rentable?
Por ejemplo, ¿puede mover objetos grandes de una costosa base de datos relacionales a un
almacén de objetos?

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


Seguridad
● Seguridad. ¿Qué tipo de cifrado requiere? ¿Necesita cifrado en reposo? ¿Qué mecanismo de
autenticación desea usar para conectarse a sus datos?

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

● Requisitos de red. ¿Necesita restringir o administrar el acceso a sus datos desde otros recursos
de la red? ¿Solo se debe poder acceder a los datos dentro del entorno de Azure? ¿Solo se debe
poder acceder a los datos desde direcciones IP o subredes específicas? ¿Se debe poder acceder
desde aplicaciones o servicios hospedados localmente o en otros centros de datos externos?

DevOps
● Conjunto de habilidades. ¿Hay algún lenguaje de programación, sistema operativo u otra
tecnología específica que sea más fácil de usar para su equipo? ¿Hay otros con los que su equipo
tendría dificultades para trabajar?

● Clientes. ¿Sus lenguajes de desarrollo cuentan con buen soporte al cliente?

Las siguientes secciones comparan varios modelos de almacén de datos en términos de perfil de
carga 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 realizan
regularmente.
• Se deben completar múltiples operaciones en una sola transacción.
• Requiere funciones de incorporación para realizar tabulaciones cruzadas.
• Se requiere una integración sólida con herramientas de informes.
• Las relaciones se aplican mediante restricciones de la base de datos.
• Los índices se usan para optimizar el rendimiento de la consulta.
• Permite el acceso a subconjuntos específicos de datos.

Tipo de datos
• Los datos están altamente normalizados.
• Se requieren y aplican esquemas de base de datos.
• Relaciones de muchos a muchos entre entidades de datos en la base de datos.
• Las restricciones se definen en el esquema y se imponen sobre cualquier dato en la base de
datos.
• Los datos requieren alta 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 gran coherencia. Las transacciones operan de manera tal que se
garantiza que todos los datos son totalmente coherentes para todos los usuarios y procesos.
• Las entradas de datos individuales son de tamaño pequeño a mediano.

Ejemplos
• Línea de negocio (administración de capital humano, administración de relaciones con el cliente,
planificación de recursos empresariales)

• Administración de inventario

• Base de datos de informes

• Contabilidad

• Administración de activos

• Administración de fondos

• Administración de órdenes

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 realizan regularmente.
• Sin falta de correspondencia de impedancia relacional de objetos. Los documentos pueden
coincidir mejor con las estructuras de objeto usadas en el código de la aplicación .
• La simultaneidad optimista es de uso más común.
• Los datos deben ser modificados y procesados por una aplicación de consumo.
• Los datos requieren de un índice en varios campos.
• Los documentos individuales se recuperan y escriben como un solo bloque.

Tipo de datos
• Los datos se pueden administrar de manera desnormalizada.
• Los datos del documento individual son de tamaño relativamente pequeño.
• Cada tipo de documento puede usar su propio esquema.
• Los documentos pueden incluir campos opcionales.
• Los datos del documento son semiestructurados, lo que significa que los tipos de datos de cada
campo no están estrictamente definidos.
• Se admite la incorporació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
• Administración de contenidos
• Datos de operaciones
• Administración de inventario
• Datos del historial de transacciones
• Vista materializada de otros almacenes NoSQL. Reemplaza la indexación de archivos/BLOB.

Almacenes de claves/valores
Carga de trabajo
• Los datos se identifican y se accede a ellos con una clave de ID única, como un diccionario.
• Inmensamente escalable.
• No se requieren combinaciones, bloqueos o uniones.
• No se usan mecanismos de incorporación.
• En general no se usan índices secundarios.

Tipo de datos
• Los datos tienden a ser de gran tamaño.
• Cada clave está asociada a un solo valor, que es un BLOB de datos no administrado.
• No hay aplicación de esquema.
• Sin relaciones entre entidades.

Ejemplos
• Almacenamiento de datos en caché
• Administración de sesiones
• Administración de preferencias del usuario y perfil
• Recomendación de productos y publicación de anuncios
• Diccionarios

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


Almacenes de claves/valores
Carga de trabajo
• Los datos se identifican y se accede a ellos con una clave de ID única, como un diccionario.
• Inmensamente escalable.
• No se requieren combinaciones, bloqueos o uniones.
• No se usan mecanismos de incorporación.
• En general no se usan índices secundarios.

Tipo de datos
• Los datos tienden a ser de gran tamaño.
• Cada clave está asociada a un solo valor, que es un BLOB de datos no administrado.
• No hay aplicación de esquema.
• Sin relaciones entre entidades.

Ejemplos
• Almacenamiento de datos en caché
• Administración de sesiones
• Administración de preferencias del usuario y perfil
• Recomendación de productos y publicación de anuncios
• Diccionarios

Bases de datos de gráficos


Carga de trabajo
• Las relaciones entre los elementos de datos son muy complejas, ya que implican muchos saltos
entre los elementos de datos relacionados.

• La relación entre los elementos de datos es dinámica y cambia con el tiempo.


• Las relaciones entre los objetos son ciudadanos de primera clase, no necesitan claves externas ni
combinaciones para transitar.

Tipo de datos

• Los datos se componen de nodos y relaciones.

• Los nodos son similares a filas de tabla o documentos JSON.

• Las relaciones son tan importantes como los nodos y se exponen directamente en el lenguaje de
consulta.
• Los objetos compuestos, como una persona con varios números de teléfono, tienden a dividirse
en nodos separados y más pequeños, que se combinan con relaciones transitables.

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


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

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 comunes.
• Diseñada para proporcionar un alto rendimiento y acceso de baja latencia.
• Admite el fácil acceso a consultas para un conjunto específico de campos dentro de un registro
mucho más grande.
• Inmensamente escalable.

Tipo de datos
• Los datos se almacenan en tablas que constan de una columna de claves y una o más familias de
columnas.
• Las columnas específicas pueden variar según las filas individuales.
• Se puede tener acceso a las celdas individuales a través de los comandos get y put
• Mediante un comando de escaneo se pueden devolver varias filas.

Ejemplos
• Recomendaciones
• Personalización
• Datos de sensores
• Telemetría
• Envío de mensajes
• Análisis de medios sociales
• Análisis web
• Supervisión de la actividad
• El tiempo y otros datos de serie cronológica

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 múltiples orígenes y servicios.
• Las consultas son ad-hoc y pueden ser complejas.
• Requiere incorporación.
• Se requiere búsqueda de texto completo.
• Se requiere una consulta de autoservicio ad hoc.
• Se requiere el análisis de datos con índice en todos los campos.

Tipo de datos
• Semiestructurado o no estructurado
• Texto
• Texto con referencia a datos estructurados

Ejemplos
• Catálogos de productos
• Búsqueda en el sitio
• Registro
• Análisis
• Sitios de compras

Almacén de datos
Carga de trabajo
• Análisis de datos
• BI empresarial

Tipo de datos
• Datos históricos provenientes de varios orígenes de datos.
• Normalmente se desnormaliza en un esquema de "estrella" o "copo de nieve", que consta de
tablas de hechos y dimensiones.
• Por lo general, se carga con datos nuevos de forma programada.
• Las tablas de dimensiones en general incluyen múltiples versiones históricas de una entidad, a las
que se denomina "dimensión lentamente cambiante".

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 gran parte de las operaciones (95-99 %) se escribe.
• Los registros generalmente se anexan secuencialmente en orden cronológico.
• Las actualizaciones son poco comunes.
• Las eliminaciones se realizan de forma masiva, en bloques o registros contiguos.
• Las solicitudes de lectura pueden ser más grandes que la memoria disponible.
• Es común que se produzcan varias lecturas simultáneamente.
• Los datos se leen secuencialmente en orden cronológico ascendente o descendente.

Tipo de datos
• Marca de tiempo que se usa como clave principal y mecanismo de clasificación.
• Mediciones de la entrada o descripciones de lo que representa la entrada.
• Etiquetas que definen la información adicional acerca del tipo, el origen y otra información sobre
la entrada.

Ejemplos
• Supervisión y telemetría de eventos.
• Sensor u otros datos de IoT.

Almacenamiento de objetos
Carga de trabajo
• Identificada por clave.
• Los objetos pueden ser de acceso público o privado.
• El contenido suele ser un activo, como una hoja de cálculo, una imagen o un archivo de video.
• El contenido debe ser duradero (permanente) y externo a cualquier nivel de aplicación o
máquina virtual.

Tipo de datos
• Los datos son de gran tamaño.
• Datos de blob.
• El valor es opaco.

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


Ejemplos
• Imágenes, videos, documentos de Office, PDF
• CSS, Scripts, CSV
• HTML estáticos, JSON
• Archivos de registro y auditoría
• Copias de seguridad de la base de datos

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

Tipo de datos
• Archivos en un conjunto jerárquico de carpetas.
• Se puede tener acceso con bibliotecas de E/S estándar.

Ejemplos
• Archivos heredados.
• Se puede tener acceso a contenido compartido entre varias VM o instancias de aplicaciones.

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


3

Diseñe su
aplicación de
Azure: principios
del diseño
Ahora que ha elegido su arquitectura y sus tecnologías para procesos
y almacenes de datos, está listo para comenzar a diseñar y desarrollar
su aplicación en la nube. Esta sección y las dos siguientes proporcionan
orientación y recursos para el diseño óptimo de una aplicación para la
nube.

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

60 CAPÍTULO 3: Diseñe su aplicación de Azure: principios del diseño


1. Diseño para recuperación automática. En un sistema distribuido, los errores ocurren. Diseñe su
aplicación para que se recupere automáticamente cuando se produzcan errores.

2. Hacer redundantes todas las cosas. Genere redundancia en su aplicación, para evitar los únicos
puntos de error.

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


lograr escalabilidad.

4. Diseño para escalado horizontal. Diseñe su aplicación para que pueda escalar horizontalmente,
mediante la incorporación o eliminación de nuevas instancias según se requiera.

5. Partición alrededor de límites. Use la creación de particiones para evitar que se alcancen los
límites de la base de datos, la red y el proceso.

6. Diseño para operaciones. Diseñe su aplicación de modo tal que el equipo de operaciones tenga
las herramientas que necesita.

7. Uso de servicios administrados. Cuando sea posible, use la plataforma como servicio (PaaS) en
lugar de la infraestructura como servicio (IaaS).

8. Uso del mejor almacén de datos para el trabajo. Elija la tecnología de almacenamiento que
mejor se adapte a sus datos y al uso que le dará.

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

10. Creación para las necesidades del negocio. Cada decisión de diseño debe estar justificada por
un requisito de negocios.

61 CAPÍTULO 3: Diseñe su aplicación de Azure: principios del diseño


3a

Diseño para
recuperación
automática
Diseñe su aplicación para que se recupere automáticamente cuando se
produzcan errores
En un sistema distribuido, los errores ocurren. El hardware puede fallar. La red puede presentar
errores transitorios. En casos aislados, un servicio o una región completa puede experimentar una
interrupción, incluso en esos casos debe existir un plan.
Por lo tanto, diseñe una aplicación que se recupere automáticamente cuando se produzcan errores.
Esto requiere un enfoque triple:

• Detección de errores.
• Respuesta fácil ante errores.
• Registro y supervisión de errores, para proporcionar información operativa.
Su respuesta a un tipo de error específico puede depender de los requisitos de disponibilidad de su
aplicación. Por ejemplo, si requiere una disponibilidad muy alta, puede realizar una conmutación por
error automáticamente a una región secundaria durante una interrupción regional. Sin embargo, eso
implicará un costo mayor que una implementación en una sola región.
Además, no se limite a considerar solo los grandes eventos como las interrupciones regionales, que
en general son poco comunes. Debe concentrarse de igual manera, si no más, en el manejo de errores
locales de corta duración, como errores de conectividad de red o de conexiones de bases de datos.

Recomendaciones
Reintente las operaciones con errores. 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.
Proteger los servicios remotos que presentan errores (interruptor). Es bueno reintentar después
de un error transitorio, pero si este persiste, puede terminar con muchas llamadas que reportan
errores en el servicio. Esto puede provocar errores en cascada a medida que se realizan copias de
seguridad de las solicitudes. Use el Patrón de interruptor para que se produzca una falla rápida (sin
realizar la llamada remota) cuando existan posibilidades de error en una operación.
Aislar los recursos críticos (cierre). Los errores en un subsistema a veces pueden provocar errores
en cascada. Esto puede ocurrir si un error provoca que algunos recursos, como subprocesos o
sockets, no se liberen de manera oportuna, lo que lleva al agotamiento de los recursos. Para evitar
esto, cree particiones de un sistema en grupos aislados, de modo que un error en una partición no
deje inactivo todo el sistema.
62 CAPÍTULO 3a | Diseño para recuperación automática
Realizar una nivelación de carga. Las aplicaciones pueden experimentar picos repentinos en el
tráfico que pueden saturar los servicios en el back-end. Para evitar que ocurra esto, use el patrón
de nivelación de carga basado en cola que los elementos de trabajo en la cola se ejecuten de forma
asincrónica. La cola actúa como un búfer que suaviza los picos en la carga.

Realizar una conmutación por error. Si no se puede llegar a una instancia, realice una conmutación
por error a otra instancia. Para cosas que no tienen estado, como un servidor web, coloque varias
instancias detrás de un equilibrador de carga o administrador de tráfico. Para cosas que almacenan
estados, como una base de datos, use réplicas y conmutaciones por error. Según el almacén de datos
y cómo se replica, esto puede requerir que la aplicación se ocupe de la coherencia final.

Compensar las transacciones fallidas. En general, evite las transacciones distribuidas, ya que
requieren coordinación entre servicios y recursos. En cambio, componga una operación a partir de
transacciones individuales más pequeñas. Si la operación falla a mitad de camino, use transacciones
compensatorias para deshacer cualquier paso que ya se haya completado.

Contar con puntos de control para las transacciones de ejecución prolongada. Los puntos
de control pueden proporcionar resistencia si se producen errores en una operación de ejecución
prolongada. Cuando la operación se reinicia (por ejemplo, porque es recogida por otra máquina
virtual), se puede reanudar desde el último punto de control.

Degradar fácilmente. A veces no se puede evitar un problema, pero puede proporcionar una
funcionalidad reducida que aún es útil. Piense en una aplicación que muestra un catálogo de libros.
Si la aplicación no puede recuperar la imagen en miniatura de la portada, es posible que muestre
una imagen de marcador de posición. Puede que subsistemas completos no sean 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 órdenes.

Limitar a clientes. A veces, una pequeña cantidad de usuarios crea una carga excesiva, lo que puede
reducir la disponibilidad de la aplicación para otros usuarios. En esta situación, limite al cliente por un
periodo determinado. Consulte Patrón de limitación.

Bloquear a los malos actores. El hecho de que limite a un cliente no significa que el cliente actuara
maliciosamente. Simplemente significa que el cliente excedió su cuota de servicio. Sin embargo, si un
cliente excede constantemente su cuota o tiene un mal comportamiento, puede bloquearlo. Defina
un proceso manual para el usuario que solicite ser desbloqueado.

Usar elección de líder. Cuando necesite coordinar una tarea, use Elección de líder para seleccionar
un coordinador. De esa forma, el coordinador no es un solo punto de error. Si el coordinador falla,
se selecciona uno nuevo. En lugar de implementar un algoritmo de elección de líder desde cero,
considere una solución lista para usar como Zookeeper.

Realizar pruebas con inserción de errores. Con demasiada frecuencia, la ruta de éxito está bien
probada, no así la ruta de error. Un sistema podría ejecutarse en producción durante mucho tiempo
antes de que se aplique una ruta de error. Use una inserción de errores para probar la resistencia del
sistema ante errores, ya sea mediante la activación de errores reales o mediante simulaciones.

Aceptar la ingeniería del caos. La ingeniería del caos amplía la noción de inserción de errores al
insertar de forma aleatoria errores o condiciones anormales en instancias de producción.

Para obtener un enfoque estructurado que haga que sus aplicaciones se recuperen automáticamente,
consulte Diseño de aplicaciones resistentes para Azure.

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


3b

Hacer redundantes
todas las cosas
Genere redundancia en su aplicación, para evitar los únicos puntos de
error
Una aplicación resistente se enruta en torno a un error. Identifique las rutas críticas en su aplicación.
¿Hay redundancia en cada punto de la ruta? Si un subsistema falla, ¿la aplicación realizará una
conmutación por error a otra cosa?

Recomendaciones
Considere los requisitos empresariales. La cantidad de redundancia integrada en un sistema puede
afectar tanto el costo como la complejidad. Se debe informar a su arquitectura de sus requisitos
empresariales, como el objetivo de tiempo de recuperación (RTO). Por ejemplo, una implementación
en varias regiones es más costosa que una implementación en una sola región y su administración es
más complicada. Necesitará procedimientos operativos para manejar la conmutación por error y la
conmutación por recuperación. El costo y la complejidad adicionales pueden justificarse en algunos
escenarios de negocios y no justificarse en otros.

Coloque VM detrás de un equilibrador de carga. No use una sola VM para cargas de trabajo
críticas. En cambio, coloque varias MV detrás de un equilibrador de carga. Si alguna VM deja de
estar disponible, el equilibrador de carga distribuye el tráfico a las demás VM en buen estado. Para
obtener información sobre cómo implementar esta configuración, consulte Múltiples VM para
escalabilidad y disponibilidad.

64 CAPÍTULO 3b | Hacer redundantes todas las cosas


Replicar bases de datos. Azure SQL Database y Cosmos DB replican automáticamente los datos dentro
de una región, y usted puede habilitar la replicación geográfica entre regiones. Si usa una solución de
base de datos de IaaS, elija una que admita replicación y conmutación por error, como los Grupos de
disponibilidad Always On de SQL Server. Para obtener información, vaya a https://docs.microsoft.com/
en-us/sql/database-engine/availability-groups/windows/always-on-availability-groups-sql-server.

Habilitar la replicación geográfica. La replicación geográfica para Azure SQL Database y Cosmos DB crea
réplicas secundarias legibles de sus datos en una o más regiones secundarias. En caso de una interrupción,
la base de datos puede realizar una conmutación por error de las escrituras a la región secundaria. Para
obtener más información acerca de Azure SQL Database, vaya a https://docs.microsoft.com/en-us/azure/
sql-database/sql-database-geo-replication-overview. Para obtener más información acerca de Cosmos DB,
vaya a https://docs.microsoft.com/en-us/azure/documentdb/documentdb-distribute-data-globally.

Crear particiones para la disponibilidad. La creación de particiones de bases de datos se usa a menudo
para mejorar la escalabilidad, pero también puede mejorar la disponibilidad. Si un fragmento queda
inactivo, aún se puede tener acceso a los demás fragmentos. Un error en un fragmento solo interrumpirá
un subconjunto del total de transacciones.
Implementar en más de una región. Para obtener la mayor disponibilidad, implemente la aplicación en
más de una región. De esta forma, en el caso poco común de que un problema afecte a una región entera,
la aplicación puede realizar una conmutación por error a otra región. El siguiente diagrama muestra una
aplicación de varias regiones que usa el Administrador de tráfico de Azure para manejar la conmutación por
error.

Sincronizar la conmutación por error de front-end y back-end. Use el Administrador de tráfico de


Azure para realizar conmutaciones por error de front-end. Si no es posible tener acceso a front-end en
una región, el Administrador de tráfico enrutará nuevas solicitudes a la región secundaria. En función de
su solución de base de datos, es posible que deba coordinar la conmutación por error en la base de datos.
Usar conmutación por error automática, pero conmutación por recuperación manual. Use el
Administrador de tráfico para realizar la conmutación por error automática, pero no para una conmutación
por recuperación automática. La conmutación por recuperación automática conlleva el riesgo de que
pueda cambiar a la región principal antes de que la región esté completamente recuperada. En su lugar,
verifique que todos los subsistemas de las aplicaciones estén en buen estado antes de realizar una
conmutación por recuperación manual. Además, en función de la base de datos, es posible que deba
verificar la coherencia de los datos antes de realizar la conmutación por recuperación.
Incluir redundancia del Administrador de tráfico. El Administrador de tráfico es un posible punto de
error. Revise el SLA del Administrador de tráfico y determine si el uso del Administrador de tráfico cumple
con sus requisitos empresariales de alta disponibilidad. De lo contrario, considere agregar otra solución
de administración de tráfico como conmutación por recuperación. Si el servicio de Administrador de
tráfico de Azure presenta errores, cambie sus registros CNAME en DNS para designar el otro servicio de
administración de tráfico.

65 CAPÍTULO 3b | Hacer redundantes todas las cosas


3c

Minimizar la
coordinación
Minimice la coordinación entre los servicios de aplicación para lograr
escalabilidad.
La mayoría de las aplicaciones en la nube constan de múltiples servicios de aplicaciones: interfaces
web, bases de datos, procesos de negocio, informes y análisis, etc. Para lograr escalabilidad y
confiabilidad, cada uno de esos servicios debe ejecutarse en múltiples instancias.
¿Qué sucede cuando dos instancias intentan realizar operaciones simultáneas que afectan algún
estado compartido? En algunos casos, debe haber coordinación entre nodos, por ejemplo para
preservar las garantías de ACID. En este diagrama, Node2 está esperando que Node1 libere un
bloqueo de base de datos:

La coordinación limita los beneficios de la escala horizontal y crea cuellos de botella. En este ejemplo,
a medida que escala la aplicación y agrega más instancias, verá una mayor contención de bloqueo.
En el peor de los casos, las instancias de front-end pasarán la mayor parte del tiempo esperando
bloqueos. La semántica "exactamente una vez" es otra fuente frecuente de coordinación. Por
ejemplo, una orden debe procesarse exactamente una vez. Dos trabajadores escuchan las nuevas
órdenes. Worker1 toma una orden para procesarla. La aplicación debe garantizar que Worker2 no
duplique el trabajo, pero también que si Worker1 falla, la orden no se elimine.

66 CAPÍTULO 3c | Minimizar la coordinación


Puede usar un patrón como programador-agente-supervisor para coordinar entre los trabajadores,
pero en este caso un mejor enfoque podría ser crear una partición del trabajo. A cada trabajador se
le asigna cierto rango de órdenes (por ejemplo, por región de facturación). Si un trabajador falla, una
nueva instancia retoma la instancia anterior, pero sin que compitan varias instancias.

Recomendaciones
Aceptar la coherencia final. Cuando se distribuyen los datos, se necesita coordinación para aplicar
garantías de coherencia sólidas. Por ejemplo, supongamos que una operación actualiza dos bases
de datos. En lugar de ponerlas en un ámbito de transacción único, lo mejor es que el sistema pueda
acomodar la coherencia final, tal vez mediante el uso del patrón de transacción compensatoria para
regresar en forma lógica después de un error.
Usar eventos de dominio para sincronizar el estado. Un evento de 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 usar una transacción global para coordinar múltiples
servicios. Si se usa este enfoque, el sistema debe tolerar la coherencia final (ver el ítem anterior).
Considerar patrones como CQRS y el abastecimiento de eventos. Estos dos patrones pueden
ayudar a reducir la contención entre las cargas de trabajo de lectura y 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, los cambios de estado se registran como una serie
de eventos en un almacén de datos solo para anexar. Anexar un evento a la transmisión 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 usar el abastecimiento
de eventos, el almacén de solo lectura puede escuchar los mismos eventos para crear una
instantánea legible del estado actual, optimizado para consultas. Sin embargo, antes de adoptar
CQRS o el abastecimiento de eventos, tenga en cuenta los desafíos que presenta este enfoque. Para
obtener más información, consulte el estilo de arquitectura CQRS.

Crear una partición de datos. Evite poner todos sus datos en un 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 única base de datos, la
partición de los datos en fragmentos puede mejorar la simultaneidad, porque un servicio que escribe
en un fragmento no afecta a un servicio que escribe en un fragmento diferente.
Diseñar operaciones idempotentes. Cuando sea posible, diseñe las operaciones para que sean
idempotentes. De esta forma, pueden manejarse usando la semántica al menos una vez. Por ejemplo,
puede colocar elementos de trabajo en una cola. Si un trabajador falla en el medio de una operación,
otro trabajador simplemente retoma el elemento de trabajo.
Usar procesamiento paralelo asincrónico. Si una operación requiere la ejecución asincrónica de
varios pasos (como llamadas de servicio remoto), es posible que pueda llamarlos en paralelo y luego
agregar los resultados. Este enfoque supone que cada paso no depende de los resultados del paso
anterior.
Usar la simultaneidad optimista cuando sea posible. El control de concurrencia pesimista usa
bloqueos de base de datos para evitar conflictos. Esto puede provocar un rendimiento deficiente y
reducir la disponibilidad. Con un control de concurrencia optimista, cada transacción modifica una
copia o instantánea de los datos. Cuando se compromete la transacción, el motor de la base de
datos valida la transacción y rechaza cualquier transacción que afecte la coherencia de la base de
datos.

67 CAPÍTULO 3c | Minimizar la coordinación


Azure SQL Database y SQL Server admiten la simultaneidad optimista mediante el aislamiento de
instantáneas. Para obtener más información vaya a https://docs.microsoft.com/en-us/sql/t-sql/
statements/set-transaction-isolation-level-transact-sql. Algunos servicios de almacenamiento
de Azure admiten la simultaneidad optimista mediante el uso de etiquetas electrónicas, que
incluyen la API de DocumentDB y Azure Storage. Para obtener más información acerca de la API de
DocumentDB, vaya a https://docs.microsoft.com/en-us/azure/documentdb/documentdb-faq.
Considerar MapReduce u otros algoritmos paralelos y distribuidos. Según los datos y el tipo
de trabajo a realizar, es posible que pueda dividir el trabajo en tareas independientes que pueden
ser realizadas por varios nodos trabajando en paralelo. Consulte el estilo de arquitectura de big
compute.
Usar elección de líder para la coordinación. En los casos en que deba coordinar operaciones,
asegúrese de que el coordinador no se convierta en un punto de error en la aplicación. Al utilizar
el patrón de elección de líder, una instancia es el líder en cualquier momento y actúa como
coordinador. Si el líder falla, se elige una nueva instancia para que sea líder.

68 CAPÍTULO 3c | Minimizar la coordinación


3d

Diseño para escalado


horizontal
Diseñe su aplicación para que pueda escalar horizontalmente
Una de las ventajas principales de la nube es el escalado elástico: la posibilidad de usar tanta
capacidad como necesite, escalar a medida que aumenta la carga y reducir horizontalmente cuando
no se necesita la capacidad adicional. Diseñe su aplicación para que pueda escalar horizontalmente,
mediante la incorporación o eliminación de nuevas instancias según se requiera.

Recomendaciones
Evitar la permanencia de la instancia. La permanencia o afinidad de sesión se produce cuando
las solicitudes de un mismo cliente siempre se enrutan al mismo servidor. La permanencia limita
la capacidad de escalado de la aplicación. Por ejemplo, el tráfico de un usuario de gran volumen
no se distribuirá entre instancias. Entre las causas de permanencia se incluyen el almacenamiento
del estado de una sesión in-memory y el uso de claves específicas de la máquina para el cifrado.
Asegúrese de que cualquier instancia pueda manejar cualquier solicitud.

Identificar cuellos de botella. Escalar no es la solución mágica para cada problema de rendimiento.
Por ejemplo, si el cuello de botella lo provoca su base de datos de back-end, no servirá de nada
agregar más servidores web. Antes de generar más instancias en el problema, identifique y resuelva
los cuellos de botella del sistema. Las partes con estado del sistema son la causa más probable de los
cuellos de botella.

Descomponer las cargas de trabajo según los requisitos de escalabilidad. Las aplicaciones a
menudo se componen de múltiples cargas de trabajo, con diferentes requisitos de escalado. Por
ejemplo, una aplicación puede tener un sitio público y un sitio de administración independiente.
El sitio público puede experimentar aumentos repentinos en el tráfico, mientras que el sitio de
administración tiene una carga más pequeña y predecible.

Alivianar la carga de tareas que consumen muchos recursos. Las tareas que requieren una gran
cantidad de recursos de CPU o E/S se deben mover a trabajos en segundo plano cuando sea posible,
para así minimizar la carga en el front-end que maneja las solicitudes del usuario.

Usar las características de escalado automático integradas. Muchos servicios de procesos de


Azure tienen soporte integrado para el escalado automático. Si la aplicación tiene una carga de
trabajo regular y predecible, planifique la escalabilidad horizontal. Por ejemplo, realícela durante el
horario hábil. De lo contrario, si la carga de trabajo no es predecible, use métricas de rendimiento
como la CPU o solicite la longitud de cola para activar el escalado automático. Para conocer los
procedimientos recomendados de escalado automático, consulte Escalado automático. Para conocer
los procedimientos recomendados de escalado automático, vaya a https://docs.microsoft.com/
en-us/azure/architecture/best-practices/auto-scaling.

69 CAPÍTULO 3d | Diseño para escalado horizontal


Considerar un escalado automático más dinámico para las cargas de trabajo críticas. Para
las cargas de trabajo críticas, le recomendamos adelantarse a la demanda. Es mejor agregar
rápidamente nuevas instancias a una carga fuerte para manejar el tráfico adicional, y luego reducirlas
gradualmente.

Diseñar para reducir horizontalmente. Recuerde que con la escala elástica, la aplicación tendrá
periodos de reducción horizontal, en los que se eliminarán las instancias. La aplicación debe manejar
fácilmente las instancias que se eliminan. Estas son algunas formas que le permitirán manejar la
reducción horizontal:

• Escuche los eventos de apagado (cuando estén disponibles) y realice un apagado limpio.
• Los clientes/consumidores de un servicio deben ayudar en el manejo de errores transitorios y
reintentar.
• Para tareas de ejecución prolongada, considere dividir el trabajo, usando puntos de control o el
patrón de canalizaciones y filtros.
• Coloque los elementos de trabajo en una cola para que otra instancia pueda reanudar el trabajo,
en caso que se elimine una instancia en el medio del procesamiento.

70 CAPÍTULO 3d | Diseño para escalado horizontal


3e

Partición alrededor
de límites
Use la creación de particiones para evitar que se alcancen los límites de
la base de datos, la red y el proceso.
Una de las ventajas principales de la nube es el escalado elástico: la posibilidad de usar tanta
capacidad como necesite, escalar a medida que aumenta la carga y reducir horizontalmente cuando
no se necesita la capacidad adicional. Diseñe su aplicación para que pueda escalar horizontalmente,
mediante la incorporación o eliminación de nuevas instancias según se requiera.

En la nube, todos los servicios tienen límites en su capacidad para escalar. Los límites del servicio de
Azure están documentados en la suscripción de Azure y en los límites, las cuotas y las restricciones
del servicio. Los límites incluyen la cantidad de núcleos, el tamaño de la base de datos, el
rendimiento de la consulta y el rendimiento de la red. Si su sistema crece hasta quedar de un tamaño
lo suficientemente grande, puede alcanzar uno o más de estos límites. Use la creación de particiones
para evitar estos límites.

Hay muchas formas de crear una partición de un sistema, tales como:

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

La partición de una base de datos puede ser horizontal, vertical o funcional.

• En la creación de particiones horizontales, también llamada fragmentación, cada partición


contiene datos para un subconjunto del conjunto de datos total. Las particiones comparten el
mismo esquema de datos. Por ejemplo, los clientes cuyos nombres comienzan con A-M van a
una partición, y los que comienzan con N-Z van a otra partición.
• En la creación de particiones verticales, cada partición contiene un subconjunto de los campos
para los elementos del almacén de datos. Por ejemplo, ponga los campos de acceso frecuente en
una partición, y los campos a los que accede con menos frecuencia en otra.
• En la creación de particiones funcionales, los datos se dividen de acuerdo con la forma en que
se usa cada contexto acotado en el sistema. Por ejemplo, almacene los datos de facturas en una
partición y los datos del inventario de productos en otra. Los esquemas son independientes.
Para obtener más información, vaya a https://docs.microsoft.com/en-us/azure/architecture/
best-practices/data-partitioning.

71 CAPÍTULO 3e | Partición alrededor de límites


Recomendaciones
Crear particiones de diferentes partes de la aplicación. Las bases de datos son un candidato obvio
para la creación de particiones, pero considere también el almacenamiento, la memoria caché, las
colas y las instancias de proceso.

Diseñar la clave de partición para evitar puntos conflictivos. Si crea una partición de una base
de datos, pero un fragmento sigue recibiendo la mayoría de las solicitudes, entonces no ha resuelto
su problema. Lo ideal es distribuir la carga de manera uniforme en todas las particiones. Por
ejemplo, genere un hash por ID de cliente y no por la primera letra del nombre del cliente, porque
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 conduzca a una distribución uniforme de los mensajes en
el conjunto de colas. Para obtener más información, consulte Fragmentación.

Crear una partición alrededor de la suscripción de Azure y los límites de servicio. Los
componentes y servicios individuales tienen límites, pero también existen límites para las
suscripciones y los grupos de recursos. Para las aplicaciones muy grandes, puede que deba crear una
partición alrededor de esos límites.

Crear una partición en diferentes niveles. Considere la implementación de un servidor de base de


datos en una VM. La VM tiene un VHD respaldado por Azure Storage. La cuenta de almacenamiento
pertenece a una suscripción de Azure. Tenga en cuenta que cada paso en la jerarquía tiene límites.
El servidor de base de datos puede tener un límite de grupo de conexiones. Las VM tienen límites
de CPU y red. El almacenamiento tiene límites de IOPS. La suscripción tiene límites en la cantidad de
núcleos de VM. En general, es más fácil crear particiones en los niveles inferiores de la jerarquía. Solo
las aplicaciones grandes deberían necesitar una partición en el nivel de suscripción.

72 CAPÍTULO 3e | Partición alrededor de límites


3f

Diseño para
operaciones
Diseñe una aplicación de modo tal que el equipo de operaciones tenga
las herramientas que necesita.

La nube ha cambiado drásticamente el rol del equipo de operaciones. Ya no son responsables de


administrar el hardware y la infraestructura que hospeda la aplicación. Dicho esto, las operaciones
siguen siendo una parte fundamental en la ejecución exitosa de una aplicación en la nube. Algunas
de las funciones importantes del equipo de operaciones incluyen:

• Implementación
• Supervisión
• Escalada
• Respuesta ante incidentes
• Auditoría de seguridad

Contar con un registro y seguimiento sólidos es particularmente importante en las aplicaciones en


la nube. Involucre al equipo de operaciones en el diseño y la planificación, para garantizar que la
aplicación les proporcione los datos y la información que necesitan para tener éxito.

Recomendaciones
Hacer que todas las cosas sean observables. Una vez que una solución se implementa y ejecuta,
los registros y seguimientos son su principal información del sistema. El seguimiento registra una
ruta en el sistema y es útil para detectar 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. Inicie sesión en producción, de lo contrario perderá la información en los momentos en
que más la necesita.

Instrumentar la supervisión. La supervisión da una idea de qué tan bien (o mal) está funcionando
una aplicación, en términos de disponibilidad, rendimiento y estado del sistema. Por ejemplo, la
supervisión le indica si se está cumpliendo el SLA. La supervisión se realiza durante el funcionamiento
normal del sistema. Debe acercarse tanto como sea posible al tiempo real, para que el personal de
operaciones pueda reaccionar rápidamente en caso de problemas. Idealmente, la supervisión puede
ayudar a evitar problemas antes de que se produzca un error crítico. Para obtener más información,
vaya a https://docs.microsoft.com/en-us/azure/architecture/best-practices/monitoring.

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


Instrumentar el análisis de la causa raíz. El análisis de la causa raíz es el proceso de hallazgo de la
causa subyacente de los errores. Se realiza después de que se produce un error.

Usar el seguimiento distribuido. Use un sistema de seguimiento distribuido diseñado para


simultaneidad, asincronía y escala de la nube. Los seguimientos deben incluir una ID de correlación
que circule más allá de los límites del servicio. Una sola operación puede implicar llamadas a
múltiples servicios de aplicación. Si se producen errores en una operación, la ID de correlación ayuda
a identificar la causa del error.

Estandarizar registros y métricas. El equipo de operaciones deberá agregar registros de los


diversos servicios en su solución. Si cada servicio usa 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 la
ID de correlación, el nombre del evento, la dirección IP del remitente, etc. Los servicios individuales
pueden derivar en esquemas personalizados que heredan el esquema base y contienen campos
adicionales.

Automatizar las tareas de administración. Incluya el aprovisionamiento, la implementación y la


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

Tratar la configuración como un código. Verifique los archivos de configuración en un sistema de


control de versiones, para que pueda llevar un registro y actualizar los cambios, y revertirlos si es
necesario.

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


3g

Uso de servicios
administrados
Cuando sea posible, use la plataforma como servicio (PaaS) en lugar de
la infraestructura como servicio (IaaS).

IaaS es como tener una caja con piezas. Puede crear cualquier cosa, pero tiene que construirla usted.
Los servicios administrados son más fáciles de configurar y administrar. No necesita aprovisionar VM,
configurar VNets, administrar parches y actualizaciones, y toda la demás sobrecarga asociada con la
ejecución de software en una VM.

Por ejemplo, supongamos que su aplicación necesita una cola de mensajes. Podría configurar
su propio servicio de mensajería en una VM, usando algo como RabbitMQ. Sin embargo, Azure
Service Bus ya ofrece mensajería confiable como servicio, y es más fácil de configurar. Simplemente
cree un espacio de nombres de Service Bus (lo que se puede hacer como parte de un script de
implementación) y luego llame a Service Bus con el SDK de cliente.

Por supuesto, su aplicación puede tener requisitos específicos que hacen que un enfoque de IaaS
sea más adecuado. Sin embargo, incluso si su aplicación se basa en IaaS, busque lugares donde sea
natural incorporar servicios administrados. Estos incluyen memorias caché, colas y almacenamiento
de datos.

En lugar de ejecutar... Considere usar...


• Active Directory • Azure Active Directory Domain Services
• Elasticsearch • Azure Search
• 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 sus datos y
al uso que le dará.

IaaS es como tener una caja con piezas. Puede crear cualquier cosa, pero tiene que construirla usted.
Los servicios administrados son más fáciles de configurar y administrar. No necesita aprovisionar VM,
configurar VNets, administrar parches y actualizaciones, y toda la demás sobrecarga asociada con la
ejecución de software en una VM.
Por ejemplo, supongamos que su aplicación necesita una cola de mensajes. Podría configurar su propio
servicio de mensajería en una VM, usando algo como RabbitMQ. Sin embargo, Azure Service Bus ya
ofrece mensajería confiable como servicio, y es más fácil de configurar. Simplemente cree un espacio
de nombres de Service Bus (lo que se puede hacer como parte de un script de implementación) y
luego llame a Service Bus con el SDK de cliente.
Por supuesto, su aplicación puede tener requisitos específicos que hacen que un enfoque de IaaS sea
más adecuado. Sin embargo, incluso si su aplicación se basa en IaaS, busque lugares donde sea natural
incorporar servicios administrados. Estos incluyen memorias caché, colas y almacenamiento de datos.

Atrás quedaron los días en los que simplemente colocaría todos sus datos en una gran base de
datos SQL relacional. Las bases de datos relacionales son muy buenas en lo que hacen: proporcionar
garantías de ACID para transacciones sobre datos relacionales. Sin embargo, tienen algunos costos:

• Las consultas pueden requerir costosas combinaciones.


• Los datos se deben normalizar y cumplir con un esquema predefinido (esquema en escritura).
• La contención de bloqueo puede afectar el rendimiento.
En cualquier solución grande, es probable que una sola tecnología de almacenamiento de datos no
satisfaga todas sus necesidades. Las alternativas a las bases de datos relacionales incluyen almacenes
de claves/valores, bases de datos de documentos, bases de datos de motores de búsqueda, bases de
datos de series temporales, bases de datos de familias de columnas y bases de datos de gráficos. Cada
una tiene pros y contras, y diferentes tipos de datos se ajustan más naturalmente a una u otra.
Por ejemplo, puede almacenar un catálogo de productos en una base de datos de documentos, como
Cosmos DB, que permite un esquema flexible. En ese caso, la descripción de cada producto es un
documento autónomo. Para realizar consultas sobre todo el catálogo, puede indexar el catálogo y

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


almacenar el índice en Azure Search. El inventario de productos puede ir en una base de datos SQL,
porque esa información requiere garantías de ACID.

Recuerde que los datos incluyen más que solo los datos conservados de una aplicación. También
incluye registros de aplicaciones, eventos, mensajes y memorias caché.

Recomendaciones
No usar una base de datos relacional para todo. Considere otros almacenes de datos cuando
corresponda. Consulte Elegir el almacén de datos adecuado.

Aceptar la persistencia políglota. En cualquier solución grande, es probable que una sola
tecnología de almacenamiento de datos no satisfaga todas sus necesidades.

Considerar el tipo de datos. Por ejemplo, coloque datos transaccionales en SQL, coloque
documentos JSON en una base de datos de documentos, coloque datos de telemetría en una base
de datos de series temporales, coloque registros de aplicaciones en Elasticsearch y coloque blobs en
Azure Blob Storage.

Preferir la disponibilidad por sobre la coherencia (fuerte). El teorema CAP implica que un sistema
distribuido debe hacer intercambios entre disponibilidad y coherencia. (Las particiones de red, la otra
rama del teorema CAP, nunca pueden evitarse por completo). A menudo, puede lograr una mayor
disponibilidad mediante la adopción de un modelo de coherencia final.

Considerar el conjunto de habilidades del equipo de desarrollo. El uso de la persistencia políglota


tiene sus ventajas, pero existe la posibilidad de que se exceda. La adopción de una nueva tecnología
de almacenamiento de datos requiere un nuevo conjunto de habilidades. El equipo de desarrollo
debe entender cómo sacar el máximo provecho de la tecnología. Deben entender los patrones de
uso apropiados, cómo optimizar las consultas, ajustar el rendimiento, etc. Tenga esto en cuenta al
considerar tecnologías de almacenamiento.

Usar transacciones compensatorias. Un efecto secundario de la persistencia políglota es que


una sola transacción puede escribir datos en múltiples almacenes. Si se produce un error, use las
transacciones compensatorias para deshacer los pasos ya completados.

Observar los contextos acotados. Contexto acotado es un término de diseño guiado por el
dominio. Un contexto acotado es un límite explícito en torno a un modelo de dominio y define a qué
partes del dominio se aplica el modelo. Idealmente, un contexto acotado se asigna a un subdominio
del dominio del negocio. Los contextos acotados de su sistema son un lugar natural para considerar
la persistencia políglota. Por ejemplo, los "productos" pueden aparecer tanto en el subdominio
Catálogo de productos como en el subdominio Inventario de productos, pero es muy probable que
estos dos subdominios tengan requisitos diferentes de almacenamiento, actualización y consulta de
productos.

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


3i

Diseño para evolución


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

Todas las aplicaciones exitosas cambian con el tiempo, ya sea para corregir errores, agregar
nuevas características, incorporar nuevas tecnologías o hacer que los sistemas existentes sean más
escalables y resistentes. Si todas las partes de una aplicación se acoplan estrechamente, se hace muy
difícil introducir cambios en el sistema. Un cambio en una parte de la aplicación puede estropear
otra parte o provocar cambios que tendrán un efecto dominó en toda la base de código.
Este problema no se limita a las aplicaciones monolíticas. Una aplicación se puede descomponer en
servicios, pero aún así exhibir el tipo de acoplamiento estrecho que deja el sistema rígido y frágil.
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, ya que
abordan muchas de las consideraciones enumeradas aquí.

Recomendaciones
Aplicar la alta cohesión y la conexión flexible. Un servicio es cohesivo si proporciona una
funcionalidad que lógicamente le pertenece. Los servicios posee una conexión flexible si puede
cambiar un servicio sin cambiar el otro. La alta cohesión generalmente significa que los cambios en
una función requerirán cambios en otras funciones relacionadas. Si considera que la actualización
de un servicio requiere actualizaciones coordinadas de otros servicios, puede ser una señal de que
sus servicios no son cohesivos. Uno de los objetivos del diseño guiado por el dominio (DDD) es
identificar esos límites.
Encapsular el conocimiento del dominio. Cuando un cliente consume un servicio, la
responsabilidad de aplicación de las reglas comerciales del dominio no debe recaer en el cliente. En
cambio, el servicio debe encapsular todo el conocimiento del dominio que es de su responsabilidad.
De lo contrario, cada cliente tendrá que aplicar las reglas de negocios, y usted terminará con el
conocimiento del dominio distribuido en diferentes partes de la aplicación.
Usar mensajes asincrónicos. La mensajería asincrónica es una forma de desacoplar al productor
del mensaje del consumidor. El productor no depende de que el consumidor responda al mensaje ni
realice ninguna acción en particular. Con una arquitectura de pub/sub, el productor puede no saber
quién está consumiendo el mensaje. Los nuevos servicios pueden consumir fácilmente los mensajes
sin modificaciones al productor.
No generar conocimiento del dominio en un gateway Los gateways pueden ser útiles en una
arquitectura de microservicios, para cosas como el enrutamiento de solicitudes, la traducción de
protocolos, el equilibrio de carga o la autenticación. Sin embargo, el gateway debe restringirse a este
tipo de funcionalidad de la infraestructura. No debe implementar ningún conocimiento del dominio
para evitar que se convierta en una característica de gran dependencia.
Exponer las interfaces abiertas. Evite crear capas de traducción personalizadas entre servicios. En
cambio, un servicio debe exponer una API con un contrato de API bien definido. La API debe tener

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


actualizaciones, para que así pueda evolucionar y a la vez mantener la compatibilidad con versiones
anteriores. De esta forma, puede actualizar un servicio sin coordinar las actualizaciones de todos los
servicios ascendentes que dependen de él. Los servicios públicos deben exponer una API de RESTful
en HTTP. Los servicios de back-end pueden usar un protocolo de mensajería de estilo RPC por
motivos de rendimiento.

Diseñar y realizar pruebas con contratos de servicios. Cuando los servicios exponen API bien
definidas, puede desarrollar y probar esas API. De esta forma, puede desarrollar y probar un servicio
individual sin tener que implementar todos sus servicios dependientes. (Por supuesto, aún realizaría
pruebas de integración y carga con los servicios reales).

Abstraer la infraestructura de la lógica de dominio. No permita que la lógica de dominio se


mezcle con la funcionalidad relacionada con la infraestructura, como la mensajería o la persistencia.
De lo contrario, los cambios en la lógica de dominio requerirán actualizaciones en las capas de
infraestructura y viceversa.

Alivianar la carga de las inquietudes transversales a un servicio independiente. Por ejemplo, si


varios servicios necesitan autenticar solicitudes, puede mover esta funcionalidad a su propio servicio.
Entonces podría evolucionar el servicio de autenticación, por ejemplo, al agregar un nuevo flujo de
autenticación, sin tocar ninguno de los servicios que lo usan.

Implementar servicios de forma independiente. Cuando el equipo de DevOps puede implementar


un solo servicio independientemente de otros servicios en la aplicación, las actualizaciones pueden
realizarse de manera más rápida y segura. Las correcciones de errores y las nuevas características se
pueden implementar con una cadencia más regular. Diseñe tanto la aplicación como el proceso de
lanzamiento para que admitan actualizaciones independientes.

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


3j

Creación para las


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

Este principio de diseño puede parecer obvio, pero es importante tenerlo en cuenta al diseñar una
solución. ¿Se adelanta a millones de usuarios o a unos miles? ¿Es aceptable una interrupción de la
aplicación de una hora? ¿Espera grandes alzas de tráfico o una carga de trabajo muy predecible?
Finalmente, cada decisión de diseño debe estar justificada por un requisito de negocios.

Recomendaciones
Definir objetivos empresariales. Incluya el objetivo de tiempo de recuperación (RTO), el objetivo
de punto de recuperación (RPO) y la interrupción máxima tolerable (MTO). Estas cifras deberían
informar las decisiones con respecto a la arquitectura. Por ejemplo, para lograr un RTO bajo, puede
implementar una conmutación por error automática a una región secundaria. Pero si su solución
puede tolerar un RTO más alto, ese grado de redundancia podría ser innecesario.

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

Modelar la aplicación en torno al dominio del negocio. Comience por analizar los requisitos
empresariales. Use estos requisitos para modelar la aplicación. Considere usar un enfoque de diseño
guiado por el dominio (DDD) para crear modelos de dominio que reflejen los procesos de negocio y
los casos de uso.

Capturar los requisitos funcionales y 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 entender los requisitos de escalabilidad, disponibilidad y
latencia. Estos requisitos influirán en las decisiones de diseño y la elección de la tecnología.

Descomponer por carga de trabajo. En este contexto, el término "carga de trabajo" significa una
capacidad discreta o tarea informática que puede separarse lógicamente de otras tareas. Diferentes

80 CAPÍTULO 3j | Creación para las necesidades del negocio


cargas de trabajo pueden tener diferentes requisitos de disponibilidad, escalabilidad, coherencia de
datos y recuperación ante desastres.

Planear el crecimiento. Una solución puede satisfacer sus necesidades actuales, en términos de
cantidad de usuarios, volumen de transacciones, almacenamiento de datos, etc. Sin embargo, una
aplicación sólida puede manejar el crecimiento sin realizar grandes cambios en su arquitectura.
Consulte Diseño para escalado horizontal y Partición alrededor de límites. También considere que
su modelo de negocio y sus requisitos empresariales probablemente cambiarán con el tiempo. Si el
modelo de servicio y los modelos de datos de una aplicación son demasiado rígidos, resulta difícil
evolucionar la aplicación para nuevos casos de uso y escenarios. Consulte Diseño para evolución.

Administrar los costos. En una aplicación local tradicional, usted paga el hardware por adelantado
(CAPEX). En una aplicación en la nube, paga por los recursos que consume. Asegúrese de entender
el modelo de precios de los servicios que consume. El costo total incluirá el uso del ancho de banda
de la red, el almacenamiento, las direcciones IP, el consumo del servicio y otros factores. Consulte los
precios de Azure para obtener más información. También considere sus costos de operación. En la
nube, no tiene que administrar hardware u otra infraestructura, sin embargo aún necesita administrar
sus aplicaciones, incluyendo DevOps, respuesta ante incidentes, recuperación ante desastres, etc.

81 CAPÍTULO 3j | Creación para las necesidades del negocio


3k

Diseño de
aplicaciones
resistentes para Azure
En lugar de comprar hardware de gama alta para escalar hacia arriba, en un entorno de nube debe
escalar horizontalmente. Los costos de los entornos de nube se mantienen bajos y el objetivo es
minimizar el efecto de un error.
En un sistema distribuido, los errores ocurrirán. El hardware puede fallar. La red puede presentar
errores transitorios. En casos aislados, un servicio o una región completa puede experimentar una
interrupción, incluso en esos casos debe existir un plan.
Desarrollar una aplicación confiable en la nube no es igual que desarrollar una aplicación confiable
en un entorno empresarial. Aunque históricamente pueda haber comprado hardware de gama alta
para escalar hacia arriba, en un entorno de nube debe escalar horizontalmente, no hacia arriba. Los
costos de los entornos de nube se mantienen bajos mediante 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
enfoque cambia a "tiempo medio para restaurar". El objetivo es minimizar el efecto de un error.
Este artículo proporciona información general acerca de cómo crear aplicaciones resistentes en
Microsoft Azure. Comienza con una definición del término resistencia y los conceptos relacionados.
A continuación, describe un proceso para lograr la resistencia, utilizando un enfoque estructurado
a lo largo del ciclo de vida de una aplicación, desde el diseño y la implementación hasta la
implementación y las operaciones.

¿Qué es la resistencia?
Resistencia es la capacidad de un sistema para recuperarse de errores y seguir funcionando. No se
trata de evitar que se produzcan errores, sino de responder a los errores de una manera que evite el
tiempo de inactividad o la pérdida de datos. El objetivo de la resistencia es devolver la aplicación a
un estado completamente funcional después de un error.

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

• Alta disponibilidad (HA) es la capacidad de la aplicación para continuar ejecutándose en buen


estado, sin un tiempo de inactividad significativo. Con "buen estado", nos referimos a 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 resistentes para Azure


• Recuperación ante desastres (DR) es la capacidad de recuperarse de incidentes excepcionales
pero graves: errores no transitorios de gran escala, como una interrupción del servicio que afecta
a toda una región. La recuperación ante desastres incluye la copia de seguridad y el archivo de
datos, y puede incluir una intervención manual, como la restauración de una base de datos a partir
de una copia de seguridad.
Una forma de pensar en HA versus DR es que DR se inicia cuando el impacto de un error excede la
capacidad del diseño de HA para manejarlo. Por ejemplo, poner varias VM detrás de un equilibrador de
carga proporcionará disponibilidad si una VM presenta errores, pero no si todas presentan errores al
mismo tiempo.
Cuando diseña una aplicación para ser resistente, debe comprender sus requisitos de disponibilidad.
¿Cuánto tiempo de inactividad es aceptable? Esto es en parte una función de costo. ¿Qué costo tendrá
para su negocio el posible tiempo de inactividad? ¿Cuánto debería invertir para que la aplicación esté
altamente disponible? También debe definir lo que significa que la aplicación esté disponible. Por
ejemplo, ¿la aplicación está "inactiva" si un cliente puede enviar una orden pero el sistema no puede
procesarla dentro de un plazo normal? También considere la probabilidad de que se produzca un tipo
de interrupción específico y si es rentable contar con una estrategia de mitigación.
Otro término común es continuidad del negocio (BC), que es la capacidad de ejecutar las funciones
empresariales esenciales durante y después de condiciones adversas, como un desastre natural o una
caída del servicio. BC abarca todo el funcionamiento del negocio, incluyendo las instalaciones físicas, el
personal, las comunicaciones, el transporte y la TI. Este artículo se centra en las aplicaciones en la nube,
pero la planificación de resistencia debe realizarse en el contexto de los requisitos generales de BC. Para
obtener más información, consulte la [Guía de planificación de contingencia] [capacity-planning-guide]
del Instituto Nacional de Ciencia y Tecnología (NIST, National Institute of Science and Technology).

Proceso para lograr resistencia


La resistencia no es un complemento. Debe diseñarse en el sistema y estar operativa. El siguiente es
un modelo general a seguir:

1. Defina sus requisitos de disponibilidad, basándose en las necesidades empresariales.


2. Diseñe la aplicación de resistencia. Comience con una arquitectura que siga prácticas comprobadas y,
a continuación, identifique los posibles puntos de error en esa arquitectura.
3. Implemente estrategias para detectar y recuperarse de errores.
4. Pruebe la implementación mediante la simulación de errores y la activación de conmutaciones por
error.
5. Implemente la aplicación en la producción mediante un proceso confiable y repetible.
6. Supervise la aplicación para detectar errores. Al supervisar el sistema, puede medir el estado de la
aplicación y responder a incidentes, si es necesario.
7. Responda si hay incidentes que requieren intervenciones manuales.

En el resto de este artículo, analizaremos 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. Los siguientes son
algunos enfoques que permiten pensar en la resistencia en esos términos.

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


Descomponer por carga de trabajo
Muchas soluciones en la nube constan de múltiples cargas de trabajo de aplicaciones. En este contexto,
el término "carga de trabajo" significa una capacidad discreta o tarea informática que puede separarse
lógicamente de otras tareas, en términos de lógica de negocios y requisitos de almacenamiento de datos.
Por ejemplo, una aplicación de comercio electrónico podría incluir las siguientes cargas de trabajo:
• Examinar y buscar un catálogo de productos.
• Crear y realizar un seguimiento de las órdenes.
• Ver recomendaciones.
Estas cargas de trabajo pueden tener diferentes requisitos de disponibilidad, escalabilidad,
coherencia de datos, recuperación ante desastres, etc. Nuevamente, estas son decisiones
comerciales.
También considere los patrones de uso. ¿Hay ciertos periodos críticos cuando el sistema debe estar
disponible? Por ejemplo, un servicio de declaración de impuestos no se puede caerse justo antes de
la fecha límite de presentación, un servicio de transmisión de video debe permanecer activo durante
un gran evento deportivo, y así sucesivamente. Durante los periodos críticos, es posible que tenga
implementaciones redundantes en varias regiones, por lo que la aplicación podría presentar errores
si una región lo hace. Sin embargo, una implementación de múltiples regiones es más costosa, por lo
que en tiempos menos críticos, puede ejecutar la aplicación en una sola región.

RTO y RPO
Dos métricas importantes a considerar son el objetivo del tiempo de recuperación y el objetivo del
punto de recuperación.
• Objetivo de tiempo de recuperación (RTO) es el tiempo máximo aceptable para que una
aplicación no esté disponible después de un incidente. Si su RTO es de 90 minutos, debe poder
restaurar la aplicación a un estado en ejecución dentro de 90 minutos desde el inicio de un
desastre. Si tiene un RTO muy bajo, puede mantener una segunda implementación en ejecución
continua en modo de espera, para protegerse contra una interrupción regional.
• Objetivo de punto de recuperación (RPO) es la duración máxima de pérdida de datos que es
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 por hora, puede perder hasta
una hora de datos.
RTO y RPO son requisitos de negocio. Realizar una evaluación de riesgos puede ayudarlo a definir el
RTO y el RPO de la aplicación. Otra métrica común es tiempo medio de recuperación (MTTR), que
es el tiempo promedio que demora restaurar la aplicación después de un error. MTTR es un hecho
empírico sobre un sistema. Si el MTTR supera el RTO, un error en el sistema causará una interrupción
comercial inaceptable, ya que no será posible restaurar el sistema dentro del RTO definido.

SLA
En Azure, el Acuerdo de nivel de servicio (SLA) describe los compromisos de Microsoft con el tiempo
de actividad y la conectividad. Si el SLA para un servicio en particular es de 99,9 %, significa que debe
esperar que el servicio esté disponible el 99,9 % del tiempo.

Notas:
El SLA de Azure SLA también incluye disposiciones para obtener un crédito de servicio si no se cumple el
SLA, junto con definiciones específicas de "disponibilidad"para cada servicio. Ese aspecto del SLA actúa
como una directiva de aplicación.

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


Tiempo de inactividad por
SLAD Tiempo de inactividad por mes Tiempo de inactividad por año
semana

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

99,9 % 10,1 minutos 43,2 minutos8 0,76 horas

99,95 % 5 minutos 21,6 minutos4 0,38 horas

99,99 % 1,01 minutos4 0,32 minutos 52,56 minutos

99,999 % 6 segundos 25,9 segundos5 0,26 minutos

Por supuesto, una mayor disponibilidad es mejor, todo lo demás es igual. Pero a medida que lucha
por tener más 9, el costo y la complejidad para alcanzar ese nivel de disponibilidad aumentan. Un
tiempo de actividad del 99,99 % se traduce en aproximadamente 5 minutos de tiempo de inactividad
total por mes. ¿Vale la pena la complejidad y el costo adicionales para llegar a los cinco 9? La
respuesta depende de los requisitos empresariales.
Estos son algunos otros aspectos que debe considerar al definir un SLA:

• Para lograr cuatro 9 (99,99 %), probablemente no pueda confiar en la intervención manual para
recuperarse de errores. La aplicación debe diagnosticarse y repararse automáticamente.
• Más allá de cuatro 9, es difícil detectar interrupciones lo suficientemente rápido como para
cumplir con el SLA.
• Piense en la ventana de tiempo con la que se mide su SLA. Cuanto más pequeña es la ventana,
más ajustadas son las tolerancias. Probablemente no tenga sentido definir su SLA en términos de
tiempo de actividad por hora o por día.

SLA compuestos
Considere una aplicación web de App Service que escribe en Azure SQL Database. En el momento de
escribir esto, estos servicios de Azure tienen los siguientes SLA:
• Aplicaciones web de App Service = 99,95 %
• SQL Database = 99,99 %

¿Cuál es el tiempo de inactividad máximo que esperaría para esta aplicación? Si cualquiera
de los servicios presenta errores, toda la aplicación presenta errores. En general, la probabilidad
de que cada servicio falle es independiente, por lo que el SLA compuesto para esta aplicación
es 99,95 %× 99,99 % = 99,94 %. Eso es más bajo que los SLA individuales, lo que no es sorprendente,
porque una aplicación que depende de múltiples servicios tiene más puntos de error potenciales.
Por otro lado, puede mejorar el SLA compuesto creando rutas de conmutación por recuperación
independientes. Por ejemplo, si SQL Database no está disponible, coloque las transacciones en una
cola, para procesarlas más tarde.

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


Con este diseño, la aplicación aún está disponible, incluso si no se puede conectarse a la base
de datos. Sin embargo, falla si la base de datos y la cola presentan errores al mismo tiempo. El
porcentaje de tiempo esperado para un error simultáneo es 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 tiene ventajas y desventajas. La lógica de la aplicación es más compleja,
está pagando por la cola y puede haber problemas de coherencia de los datos a considerar.

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


en más de una región y usar el Administrador de tráfico de Azure para conmutar por error si
la aplicación presenta errores en una región. Para una implementación en dos regiones, el SLA
compuesto se calcula de la siguiente manera.
Donde N es el SLA compuesto para la aplicación implementada en una región. La probabilidad
esperada de que la aplicación falle en ambas regiones al mismo tiempo es (1 - N) × (1 - N). Por lo
tanto,
• SLA combinado para ambas regiones = 1 - (1 - N)(1 - N) = N + (1 - N)N

Finalmente, debe tener en cuenta el SLA para el Administrador de tráfico. En el momento de escribir
esto, el SLA para el Administrador de tráfico 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 un tiempo de inactividad


durante una conmutación por error. Consulte Supervisión y conmutación por error tras un error del
Administrador de tráfico.

El número de SLA calculado es una referencia útil, pero no cuenta toda la historia sobre la
disponibilidad. A menudo, una aplicación puede degradarse fácilmente cuando una ruta no crítica
presenta errores. Piense en una aplicación que muestra un catálogo de libros. Si la aplicación no
puede recuperar la imagen en miniatura de la portada, es posible que muestre una imagen de
marcador de posición. En ese caso, no obtener la imagen no reduce el tiempo de actividad de la
aplicación, aunque afecta la experiencia del usuario.

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


Diseño para resistencia
Durante la fase de diseño, debe realizar un análisis de modo de error (FMA). El objetivo de un FMA es
identificar posibles puntos de error y definir cómo la aplicación responderá a esos errores.
• ¿Cómo detectará la aplicación este tipo de error?
• ¿Cómo responderá la aplicación a este tipo de error?
• ¿Cómo registrará y supervisará este tipo de error?
Para obtener más información acerca del proceso de 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 de modo de error (FMA). El objetivo de un FMA es
identificar posibles puntos de error y definir cómo la aplicación responderá a esos errores.
• ¿Cómo detectará la aplicación este tipo de error?
• ¿Cómo responderá la aplicación a este tipo de error?
• ¿Cómo registrará y supervisará este tipo de error?
Para obtener más información acerca del proceso de 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á HTTP 5xx


disponible

Limitación HTTP 429 (demasiadas


solicitudes)

Autenticación HTTP 401 (no autorizado)

Respuesta lenta El tiempo de espera de una


solicitud se agota

Estrategias de resistencia
Esta sección ofrece un estudio de algunas estrategias de resistencia comunes. La mayoría de ellas
no se limitan a una tecnología específica. Las descripciones en esta sección un resumen de la idea
general detrás de cada técnica, con enlaces para lectura adicional.

Errores temporales de reintento


Los errores temporales pueden ser causados por la pérdida momentánea de conectividad de red, una
conexión de base de datos perdida o un tiempo de espera agotado cuando un servicio está ocupado. A
menudo, un error transitorio puede resolverse simplemente al reintentar la solicitud. Para muchos servicios
de Azure, el SDK del cliente implementa reintentos automáticos, de una manera transparente para la
persona que llama; consulte la guía de reintentos para servicios específicos.
Cada intento de reintento se suma a la latencia total. Además, demasiadas solicitudes fallidas pueden causar
un cuello de botella, ya que las solicitudes pendientes se acumulan en la cola. Estas solicitudes bloqueadas
pueden contener recursos críticos del sistema como memoria, subprocesos, conexiones de bases de
datos, etc., que pueden causar errores en cascada. Para evitar esto, aumente el lapso entre cada intento de
reintento y limite el número total de solicitudes fallidas.

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


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

Equilibrio de carga entre instancias

Para la escalabilidad, una aplicación en la nube debería poder escalar al agregar más instancias. Este
enfoque también mejora la resistencia, ya que las instancias en mal estado se pueden eliminar de la
rotación.
Por ejemplo:
• Coloque dos o más VM detrás de un equilibrador de carga. El equilibrador de carga distribuye
el tráfico a todas las VM. Consulte Ejecutar VM con equilibrio de carga para la escalabilidad y la
disponibilidad.
• Escale una aplicación Azure App Service a múltiples instancias. App Service equilibra
automáticamente la carga entre las instancias. Consulte Aplicación web básica.

• Use el Administrador de tráfico de Azure para distribuir el tráfico en un conjunto de extremos.

Replicar los datos


La replicación de datos es una estrategia general para manejar errores no transitorios en un almacén
de datos. Muchas tecnologías de almacenamiento proporcionan replicación integrada, incluyendo
Azure SQL Database, Cosmos DB y Apache Cassandra.
Es importante considerar las rutas de lectura y escritura. Según la tecnología de almacenamiento, es
posible que tenga varias réplicas de escritura, o una sola réplica de escritura y varias réplicas de solo
lectura.
Para maximizar la disponibilidad, las réplicas se pueden colocar en varias regiones. Sin embargo,
esto aumenta la latencia al replicar los datos. Normalmente, la replicación entre regiones se realiza
de forma asíncrona, lo que implica un modelo de coherencia final y la posible pérdida de datos si se
producen errores en una réplica.

Degradar fácilmente

Si un servicio presenta errores y no existe una ruta de conmutación por error, la aplicación puede
degradarse fácilmente mientras se proporciona una experiencia de usuario aceptable.
Por ejemplo:

• Colocar un elemento de trabajo en una cola, para ser manejado más tarde.
• Devolver un valor estimado.
• Usar datos almacenados en memorias caché locales.
• Mostrar al usuario un mensaje de error. (Esta opción es mejor que hacer que la aplicación deje
de responder a las solicitudes).
88 CAPÍTULO 3k | Diseño de aplicaciones resistentes para Azure
Limitar a los usuarios con altos volúmenes
En ocasiones, una pequeña cantidad de usuarios crea una carga excesiva. Eso puede tener un
impacto en otros usuarios, reduciendo la disponibilidad general de su aplicación.

Cuando un único cliente realiza un número excesivo de solicitudes, la aplicación puede limitar al
cliente por un periodo determinado. Durante el período de regulación, la aplicación rechaza algunas
o todas las solicitudes de ese cliente (dependiendo de la estrategia de limitación exacta). El umbral
de limitación puede depender del nivel de servicio del cliente.

La limitación no implica que el cliente necesariamente actuara de forma maliciosa, sino solo que
excedió su cuota de servicio. En algunos casos, un consumidor puede exceder en forma constante su
cuota o de lo contrario tener un mal comportamiento. En ese caso, podría ir más allá y bloquear al
usuario. Normalmente, esto se hace bloqueando una clave de API o un rango de direcciones IP.

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

Usar un interruptor

El patrón de interruptor puede evitar que una aplicación intente repetidamente una operación que
probablemente falle. Esto es similar a un interruptor físico, uno que interrumpe el flujo de corriente
cuando un circuito está sobrecargado.

El interruptor aborda las llamadas a un servicio. Tiene tres estados:

• Cerrado. Este es el estado normal. El interruptor envía solicitudes al servicio, y un contador


rastrea la cantidad de errores recientes. Si el recuento de errores excede los límites en un período
determinado, el interruptor cambia al estado Abierto.
• Abierto. En este estado, el interruptor rechaza de inmediato todas las solicitudes, 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 cambia a Abierto, se inicia un
temporizador. Cuando el temporizador caduca, el interruptor cambia al estado Semiabierto.
• Semiabierto. En este estado, el interruptor deja pasar un número limitado de solicitudes al
servicio. Si se realizan correctamente, se supone que el servicio se recuperará y el interruptor
vuelve al estado Cerrado. De lo contrario, vuelve al estado Abierto. El estado Semiabierto impide
que un servicio de recuperación se inunde repentinamente con solicitudes.

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

Usar la nivelación de carga para suavizar los picos en el tráfico


Las aplicaciones pueden experimentar picos repentinos en el tráfico que pueden saturar los
servicios en el back-end. Si un servicio de back-end no puede responder a las solicitudes de forma
suficientemente rápida, las solicitudes se podrían colocar en la cola (copia de seguridad), o el servicio
podría limitar la aplicación.
Para evitar esto, puede usar una cola como búfer. Cuando hay un nuevo elemento de trabajo, en
lugar de llamar inmediatamente al servicio de back-end, la aplicación pone en cola el elemento de
trabajo para que se ejecute de forma asincrónica. La cola actúa como un búfer que suaviza los picos
en la carga.
Para obtener más información, consulte Patrón de nivelación de carga basado en cola.

89 CAPÍTULO 3k | Diseño de aplicaciones resistentes para Azure


Aislar recursos críticos
Los errores en un subsistema a veces pueden tener un efecto de cascada y provocar errores en
otras partes de la aplicación. Esto puede ocurrir si un error provoca que algunos recursos, como
subprocesos o sockets, no se liberen de manera oportuna, lo que lleva al agotamiento de los
recursos.
Para evitar esto, puede crear particiones de un sistema en grupos aislados, de modo que un error en
una partición no deje inactivo todo el sistema. Esta técnica a veces se denomina patrón de cierre.
Ejemplos:
• Cree una partición de una base de datos (por ejemplo, por inquilino) y asigne un grupo de
instancias de servidor web independiente para cada partición.
• Use grupos de subprocesos independientes para aislar las llamadas a diferentes servicios. Esto
ayuda a prevenir los errores en cascada si uno de los servicios presenta errores. Para ver un
ejemplo, consulte la biblioteca Hystrix de Netflix.
• Use contenedores para limitar los recursos disponibles para un subsistema en particular.

Aplicar transacciones compensatorias

Una transacción compensatoria es una transacción que deshace los efectos de otra transacción
completa.

En un sistema distribuido, puede ser muy difícil lograr una coherencia transaccional sólida. Las
transacciones compensatorias son una forma de lograr coherencia mediante el uso de una serie de
transacciones individuales más pequeñas que se pueden deshacer en cada paso.

Por ejemplo, para reservar un viaje, un cliente puede reservar un automóvil, una habitación de hotel
y un vuelo. Si alguno de estos pasos presenta errores, la operación completa presenta errores. En
lugar de intentar usar una única transacción distribuida para toda la operación, puede definir una
transacción compensatoria para cada paso. Por ejemplo, para deshacer una reserva de automóvil,
usted cancela la reserva. Para completar toda la operación, un coordinador ejecuta cada paso. Si
se producen errores en algún paso, el coordinador aplica las transacciones compensatorias para
deshacer los pasos que se completaron.

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

90 CAPÍTULO 3k | Diseño de aplicaciones resistentes para Azure


Prueba de resistencia

En general, no puede probar la resistencia de la misma forma que prueba la funcionalidad de la


aplicación (ejecutando pruebas unitarias, etc.). En cambio, debe probar cómo funciona la carga de
trabajo de extremo a extremo bajo condiciones de error que solo ocurren intermitentemente.
La prueba es un proceso iterativo. Pruebe la aplicación, mida el resultado, analice y trate cualquier
error que se produzca, y repita el proceso.
Prueba de inserción de errores. Pruebe la resistencia del sistema durante errores, ya sea mediante la
activación de errores reales o de simulaciones. Estos son algunos escenarios comunes de error que
debe probar:

• Apagar instancias de VM.


• Fallos en los procesos.
• Caducar certificados.
• Cambiar las claves de acceso.
• Apagar el servicio de DNS en los controladores de dominio.
• Limitar los recursos disponibles del sistema, como la RAM o el número de subprocesos.
• Desmontar discos.
• Volver a implementar una VM.
Mida los tiempos de recuperación y verifique que se cumplan sus requisitos empresariales. También
pruebe combinaciones de modos de error. Asegúrese de que los errores no se produzcan en cascada
y se manejen de forma aislada.
Este es otro motivo por el cual es importante analizar posibles puntos de error durante la fase de
diseño. Los resultados de ese análisis deben ser entradas en su plan de prueba.
Pruebas de carga. Realice una prueba de carga de una aplicación con una herramienta como Visual
Studio Team Services o Apache JMeter. Las pruebas de carga son cruciales para identificar errores
que solo ocurren con carga, como una base de datos de back-end sobrepasada o la limitación del
servicio. Pruebe la carga máxima con datos de producción o sintéticos que se acerquen lo más
posible a los datos de producción. El objetivo es ver cómo se comporta la aplicación en condiciones
reales.

Implementación resistente
Una vez que una aplicación se implementa en producción, las actualizaciones son una posible fuente
de errores. En el peor de los casos, una mala actualización puede provocar tiempo de inactividad.
Para evitar esto, el proceso de implementación debe ser predecible y repetible. La implementación
incluye el aprovisionamiento de los recursos de Azure, la implementación del código de la aplicación
y la aplicación de la configuración. Una actualización puede involucrarlos a todos o a un subconjunto.
El punto crucial es que las implementaciones manuales son propensas a errores. Por lo tanto, se
recomienda tener un proceso automatizado e idempotente que se pueda ejecutar a petición y volver
a ejecutar si algo presenta errores.
• Use las plantillas de Resource Manager para automatizar el aprovisionamiento de los recursos de
Azure.
• Use la Configuración de estado deseado (DSC) de Azure Automation para configurar VM.
• Use un proceso de implementación automatizado para el código de la aplicación.

91 CAPÍTULO 3k | Diseño de aplicaciones resistentes para Azure


Dos conceptos relacionados con una implementación resistente son la infraestructura como código y
la infraestructura inmutable.
• Infraestructura como código es la práctica de usar códigos para aprovisionar y configurar
infraestructura. La infraestructura como código puede usar un enfoque declarativo o un enfoque
imperativo (o una combinación de ambos). Las plantillas de Resource Manager son un ejemplo
de un enfoque declarativo. Los scripts de PowerShell son un ejemplo de un enfoque imperativo.
• Infraestructura inmutable es el principio de que no se debe modificar la infraestructura
después de que se ha implementado en producción. De lo contrario, puede entrar en un estado
en el que se han aplicado cambios ad hoc, lo que hace difícil saber exactamente qué cambió,
además de hacer que sea difícil razonar sobre el sistema.

Otra pregunta es cómo implementar una actualización de la aplicación. Recomendamos técnicas


como la implementación blue-green o los lanzamientos de valores controlados, que impulsan
las actualizaciones de forma altamente controlada para minimizar los posibles impactos de una
implementación incorrecta.
• La implementación blue-green es una técnica en la que 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 de tráfico a la versión actualizada. Por ejemplo,
Aplicaciones web Azure App Service lo activa con ranuras de transición.
• Los lanzamientos de valores controlados son similares a las implementaciones blue-green.
En lugar de cambiar todo el tráfico a la versión actualizada, implementa 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, retroceda y vuelva a la implementación anterior. De lo contrario, enrute más
tráfico a la nueva versión, hasta que obtenga el 100 % del tráfico.
Independientemente del enfoque que adopte, asegúrese de que puede retroceder a la última
implementación conocida, en caso de que la nueva versión no funcione. Además, si se producen
errores, los registros de la aplicación deben indicar qué versión causó el error.

Supervisión y diagnóstico
La supervisión y el diagnóstico son cruciales para la resistencia. Si algo presenta errores, debe saber
qué fue, y necesita información sobre la causa del error.
La supervisión de un sistema distribuido a gran escala plantea un desafío importante. Piense en una
aplicación que se ejecuta en unas docenas de VM: no es práctico iniciar sesión en cada VM, una a
la vez, y revisar los archivos de registro para intentar solucionar un problema. Además, la cantidad
de instancias de VM probablemente no sea estática. Las VM se agregan y eliminan a medida que
la aplicación escala y se reduce, y en ocasiones una instancia puede presentar errores y necesita
ser reaprovisionada. Además, una aplicación en la nube típica podría usar múltiples almacenes de
datos (almacenamiento de Azure, SQL Database, Cosmos DB, Caché en Redis), y una única acción del
usuario puede abarcar varios subsistemas.
Puede pensar en el proceso de supervisión y diagnóstico como una tubería con varias etapas
distintas:

92 CAPÍTULO 3k | Diseño de aplicaciones resistentes para Azure


• Instrumentación. Los datos sin procesar para la supervisión y el diagnóstico provienen
de diversas fuentes, las que incluyen registros de aplicaciones, registros del servidor web,
contadores de rendimiento del sistema operativo, registros de bases de datos y diagnósticos
integrados en la plataforma Azure. La mayoría de los servicios de Azure tienen una característica
de diagnóstico que puede usar para determinar la causa de los problemas.
• Recolección y almacenamiento. Los datos de instrumentación sin procesar se pueden
guardar en varias ubicaciones y con varios formatos (por ejemplo, registros de seguimiento de
aplicaciones, registros de IIS, contadores de rendimiento). Estas fuentes dispares se recopilan,
consolidan y almacenan de manera confiable.
• Análisis y diagnóstico. Una vez que los datos se han consolidado, se pueden analizar para
solucionar problemas y proporcionar información general del estado de la aplicación.
• Visualización y alertas. En esta etapa, los datos de telemetría se presentan de tal manera
que un operador puede detectar rápidamente problemas o tendencias. Los ejemplos incluyen
paneles o alertas por correo electrónico.
La supervisión no es lo mismo que la detección de errores. Por ejemplo, su aplicación puede detectar
un error transitorio y reintentar, lo que no provoca tiempo de inactividad. Pero también debe
registrar la operación de reintento para que pueda controlar la tasa de error, a fin de obtener una
panorámica del estado de la aplicación.
Los registros de la aplicación son una fuente importante de datos de diagnóstico. Los
procedimientos recomendados para el registro de una aplicación incluyen:
• Iniciar sesión en la producción. De lo contrario, perderá la información donde más la necesita.
• Registrar eventos en los límites de servicio. Incluya una ID de correlación que fluya por los límites
de servicio. Si una transacción fluye por múltiples servicios y uno de ellos presenta errores, la ID
de correlación lo ayudará a identificar por qué se produjo el error en la transacción.
• Use el registro semántico, también conocido como registro estructurado. Los registros no
estructurados dificultan la automatización del consumo y el análisis de los datos de registro, los
que son necesarios a escala de la nube.
• Use el registro asincrónico. De lo contrario, el sistema de registro en sí puede hacer que la
aplicación presente errores al provocar la creación de copias de seguridad de las solicitudes, ya
que estas se bloquean mientras se espera para escribir un registro de evento.
• El registro de aplicaciones no es lo mismo que la auditoría. La auditoría se puede hacer por
razones de cumplimiento o reglamentarias. Como tales, los registros de auditoría deben estar
completos, y no es aceptable omitirlos mientras se procesan las transacciones. Si una aplicación
requiere auditoría, esta debe mantenerse separada del registro de diagnóstico.
Para obtener más información acerca de la supervisión y el diagnóstico, consulte la Guía de
supervisión y diagnóstico.

Respuestas a errores manuales


Las secciones anteriores se han centrado en las estrategias de recuperación automáticas, que son
críticas para la alta disponibilidad. Sin embargo, a veces se necesita intervención manual.
• Alertas. Supervise su 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
su aplicación, es posible que deba aumentar la capacidad de la base de datos u optimizar sus
consultas. En este ejemplo, aunque la aplicación pueda manejar los errores de aceleración
de forma transparente, su telemetría aún debe generar una alerta para que pueda realizar un
seguimiento.
• Conmutación por error manual. Algunos sistemas no pueden realizar conmutaciones por error
de forma automática y requieren una conmutación por error manual.

93 CAPÍTULO 3k | Diseño de aplicaciones resistentes para Azure


• Prueba de preparación operativa. Si su aplicación realiza una conmutación por error a una
región secundaria, debe realizar una prueba de preparación operativa antes de volver a la región
principal. La prueba debe verificar que la región principal esté en buen estado y lista para recibir
tráfico nuevamente.
• Verificación de coherencia de datos. Si se produce un error en un almacén de datos, puede
haber incoherencias en los datos cuando el almacén vuelve a estar disponible, especialmente si
los datos fueron replicados.
• Restauración desde la copia de seguridad. Por ejemplo, si SQL Database experimenta una
interrupción regional, puede restaurar geográficamente la base de datos desde la última copia
de seguridad.
Documente y pruebe su plan de recuperación ante desastres. Evalúe el impacto comercial de los
errores en la aplicación. Automatice el proceso tanto como sea posible y documente los pasos
manuales, como la conmutación por error manual o la restauración de datos desde las copias de
seguridad. Evalúe regularmente su proceso de recuperación de desastres para validar y mejorar el
plan.

Resumen
En este artículo se analizó la resistencia desde una perspectiva holística, enfatizando algunos de los
desafíos únicos de la nube. Estos incluyen la naturaleza distribuida de la informática en la nube, el
uso de hardware básico y la presencia de errores transitorios en la red.

Estos son los puntos principales que se deben rescatar de este artículo:

• La resistencia conduce a una mayor disponibilidad y un menor tiempo promedio de recuperación


en caso de errores.
• Lograr la resistencia en la nube requiere un conjunto de técnicas diferente de las soluciones
locales tradicionales.

• La resistencia no se produce por accidente. Debe diseñarse e integrarse desde el principio.


• La resistencia toca cada parte del ciclo de vida de la aplicación, desde la planificación y
codificación hasta las operaciones.

• Realice pruebas y supervisión.

94 CAPÍTULO 3k | Diseño de aplicaciones resistentes para Azure


4

Diseñe su
aplicación de
Azure: use estos
pilares de calidad
Escalabilidad, disponibilidad, resistencia, administración y seguridad son
los cinco pilares de calidad de software. Concentrarse en estos pilares le
ayudará a diseñar una aplicación en la nube exitosa. Puede usar las listas
de verificación de esta guía para revisar su aplicación en relación con
estos pilares.

95 CAPÍTULO 4 | Diseñe su aplicación de Azure: use estos pilares de calidad


Pilar Descripción
Escalabilidad Diseñe el sistema para que pueda escalar mediante la incorporación de
nuevas instancias.
Disponibilidad Defina un objetivo de nivel de servicio (SLO) que defina claramente la
disponibilidad esperada y cómo se mide. Usa la ruta crítica para definir. Un
punto porcentual adicional de disponibilidad puede sumar horas adicionales
o días de actividad en un año.

Resistencia Las aplicaciones en la nube presentan errores ocasionales y deben


desarrollarse para que puedan recuperarse de ellos. Desarrolle mitigaciones
de resistencia en su aplicación en todos los niveles. Enfóquese primero en
las mitigaciones tácticas e incluya supervisión.
Administración Automatice las implementaciones y conviértalas en un proceso rápido y
rutinario para acelerar el lanzamiento de nuevas funciones o correcciones
de errores. Desarrolle operaciones de reversión o aplazamiento, e incluya
supervisión y diagnóstico.
Seguridad Cree administración de identidades, protección de infraestructura y
soberanía y cifrado de datos en su aplicación y en sus procesos DevOps.

Escalabilidad
La escalabilidad es la capacidad de un sistema para manejar una mayor carga. Existen dos formas
principales en que una aplicación puede escalar. El escalado vertical significa aumentar la capacidad
de un recurso, por ejemplo, usando una VM más grande. El escalado horizontal agrega nuevas
instancias de un recurso, como VM o réplicas de bases de datos.

El escalado horizontal tiene importantes ventajas sobre el escalado vertical:


• La verdadera escala de la nube. Las aplicaciones pueden diseñarse para ejecutarse en cientos o
incluso miles de nodos, alcanzando escalas que no son posibles en un solo nodo.
• El escalado horizontal es elástico. Puede agregar más instancias si la carga aumenta, o
eliminarlas durante períodos más tranquilos.
• El escalado puede activarse automáticamente, ya sea en un programa o en respuesta a cambios
en la carga.
• El escalado horizontal puede ser más económico que el vertical. Ejecutar varias VM pequeñas
puede costar menos que una sola VM grande.
• El escalado horizontal también puede mejorar la resistencia, al agregar redundancia. Si una
instancia presenta errores, la aplicación sigue ejecutándose.

Una ventaja del escalado vertical es que se puede hacer sin realizar ningún cambio en la aplicación.
Sin embargo, en algún momento alcanzará un límite, donde ya no podrá escalar más. En ese
momento, cualquier escalado adicional deberá ser horizontal.

El escalado horizontal debe diseñarse en el sistema. Por ejemplo, puede escalar VM colocándolas
detrás de un equilibrador de carga. Pero cada VM del grupo debe ser capaz de manejar cualquier
solicitud del cliente, por lo que la aplicación no debe tener estado o debe almacenar su estado
externamente (por ejemplo, en una memoria caché distribuida). Los servicios de PaaS administrados
a menudo tienen integrado el escalado horizontal y el escalado automático. La facilidad de escalar
estos servicios es una gran ventaja del uso de los servicios de PaaS.

96 CAPÍTULO 4 | Diseñe su aplicación de Azure: use estos pilares de calidad


Sin embargo, el solo hecho de agregar más instancias no significa que una aplicación escalará.
Simplemente podría mover el cuello de botella a otro lugar. Por ejemplo, si escala el front-end de
una Web para manejar más solicitudes de clientes, eso podría desencadenar conflictos de bloqueo
en la base de datos. Debería considerar medidas adicionales, como simultaneidad optimista o
partición de datos, para así permitir un mayor rendimiento de la base de datos.

Siempre realice pruebas 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 los cuellos de
botella y requieren un diseño meticuloso para escalar horizontalmente. Resolver un cuello de botella
puede revelar la existencia de otros cuellos de botella en otros lugares.

Use la Lista de comprobación de escalabilidad para revisar su diseño desde un punto de vista de
escalabilidad.

Guía de escalabilidad
• Patrones de diseño de escalabilidad y rendimiento
• Procedimientos recomendados: escalado automático, trabajos en segundo plano,
almacenamiento en caché, CDN, creación de particiones 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ñe su aplicación de Azure: use estos pilares de calidad


Disponibilidad
La disponibilidad es la proporción de tiempo que el sistema es funcional y operativo. Por lo general,
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 en la nube debe tener un objetivo de nivel de servicio (SLO) que defina claramente
la disponibilidad esperada y cómo se mide la disponibilidad. Al definir la disponibilidad, observe la
ruta crítica. El front-end de una web podría ser capaz de atender las solicitudes de los clientes, pero
si cada transacción presenta errores porque no se puede conectar a la base de datos, la aplicación no
está disponible para los usuarios.
La disponibilidad a menudo se describe en términos de "9"; por ejemplo, "cuatro 9" significa 99,99 %
de tiempo de actividad. La siguiente tabla muestra el posible tiempo de inactividad acumulativo en
diferentes niveles de disponibilidad.

Tiempo de inactividad por


SLAD Tiempo de inactividad por mes Tiempo de inactividad por año
semana

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

99,9 % 10,1 minutos 43,2 minutos8 0,76 horas

99,95 % 5 minutos 21,6 minutos4 0,38 horas

99,99 % 1,01 minutos4 0,32 minutos 52,56 minutos

99,999 % 6 segundos 25,9 segundos5 0,26 minutos

Tenga en cuenta que el tiempo de actividad del 99 % podría traducirse en una interrupción del
servicio de casi 2 horas por semana. Para muchas aplicaciones, especialmente aplicaciones orientadas
al consumidor, ese no es un SLO aceptable. Por otra parte, cinco 9 (99,999 %) significa no más
de 5 minutos de tiempo de inactividad en un año. Ya es bastante desafiante detectar un corte de
energía así de rápido, mucho más lo es resolver el problema. Para obtener una disponibilidad muy
alta (99,99 % o más), no puede confiar en la intervención manual para recuperarse de errores. La
aplicación debe diagnosticarse y repararse automáticamente, que es donde la resistencia se vuelve
crucial.

Guía de escalabilidad
• Patrones de diseño de 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ñe su aplicación de Azure: use estos pilares de calidad


Resistencia
Resistencia es la capacidad del sistema para recuperarse de errores y seguir funcionando. El objetivo
de la resistencia es devolver la aplicación a un estado completamente funcional después de que se
produce un error. La resistencia está estrechamente relacionada con la disponibilidad.

En el desarrollo de aplicaciones tradicionales, el enfoque ha estado en reducir el tiempo medio


entre errores (MTBF). El esfuerzo estaba en intentar evitar que el sistema presentara errores. En la
informática en la nube, se requiere una mentalidad diferente, debido a varios factores:

• Los sistemas distribuidos son complejos, y un error en un punto puede potencialmente generar
una cascada en todo el sistema.
• Los costos de los entornos en la nube se mantienen bajos mediante el uso de hardware básico,
por lo que se deben esperar errores de hardware ocasionales.
• Las aplicaciones a menudo dependen de servicios externos, que pueden quedar temporalmente
no disponibles o limitar a los usuarios de gran volumen.
• Los usuarios de hoy esperan que una aplicación esté disponible en todo momento sin
desconectarse nunca.

Todos estos factores significan que las aplicaciones en la nube deben diseñarse para esperar errores
ocasionales y recuperarse de ellos. Azure tiene muchas características de resistencia ya integradas en
la plataforma. Por ejemplo:

• Almacenamiento de Azure, SQL Database y Cosmos DB proporcionan replicación de datos


integrada, tanto dentro de una región como a través de regiones.
• Los discos administrados de Azure se colocan automáticamente en diferentes unidades de escala
de almacenamiento para limitar los efectos de los errores de hardware.
• Las VM en un conjunto de disponibilidad se distribuyen en varios dominios de error. Un dominio
de error es un grupo de VM que comparten una fuente de alimentación y un conmutador de red
comunes. La expansión de las VM en los dominios de error limita el impacto de los errores del
hardware, las interrupciones de la red o las interrupciones de energía.

Dicho esto, aún debe desarrollar la capacidad de recuperación de su aplicación. Las estrategias de
resistencia se pueden aplicar en todos los niveles de la arquitectura. Algunas mitigaciones son de
naturaleza más táctica: por ejemplo, reintentar una llamada remota después de un error transitorio
de la red. Otras mitigaciones son más estratégicas, como las conmutaciones por error de toda la
aplicación a una región secundaria. Las mitigaciones tácticas pueden marcar una gran diferencia. Si
bien es poco común que una región entera experimente una interrupción, los problemas transitorios
como la congestión de la red son más comunes, así que enfóquese en estos primero. Tener la
supervisión y el diagnóstico adecuados también es importante, tanto para detectar los errores
cuando ocurren como para encontrar las causas.

Cuando diseña una aplicación para ser resistente, debe comprender sus requisitos de disponibilidad.
¿Cuánto tiempo de inactividad es aceptable? Esto es en parte una función de costo. ¿Qué costo
tendrá para su negocio el posible tiempo de inactividad? ¿Cuánto debería invertir para que la
aplicación esté altamente disponible?

Use la Lista de comprobación de resistencia para revisar su diseño desde un punto de vista de la
resistencia.

99 CAPÍTULO 4 | Diseñe su aplicación de Azure: use estos pilares de calidad


Guía de resistencia
• Para obtener información acerca del diseño de aplicaciones resistentes para Azure, vaya a
https://docs.microsoft.com/en-us/azure/architecture/resiliency/index.
• Patrones de diseño de resistencia
• Procedimientos recomendados: manejo de errores transitorios, guía de reintentos 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 una aplicación en ejecución de
producción. Las implementaciones deben ser confiables y predecibles. Deben ser automatizadas
para reducir la posibilidad de error humano. Deben ser un proceso rápido y de rutina, para que no
ralenticen el lanzamiento de nuevas características o correcciones de errores. Igualmente importante,
debe poder revertir o aplazar rápidamente si una actualización tiene problemas.

La supervisión y el diagnóstico son cruciales. Para conocer los procedimientos recomendados de


supervisión y diagnóstico, vaya a https://docs.microsoft.com/en-us/azure/architecture/
best-practices/monitoring. Las aplicaciones en la nube se ejecutan un centro de datos remoto
donde no tiene control total de la infraestructura o, en algunos casos, del sistema operativo. En una
aplicación grande, no es práctico iniciar sesión en las VM para solucionar un problema o examinar
los archivos de registro. Con los servicios de PaaS, es posible que ni siquiera exista una VM dedicada
para iniciar sesión. La supervisión y el diagnóstico dan una idea del sistema, para que sepa cuándo y
dónde ocurren los errores. Todos los sistemas deben ser observables. Utilice un esquema de inicio de
sesión común y coherente que le permita correlacionar los eventos en todos los sistemas.

El proceso de supervisión y diagnóstico tiene varias fases distintas:

• Instrumentación. Generación de datos sin procesar, desde registros de aplicaciones, registros del
servidor web, diagnósticos integrados en la plataforma de Azure y otras fuentes.
• Recolección y almacenamiento. Consolidación de los datos en un solo lugar.
• Análisis y diagnóstico. Para solucionar problemas y ver el estado general.
• Visualización y alertas. Usar datos de telemetría para detectar tendencias o alertar al equipo de
operaciones.

Use la lista de verificación de DevOps para revisar su diseño desde un punto de vista de DevOps y
administración.

Guía de administración y DevOps


• Patrones de diseño para administración y monitoreo
• Mejores procedimientos: monitoreo y diagnóstico

100 CAPÍTULO 4 | Diseñe su aplicación de Azure: use estos pilares de calidad


Seguridad
Debe pensar en la seguridad durante todo el ciclo de vida de una aplicación, desde el diseño y la
aplicación hasta la implementación y las operaciones. La plataforma Azure proporciona protección
contra una variedad de amenazas, como la intrusión de la red y los ataques de DDoS. No obstante,
usted aún necesita desarrollar la seguridad en su aplicación y en sus procesos de DevOps.
Estas son algunas áreas de amplia seguridad para tener en cuenta.

Administración de la identidad
Considere usar Azure Active Directory (Azure AD) para autenticar y autorizar usuarios. Azure AD es un
servicio de administración de identidad y de acceso completamente administrado. Se puede utilizar
para crear los dominios que existen únicamente en Azure o que se integran con sus identidades de
Active Directory locales. Azure AD también se integra con Office365, Dynamics CRM Online y muchas
aplicaciones SaaS de terceros. Para aplicaciones destinadas al consumidor, Azure Active Directory
B2C permite que los usuarios se autentiquen con sus cuentas sociales actuales (como Facebook,
Google o LinkedIn), o creen una nueva cuenta de usuario administrada por Azure AD.
Si desea integrar un entorno de Active Directory local con una red de Azure, son posibles varios
enfoques según sus requisitos. Para obtener más información, consulte nuestras arquitecturas de
referencia de Administración de identidad.

Protección de la infraestructura
Controle el acceso a los recursos de Azure que implemente. Cada suscripción a Azure tiene una
relación de confianza con un inquilino Azure AD. Use el control de acceso basado en rol (RBAC) para
conceder a los usuarios dentro de su organización los permisos correctos a los recursos de Azure.
Conceda acceso mediante la asignación de rol de RBAC a los usuarios o grupos en un determinado
ámbito. El ámbito puede ser una suscripción, un grupo de recursos o un único recurso. Audite todos
los cambios a la infraestructura. Para obtener más información, vaya a https://docs.microsoft.com/
en-us/azure/active- directory/.

Seguridad de aplicaciones
En general, los procedimientos de seguridad recomendados para el desarrollo de aplicaciones siguen
vigentes en la nube. Estos incluyen cosas como el uso de SSL en todas partes, la protección contra
ataques CSRF y XSS, la prevención de ataques por inyección de código SQL, entre otros.
A menudo las aplicaciones en la nube utilizan servicios administrados con claves de acceso. Nunca
las compruebe en el control de código fuente. Considere almacenar los secretos de aplicaciones en
Azure Key Vault.

Cifrado y la soberanía de los datos


Asegúrese de que los datos permanecen en la zona geopolítica correcta mediante la alta
disponibilidad de Azure. El almacenamiento de información replicada geográficamente de Azure
utiliza el concepto de una región emparejada en la misma región geopolítica.
Utilice Key Vault para proteger las claves y secretos criptográficos. Gracias a Key Vault, puede cifrar
las claves y secretos mediante el uso de las claves que están 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ñe su aplicación de Azure: use estos pilares de calidad


Para obtener más información, visite:

• 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 vínculo} proporciona monitoreo de seguridad integrada y
administración de directivas en las suscripciones de Azure. Visite https://azure.microsoft.com/
en-us/services/security-center/.
• Para obtener información sobre cómo proteger sus aplicaciones en la nube,
https://docs.microsoft.com/en-us/azure/security/.

102 CAPÍTULO 4 | Diseñe su aplicación de Azure: use estos pilares de calidad


5

Diseñar su aplicación
de Azure: patrones
de diseño
Estos patrones de diseño son útiles para desarrollar aplicaciones
confiables, escalables y seguras en la nube.
Cada patrón describe el problema que aborda el patrón, las consideraciones para aplicarlo y un
ejemplo basado en Microsoft Azure. La mayoría de los patrones incluye ejemplos o fragmentos
de código que muestran cómo implementar el patrón en Azure. Sin embargo, la mayoría de los
patrones es importante para cualquier sistema distribuido, ya sea que esté hospedado en Azure o en
otras plataformas de nube.

Desafíos en el desarrollo de la nube


Disponibilidad
La disponibilidad define la proporción de tiempo que el sistema es funcional y operativo. Se verá
afectada por los errores del sistema, los problemas de infraestructura, los ataques malintencionados
y la carga del sistema. Por lo general, se mide como un porcentaje del tiempo de actividad. Por lo
general, las aplicaciones en la nube les ofrecen a los usuarios un acuerdo de nivel de servicio (SLA);
es decir que las aplicaciones se deben diseñar e implementar de una manera que maximice la
disponibilidad.

Patrón Resumen
Monitoreo del estado del Implemente comprobaciones funcionales en una aplicación a la
punto de conexión que puedan acceder las herramientas externas a través de puntos
de conexión expuestos a intervalos periódicos.

Nivelación de carga basada Utilice una cola que actúa como un amortiguador entre una tarea
en cola y un servicio que invoca con el fin de perfeccionar las cargas
pesadas intermitentes.

Regulación Controle el consumo de recursos que utiliza mediante la instancia


de una aplicación, un inquilino individual o un servicio completo.

103 CAPÍTULO 5 | Diseñar su aplicación de Azure: patrones de diseño


Administración de datos
La administración de datos es el elemento clave de las aplicaciones en la nube e influye en la
mayoría de los atributos de calidad. Los datos normalmente se alojan en lugares diferentes y en
varios servidores por razones tales como el rendimiento, la escalabilidad o la disponibilidad, y esto
puede presentar una serie de desafíos. Por ejemplo, se debe mantener la coherencia de los datos, y
normalmente los datos deberán sincronizarse en diferentes lugares.

Patrón Resumen
Cache-Aside Cargue los datos on-demand en la memoria caché de un almacén de
datos
CQRS Segregue las operaciones que leen los datos de las operaciones que
actualizan los datos mediante interfaces independientes.
Abastecimiento de Utilice un almacén solo para anexar para grabar la serie completa de
eventos eventos que describen las acciones sobre los datos de un dominio.
Tabla de índice Cree índices sobre los campos en los almacenes de datos a los que las
consultas hacen referencia con frecuencia.
Vista materializada Genere vistas rellenadas previamente sobre los datos en uno o más
almacenes de datos cuando los datos no tengan el formato ideal para
las operaciones de consulta necesarias.
Particionamiento Divida un almacén de datos en un conjunto de particiones horizontales
o fragmentos.
Hospedaje de Implemente el contenido estático en un servicio de almacenamiento
contenido estático basado en la nube que puede entregar directamente al cliente.
Clave auxiliar Utilice un token o clave que les proporciona a los clientes acceso
directo limitado a un recurso o servicio específico.

Diseño e implementación
Un buen diseño abarca factores tales como la uniformidad y la coherencia en el diseño y la
implementación, la capacidad de mantenimiento para simplificar la administración y el desarrollo, y la
reutilización para permitir que los componentes y 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 costo total de propiedad de las aplicaciones y servicios alojados en la nube.

Patrón Resumen
Embajador Cree servicios auxiliares que envían solicitudes de red en nombre de
una aplicación o servicio de consumidor.

Capa contra la Implemente una capa de fachada o adaptador entre una aplicación
corrupción moderna y un sistema heredado.

Back-ends para front- Cree servicios de backend independientes para que los utilicen
ends aplicaciones o interfaces de frontend específicas.

104 CAPÍTULO 5 | Diseñar su aplicación de Azure: patrones de diseño


CQRS Segregue las operaciones que leen los datos de las operaciones que
actualizan los datos mediante interfaces independientes.

Consolidación de los Consolide varias tareas u operaciones en una sola unidad


recursos de cálculo computacional.

Almacén de Mueva la información de configuración del paquete de implementación


configuración externa de la aplicación hacia una ubicación centralizada.

Agregación de gateway Use un gateway para agregar varias solicitudes individuales a una sola
solicitud.

Descarga de gateway Descargue una funcionalidad de servicio compartido o especializado en


un proxy de gateway.

Enrutamiento de Enrute solicitudes a varios servicios con un solo punto de conexión.


gateway

Elección de líder Coordine las acciones realizadas por una colección de instancias de
tareas de colaboración en una aplicación distribuida mediante la
elección de una instancia como el líder que asume la responsabilidad
de administrar otras instancias.

Tubos y filtros Divida una tarea que realiza el complejo procesamiento en una serie de
elementos independientes que se pueden reutilizar.

Sidecar Implemente los componentes de una aplicación en un proceso


o contenedor independiente para proporcionar aislamiento y
encapsulamiento.

Hospedaje de Implemente el contenido estático en un servicio de almacenamiento


contenido estático basado en la nube que puede entregar directamente al cliente.

Regulador Migre progresivamente un sistema heredado mediante el reemplazo


gradual de partes específicas de la funcionalidad con nuevas
aplicaciones y servicios.

Mensajes
La naturaleza distribuida de las aplicaciones en la nube requiere una infraestructura de mensajería
que conecte los componentes y servicios, idealmente acoplados de forma imprecisa para maximizar
la escalabilidad. La mensajería asincrónica es ampliamente utilizada y proporciona muchos beneficios,
pero también trae desafíos tales como el orden de los mensajes, la administración de mensajes
infectados, la idempotencia y mucho más.

Patrón Resumen
Consumidores que Permita que varios consumidores simultáneos procesen los mensajes
compiten recibidos en el mismo canal de mensajería.

Tubos y filtros Divida una tarea que realiza el complejo procesamiento en una serie de
elementos independientes que se pueden reutilizar.

105 CAPÍTULO 5 | Diseñar su aplicación de Azure: patrones de diseño


Cola de prioridad Priorice las solicitudes enviadas a los servicios para que las solicitudes
con una prioridad más alta se reciban y procesen más rápidamente que
las de menor prioridad.

Nivelación de carga Utilice una cola que actúa como un amortiguador entre una tarea y
basada en cola un servicio que invoca con el fin de perfeccionar las cargas pesadas
intermitentes.

Supervisor de agente Coordine una serie de acciones a través de un conjunto distribuido de


planificador servicios y otros recursos remotos.

Administración y monitoreo
Las aplicaciones en la nube se ejecutan un centro de datos remoto donde no tiene control total de
la infraestructura o, en algunos casos, del sistema operativo. Esto puede hacer que la administración
y el monitoreo sean más difíciles que una implementación local. Las aplicaciones deben exponer
información del tiempo de ejecución que los administradores y operadores puedan utilizar para
administrar y monitorear el sistema, así como también apoyar los requisitos cambiantes del negocio
y la personalización sin tener que detener o volver a implementar la aplicación.

Patrón Resumen
Embajador Cree servicios auxiliares que envían solicitudes de red en nombre de
una aplicación o servicio de consumidor.

Capa contra la Implemente una capa de fachada o adaptador entre una aplicación
corrupción moderna y un sistema heredado.

Almacén de Mueva la información de configuración del paquete de implementación


configuración externa de la aplicación hacia una ubicación centralizada.

Agregación de gateway Use un gateway para agregar varias solicitudes individuales a una sola
solicitud.

Descarga de gateway Descargue una funcionalidad de servicio compartido o especializado en


un proxy de gateway.

Enrutamiento de Enrute solicitudes a varios servicios con un solo punto de conexión.


gateway
Monitoreo del estado Implemente comprobaciones funcionales en una aplicación a la que
del punto de conexión puedan acceder las herramientas externas a través de puntos de
conexión expuestos a intervalos periódicos.

Sidecar Implemente los componentes de una aplicación en un proceso


o contenedor independiente para proporcionar aislamiento y
encapsulamiento.

Regulador Migre progresivamente un sistema heredado mediante el reemplazo


gradual de partes específicas de la funcionalidad con nuevas
aplicaciones y servicios.

106 CAPÍTULO 5 | Diseñar su aplicación de Azure: patrones de diseño


Rendimiento y escalabilidad
El funcionamiento es una indicación de la capacidad de respuesta de un sistema para ejecutar
cualquier acción dentro de un intervalo de tiempo determinado, mientras que la escalabilidad es
la capacidad de un sistema para controlar los aumentos en la carga sin afectar el rendimiento o
para aumentar con facilidad los recursos disponibles. Las aplicaciones en la nube normalmente
encuentran cargas de trabajo variables y alzas en la actividad. Su predicción, especialmente en un
escenario de varios usuarios, es casi imposible. En cambio, las aplicaciones deben ser capaces de
ampliarse dentro de los límites para satisfacer las alzas de demanda y escalar cuando disminuya la
demanda. La escalabilidad preocupa no solo en las instancias de cálculo sino que también en otros
elementos, como el almacenamiento de datos, la infraestructura de mensajería y más.

Patrón Resumen
Cache-Aside Cargue los datos on-demand en la memoria caché de un almacén de
datos

CQRS Segregue las operaciones que leen los datos de las operaciones que
actualizan los datos mediante interfaces independientes.

Abastecimiento de Utilice un almacén solo para anexar para grabar la serie completa de
eventos eventos que describen las acciones sobre los datos de un dominio.

Tabla de índice Cree índices sobre los campos en los almacenes de datos a los que las
consultas hacen referencia con frecuencia.

Vista materializada Genere vistas rellenadas previamente sobre los datos en uno o más
almacenes de datos cuando los datos no tengan el formato ideal para
las operaciones de consulta necesarias.

Cola de prioridad Priorice las solicitudes enviadas a los servicios para que las solicitudes
con una prioridad más alta se reciban y procesen más rápidamente que
las de menor prioridad.

Nivelación de carga Utilice una cola que actúa como un amortiguador entre una tarea y
basada en cola un servicio que invoca con el fin de perfeccionar las cargas pesadas
intermitentes.

Particionamiento Divida un almacén de datos en un conjunto de particiones horizontales


o fragmentos.

Hospedaje de Implemente el contenido estático en un servicio de almacenamiento


contenido estático basado en la nube que puede entregar directamente al cliente.

Regulación Controle el consumo de recursos que utiliza mediante la instancia de


una aplicación, un inquilino individual o un servicio completo.

107 CAPÍTULO 5 | Diseñar su aplicación de Azure: patrones de diseño


Resistencia
La resistencia es la capacidad de un sistema para manejar y recuperarse fácilmente de los errores.
La naturaleza del alojamiento en la nube, donde las aplicaciones son a menudo de varios inquilinos,
usan servicios compartidos de plataforma, compiten por recursos y ancho de banda, y funcionan
en hardware básico significan que hay un aumento de la probabilidad de que surgirán más errores
transitorios y permanentes. La detección de errores y la recuperación rápida y eficiente son
necesarias para mantener la resistencia.

Patrón Resumen

Cierre Aísle los elementos de una aplicación en grupos para que si uno falla,
los demás sigan funcionando.

Interruptor Maneje los errores que podrían requerir una cantidad variable de
tiempo para corregirse al conectarse a un recurso o servicio remoto.

Compensación de Deshaga el trabajo realizado por una serie de pasos, que en conjunto
transacciones definen una operación finalmente coherente.

Monitoreo del estado Implemente comprobaciones funcionales en una aplicación a la que


del punto de conexión puedan acceder las herramientas externas a través de puntos de
conexión expuestos a intervalos periódicos.

Elección de líder Coordine las acciones realizadas por una colección de instancias de
tareas de colaboración en una aplicación distribuida mediante la
elección de una instancia como el líder que asume la responsabilidad
de administrar otras instancias.

Nivelación de carga Utilice una cola que actúa como un amortiguador entre una tarea y
basada en cola un servicio que invoca con el fin de perfeccionar las cargas pesadas
intermitentes.

Volver a intentar Habilite una aplicación para manejar errores temporales previstos
cuando intente conectarse a un servicio o recurso de red al reintentar
de manera transparente una operación que falló previamente.

Supervisor de agente Coordine una serie de acciones a través de un conjunto distribuido de


planificador servicios y otros recursos remotos.

108 CAPÍTULO 5 | Diseñar su aplicación de Azure: patrones de diseño


Seguridad
La seguridad es la capacidad de un sistema para evitar acciones malintencionadas o accidentales
fuera del uso previsto y para evitar la divulgación o la pérdida de información. Las aplicaciones en
la nube están expuestas en Internet fuera de los límites de confianza locales, suelen estar abiertas
al público y pueden prestar servicio a usuarios que no son de confianza. Las aplicaciones deberán
diseñarse e implementarse de una manera que las proteja de los ataques malintencionados, restrinja
el acceso a solo a usuarios autorizados y proteja los datos confidenciales.

Patrón Resumen
Identidad federada Delegue la autenticación a un proveedor de identidad externo.

Equipo selector Proteja aplicaciones y servicios mediante el uso de una instancia de host
dedicado que actúe como un intermediario entre clientes y la aplicación
o servicio, valide y desinfecte las solicitudes, y apruebe las solicitudes y
los datos entre ellos.

Clave auxiliar Utilice un token o clave que les proporciona a los clientes acceso
directo limitado a un recurso o servicio específico.

109 CAPÍTULO 5 | Diseñar su aplicación de Azure: patrones de diseño


6

Catálogo de patrones
Patrón de embajador
Cree servicios auxiliares que envían solicitudes de red en nombre de una aplicación o servicio de
consumidor. Un embajador de servicio puede considerarse como un proxy fuera de proceso que está
coubicado con el cliente.
Este patrón puede ser útil para la descarga de tareas comunes de conectividad de cliente como
monitoreo, registro, enrutamiento, seguridad (como TLS) y patrones de resistencia en un lenguaje
independiente. A menudo se utiliza con aplicaciones heredadas, u otras aplicaciones que son difíciles
de modificar, con el fin de ampliar sus capacidades de red. También puede permitir que un equipo
especializado aplique esas características.

Contexto y problema
Las aplicaciones resistentes basadas en la nube requieren características como interrupción del
circuito, enrutamiento, medición, monitoreo y 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 agregar estas características, debido a que el código ya no se puede mantener
o el equipo de desarrollo ya no lo puede modificar con facilidad.
Las llamadas de red también pueden requerir una configuración sustancial para la conexión,
la autenticación y la autorización. Si estas llamadas se utilizan en varias aplicaciones, desarrolladas con
varios lenguajes y marcos, las llamadas deben configurarse para cada una de estas instancias. Además,
es posible que un equipo central dentro de la organización deba administrar la funcionalidad de red
y seguridad. Con una base de códigos grande, puede ser riesgoso para ese equipo actualizar el código
de la aplicación si no está familiarizados con este.

Solución
Coloque los marcos y bibliotecas de cliente en un proceso externo que actúa como un proxy entre la
aplicación y los servicios externos. Implemente el proxy en el mismo entorno host que su aplicación
para permitir el control sobre el enrutamiento, la resistencia, las características de seguridad y para
evitar cualquier restricción de acceso relacionada con los hosts. También puede utilizar el patrón de
embajador para estandarizar y ampliar la instrumentación. El proxy puede monitorear las métricas de
rendimiento, como la latencia o el uso de recursos, y este monitoreo ocurre en el mismo entorno de
host que la aplicación.

110 CAPÍTULO 6 | Catálogo de patrones


Las características que se descargan en embajador pueden administrarse independientemente
de la aplicación. Puede actualizar y modificar el embajador sin alterar la funcionalidad obsoleta
de la aplicación. También permite para equipos independientes y especializados implementen y
mantengan las características de seguridad, redes o autenticación que se migraron al embajador.
Los servicios de embajador pueden implementarse como un sidecar para acompañar el ciclo de
vida de una aplicación o servicio de consumo. Alternativamente, si varios procesos independientes
en un host común comparten un embajador, se puede implementar como un demonio o servicio
de Windows. Si el servicio de consumo está en contenedores, el embajador debe crearse como un
contenedor separado en el mismo host, con los vínculos correspondientes configurados para la
comunicación.

Problemas y consideraciones

• El proxy agrega alguna sobrecarga de latencia. Considere si es un mejor enfoque una biblioteca
de cliente invocada directamente por la aplicación.
• Considere el posible impacto de incluir características generalizadas en el proxy. Por ejemplo, el
embajador podría manejar reintentos, pero es posible que no sea seguro a menos que todas las
operaciones sean idempotentes.
• Considere un mecanismo para permitir que el cliente pase algún contexto al proxy, así como
al cliente. Por ejemplo, incluya encabezados de solicitud HTTP para dejar de reintentar o
especifique el número máximo de veces que se realizará el reintento.
• Considere cómo empaquetar e implementar el proxy.
• Considere la posibilidad de utilizar una única instancia compartida para todos los clientes o una
instancia para cada cliente.

Cuándo utilizar este patrón

Use este patrón cuando:

• Necesite desarrollar un conjunto común de funciones de conectividad de cliente para diversos


lenguajes o marcos.
• Necesite descargar los problemas transversales de conectividad del cliente para los
desarrolladores de infraestructura u otros equipos más especializados.
• Necesite admitir los requisitos de conectividad de nube o clúster en una aplicación heredada o
una aplicación que sea difícil de modificar.

Este patrón puede no ser adecuado:


• Cuando la latencia de la solicitud de red es crítica. Un proxy introducirá algunos gastos
generales, aunque mínimos, y en algunos casos esto puede afectar a la aplicación.
• Cuando un solo idioma consuma las funciones de conectividad del cliente. En ese caso, una
mejor opción podría ser una biblioteca de cliente que se distribuya a los equipos de desarrollo
como un paquete.
• Cuando no se puedan generalizar las funciones de conectividad y requieran 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 realiza 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 contra la corrupción


Aplique una fachada o capa de adaptador entre una aplicación moderna y un sistema heredado que
depende de este. Esta capa traduce las solicitudes entre la aplicación moderna y el sistema heredado.
Utilice este patrón para asegurarse de que el diseño de una aplicación no esté limitada por las
dependencias en los sistemas heredados.

Contexto y problema
La mayoría de las aplicaciones se basa en otros sistemas para algunos datos o funcionalidad. Por
ejemplo, cuando se migra una aplicación heredada a un sistema moderno, todavía puede necesitar
recursos heredados existentes. Las nuevas características deben ser capaces de llamar al sistema
heredado. Esto es especialmente cierto para las migraciones graduales, donde con el tiempo se migran
diversas características de una aplicación más grande a un moderno sistema.
A menudo, estos sistemas heredados sufren de problemas de calidad como esquemas de datos
complicados o API obsoletas. Las características y tecnologías usadas en los sistemas heredados
pueden variar ampliamente de los sistemas más modernos. Para interoperar con el sistema heredado,
puede que la nueva aplicación requiera admitir infraestructura obsoleta, protocolos, modelos de datos,
API u otras características que de otra manera no pondría en una aplicación moderna.
Mantener el acceso entre sistemas nuevos y heredados puede forzar al nuevo sistema a cumplir con
al menos algunas de las API del sistema heredado u otra semántica. Cuando estas características
heredadas tienen problemas de calidad, admitirlas "corrompe" lo que de otra manera podría ser una
aplicación moderna con un diseño limpio.

Solución
Aísle los sistemas heredados y modernos al colocar una capa contra la corrupción entre ellos. Esta
capa traduce las comunicaciones entre los dos sistemas, lo que permite que el sistema heredado
permanezca sin cambios, mientras que la aplicación moderna puede evitar comprometer 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 contra la corrupción siempre utiliza el modelo
de datos y la arquitectura de la aplicación. Las llamadas de la capa contra la corrupción al sistema
heredado se ajustan al modelo de datos o los métodos de ese sistema. La capa contra la corrupción
contiene toda la lógica necesaria para traducir 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 contra la corrupción puede añadir latencia a las llamadas realizadas entre los dos
sistemas.
• La capa contra la corrupción agrega un servicio adicional que se debe administrar y mantener.
• Considere cómo escalará su capa contra la corrupción.
• Considere si necesita más de una capa contra la corrupción. Puede descomponer la
funcionalidad en múltiples servicios mediante diferentes tecnologías o lenguajes, o puede haber
otras razones para dividir la capa contra la corrupción.
• Considere cómo se administrará la capa contra la corrupción en relación con sus otras
aplicaciones o servicios. ¿Cómo se integrarán en sus procesos de monitoreo, liberación y
configuración?
• Asegúrese de que se mantenga la coherencia de transacciones y datos, y que se puedan
monitorear.
• Considere si la capa contra la corrupción debe encargarse de todas las comunicaciones entre los
sistemas heredados y modernos, o solo un subconjunto de características.
• Considere si la capa contra la corrupción debe ser permanente o retirarse finalmente una vez se
ha migrado toda la funcionalidad heredada.

113 CAPÍTULO 6 | Catálogo de patrones


Cuándo utilizar este patrón
Use este patrón cuando:

• Se planee una migración en varias etapas, pero la integración entre los sistemas nuevos y
heredados deba mantenerse.
• Los sistemas nuevos y heredados tienen semántica diferente, pero todavía necesitan
comunicarse.
Este patrón puede no ser conveniente si no hay diferencias semánticas importantes entre los
sistemas nuevos y heredados.

Backends para el patrón de frontends


Cree servicios de backend independientes para que los utilicen aplicaciones o interfaces de frontend
específicas. Este patrón es útil cuando desea evitar la personalización de un backend único para
múltiples interfaces.

Contexto y problema
Una aplicación puede orientarse inicialmente en la interfaz de usuario web de escritorio. Por lo general,
se desarrolla en paralelo un servicio de backend 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 backend. El servicio de backend se convierte en un
backend de propósito general, que sirve a los requisitos de las interfaces móviles y de escritorio.
No obstante, las capacidades de un dispositivo móvil difieren significativamente en un explorador de
escritorio en las limitaciones de tamaño de la pantalla, rendimiento y visualización. Como resultado, los
requisitos para un backend de aplicaciones móviles difieren de la interfaz de usuario web de escritorio.

Estas diferencias resultan en los requisitos de competencia para el backend. El backend requiere
cambios normales y significativos para servir tanto a la interfaz de usuario web de escritorio y la
aplicación móvil. A menudo, equipos de interfaz independientes trabajan en cada frontend, lo
que permite que el backend se convierta en un cuello de botella en el proceso de desarrollo. Los
requisitos de actualización en conflicto y la necesidad de mantener el servicio en funcionamiento en
ambos frontends, puede generar un gasto muy alto en un único recurso que se puede implementar.
Como la actividad de desarrollo se centra en el servicio de backend, puede crearse un equipo
independiente para administrar y mantener el backend. En última instancia, esto resulta en una
desconexión entre los equipos de desarrollo de interfaz y backend, lo que agrega una carga al
equipo backend para equilibrar los requisitos competitivos de los diferentes equipos de interfaz
de usuario. Cuando un equipo de interfaz requiere cambios en el backend, esos cambios deben
validarse con los otros equipos de interfaz antes de que se pueden integrarse en el backend.

114 CAPÍTULO 6 | Catálogo de patrones


Solución
Cree un backend por interfaz de usuario. Afine el comportamiento y el rendimiento de cada backend
para satisfacer mejor las necesidades del entorno de frontend, sin preocuparse de afectar otras
experiencias de frontend.

Debido a que cada backend es específico para una interfaz, se puede optimizar para dicha interfaz.
Como resultado, será más pequeño, menos complejo y probablemente más rápido que un backend
genérico que intenta satisfacer los requisitos de todas las interfaces. Cada equipo de interfaz tiene
autonomía para controlar su propio backend y no se basa en un equipo de desarrollo de backend
centralizado. Esto le da flexibilidad al equipo de interfaz en la selección de idioma, la cadencia de
lanzamiento, la priorización de la carga de trabajo y la integración de la característica en su backend.

Problemas y consideraciones
• Considere cuántos backends implementar.
• Si diferentes interfaces (por ejemplo, los clientes móviles) realizarán las mismas solicitudes,
considere si es necesario implementar un backend para cada interfaz o si bastará con un
backend.
• La duplicación de código en los servicios es muy probable al implementar este patrón.
• Los servicios de backend enfocados en frontend solo deben contener comportamiento y lógica
específica del cliente. La lógica de negocios generales y otras características globales deben
administrarse en otras partes de la aplicación.
• Piense en cómo este patrón podría reflejarse en las responsabilidades de un equipo de
desarrollo.
• Considere cuánto tiempo tomará implementar este patrón. ¿El esfuerzo de desarrollar nuevos
backends incurrirá en deuda técnica a la vez que sigue admitiendo el backend genérico
existente?

115 CAPÍTULO 6 | Catálogo de patrones


Cuándo utilizar este patrón
Use este patrón cuando:

• Debe mantenerse un servicio de backend de propósito general o compartido con sobrecarga de


desarrollo significativo.
• Usted quiere optimizar el backend para los requisitos de interfaces específicas del cliente.
• Las personalizaciones se hacen en un servidor de propósito general para dar cabida a múltiples
interfaces.
• Un lenguaje alternativo es más adecuado para el backend de una interfaz de usuario diferente.

Este patrón puede no ser adecuado:

• Cuando las interfaces hacen solicitudes similares o iguales al backend.


• Cuando se utiliza solamente una interfaz para interactuar con el backend.

Guía relacionada
• Patrón de agregación de gateway

• Patrón de descarga de gateway

• Patrón de enrutamiento de gateway

Patrón de cierre
Aísle los elementos de una aplicación en grupos para que si uno falla, los demás sigan funcionando.

Este patrón se denomina cierre porque se asemeja a las particiones seccionadas del casco de una
embarcación. Si el casco está comprometido, solo la parte dañada se llena de agua, lo que impide
que el barco se hunda.

Contexto y problema
Una aplicación basada en la nube puede incluir múltiples servicios, donde cada uno de ellos
tiene uno o más consumidores. Una carga excesiva o un error en el servicio afectará a todos los
consumidores del servicio.
Además, un consumidor puede enviar solicitudes a múltiples servicios simultáneamente por medio
de recursos para cada solicitud. Cuando el consumidor envía una solicitud a un servicio que está
mal configurado o que no responde, los recursos utilizados por la solicitud del cliente no pueden
liberarse de forma oportuna. Dado que las solicitudes al servicio continúan, esos recursos pueden
agotarse. Por ejemplo, el grupo de conexiones del cliente puede agotarse. En ese momento, se ven
afectadas las solicitudes que realiza el consumidor a otros servicios. Finalmente el consumidor ya
no puede enviar solicitudes a otros servicios, tampoco puede hacerlo al servicio original que no
responde.
El mismo problema de agotamiento de recursos afecta a los servicios con múltiples consumidores.
Un gran número de solicitudes procedentes de un cliente puede agotar los recursos disponibles en
el servicio. Otros consumidores ya no son capaces de consumir el servicio, lo que provoca un efecto
en cascada del error.

116 CAPÍTULO 6 | Catálogo de patrones


Solución
Instancias de servicio de partición en grupos diferentes, basadas en los requisitos de carga y
disponibilidad del consumidor. Este diseño ayuda a aislar los errores y permite mantener la funcionalidad
del servicio para algunos consumidores, incluso durante un error.
Un consumidor puede también particionar los recursos para asegurarse de que los recursos utilizados
para llamar a un servicio no afecten a los recursos que utiliza para llamar a otro servicio. Por ejemplo, un
consumidor que pide servicios múltiples se puede asignar un grupo de conexiones para cada servicio. Si
un servicio comienza a fallar, solo afecta al grupo de conexiones asignado a ese servicio, lo que permite
que el consumidor siga utilizando los otros servicios.
Los beneficios de este modelo incluyen:
• Aísla los consumidores y servicios de los errores en cascada. Un problema que afecta a un
consumidor o un servicio puede aislarse dentro de su propio cierre, lo que impide que la solución
completa falle.
• Permite conservar algunas funcionalidades en caso de un error del servicio. Otros servicios y
características de la aplicación seguirán en funcionamiento.
• Permite implementar servicios que ofrecen una calidad de servicio diferente para el consumo de
aplicaciones. Un grupo de consumidores de alta prioridad puede configurarse para utilizar los
servicios de alta prioridad.

El siguiente diagrama muestra cierres estructurados en torno a grupos de conexión que llaman a
servicios individuales. Si el servicio A falla o causa algún otro problema, el grupo de conexiones se
aísla para que solo se vean afectadas las cargas de trabajo con el grupo de subprocesos asignado
al servicio A. Las cargas de trabajo que utilizan el servicio B y C no se ven afectadas y pueden seguir
trabajando sin interrupción.

El siguiente diagrama muestra a varios clientes que llaman a un único servicio. A cada cliente se le
asigna una instancia de servicio independiente. El cliente 1 ha hecho muchas solicitudes y ha abrumado
su instancia. Debido a que cada instancia de servicio está aislada de las demás, el resto de los clientes
puede seguir haciendo llamadas.

117 CAPÍTULO 6 | Catálogo de patrones


Problemas y consideraciones
• Defina particiones en torno a los requisitos comerciales y técnicos de la aplicación.

• Cuando particione los servicios o consumidores en cierres, considere el nivel de aislamiento que
ofrece la tecnología, así como la sobrecarga en términos de costo, rendimiento y manejabilidad.

• Considere combinar cierres con patrones de reintento, interruptor y regulación para


proporcionar un manejo más sofisticado de los errores.

• Cuando particione a los consumidores en cierres, considere el uso de procesos, grupos de


subprocesos y semáforos. Proyectos como Netflix Hystrix y Polly ofrecen un marco para la
creación de cierres de consumidores

• Al particionar servicios en cierres, considere implementarlos en máquinas virtuales, contenedores


o procesos independientes. Los contenedores ofrecen un buen equilibrio de aislamiento de
recursos con costos bastante bajos.

• Los servicios que se comunican mediante mensajes asincrónicos pueden aislarse a través de
diversos sistemas de colas. Cada cola puede tener un conjunto dedicado de instancias que
procesan mensajes en la cola, o un solo grupo de instancias mediante un algoritmo para quitar
de la cola y el procesamiento de envíos

• Determine el nivel de granularidad para los cierres. Por ejemplo, si desea distribuir los inquilinos
a través de particiones, puede colocar cada inquilino en una partición diferente, o varios
inquilinos en una partición.

• Monitoree el rendimiento y SLA de cada partición.

Cuándo utilizar este patrón


Use este patrón cuando:
• Aísle los recursos utilizados para consumir un conjunto de servicios de backend, en especial si la
aplicación puede proporcionar algún nivel de funcionalidad aun cuando uno de los servicios no
esté respondiendo.
• Aísle a los consumidores críticos de los consumidores estándar.
• Proteja la aplicación de los errores en cascada.
Este patrón puede no ser adecuado:
• El uso menos eficiente de los recursos puede no ser aceptable en el proyecto.
• La complejidad adicional no es necesaria.

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"

Guía relacionada

• Patrón de interruptor
• Diseño de aplicaciones resistentes para Azure
• Patrón de reintento
• Patrón de limitación

Patrón Cache-Aside
Cargue los datos on-demand en la memoria caché de un almacén de datos. Esto puede mejorar
el rendimiento y también ayuda a mantener la coherencia entre los datos que se mantienen en la
memoria caché y los datos 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
mantenida en un almacén de datos. Sin embargo, no es práctico esperar que los datos almacenados
en caché siempre serán 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 manejar las
situaciones que ocurran cuando los datos en la memoria caché queden obsoletos.

Solución
Muchos sistemas de almacenamiento en caché proporcionan operaciones de lectura y escritura/
escritura 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 memoria caché, se recuperan del almacén de datos y
se agregan a la memoria caché. Cualquier modificación a los datos que se mantienen en la memoria
caché se escriben automáticamente de nuevo en el almacén de datos.
Para las memorias caché que no proporcionan esta funcionalidad, es responsabilidad de las
aplicaciones que utilizan la memoria caché mantener los datos.
119 CAPÍTULO 6 | Catálogo de patrones
Una aplicación puede emular la funcionalidad de la memoria caché de lectura mediante la
implementación de la estrategia cache-aside. Esta estrategia carga datos en la memoria caché
on-demand. La figura ilustra con el patrón Cache-Aside para almacenar los datos en la memoria
caché.

Si una aplicación actualiza la información, puede seguir la estrategia de escritura al realizar la


modificación para el almacén de datos e invalidar el elemento correspondiente en la memoria caché.
Cuando se requiere el elemento a continuación, el uso de la estrategia cache-aside hará que los
datos actualizados se recuperen desde el almacén de datos y se agreguen a la memoria caché.

Problemas y consideraciones
Considere los siguientes puntos en el momento de decidir cómo implementar este patrón:
Vida útil de los datos almacenados en la memoria caché. Muchas memorias caché implementan
una directiva de caducidad que invalida los datos y los retira de la memoria caché si no se puede
acceder por un período determinado. Para que cache-aside sea eficaz, asegúrese de que la directiva
de caducidad coincida con el patrón de acceso para las aplicaciones que utilizan los datos. No
configure el período de caducidad muy breve porque esto puede provocar que las aplicaciones
recuperen continuamente los datos desde el almacén de datos y los agreguen a la memoria caché.
Del mismo modo, no configure el período de caducidad tan prolongado como para que los datos en
la memoria caché lleguen a ser obsoletos. Recuerde que el almacenamiento en caché es más eficaz
para datos relativamente estáticos o datos que se leen con frecuencia.
Expulsión de datos. La mayoría de las memorias caché tienen un tamaño limitado en comparación
con el almacén de datos donde se originan los datos y expulsarán los datos si fuese necesario. La
mayoría de las memorias caché adoptan una directiva menos usada recientemente para seleccionar
los elementos que se expulsarán, pero esto podría ser personalizable. Configure la propiedad de
caducidad global, otras propiedades de la memoria caché y la propiedad de caducidad de cada
elemento almacenado en caché para que la memoria caché sea rentable. No siempre es apropiado
aplicar una directiva global de expulsión a cada elemento de la memoria caché. Por ejemplo, si un
elemento almacenado en memoria caché es muy costoso para recuperarlo desde el almacén de
datos, puede ser beneficioso mantenerlo en la memoria caché a expensas de elementos de acceso
más frecuente pero menos costosos.

120 CAPÍTULO 6 | Catálogo de patrones


Limpieza de la memoria caché. Muchas soluciones llenan previamente la memoria caché con los datos
que una aplicación probablemente necesite como parte del proceso de arranque. El patrón Cache-Aside
todavía puede ser útil si algunos de estos datos caducan o son expulsados.
Coherencia. Implementar el patrón Cache-Aside no garantiza la coherencia entre el almacén de datos y la
memoria caché. Un elemento en el almacén de datos puede cambiarse en cualquier momento mediante
un proceso externo y este cambio puede no reflejarse en la memoria caché hasta la próxima vez que se
cargue el elemento. En un sistema que replica los datos a través de almacenes de datos, este problema
puede llegar a ser grave si la sincronización se produce con frecuencia.
Almacenamiento en memoria caché local (in-memory). Una memoria caché puede ser local para
una instancia de aplicación y almacenarse in-memory. Cache-aside puede ser útil en este entorno si una
aplicación accede repetidamente a los mismos datos. Sin embargo, una memoria caché local es privada y,
por lo tanto, cada una de las diferentes instancias de la aplicación podría tener una copia de la misma en
los datos almacenados en caché. Estos datos podrían ser rápidamente incoherentes entre memorias caché,
por lo que podría ser necesario caducar los datos almacenados en una memoria caché privada y actualizar
más a menudo. En estos escenarios, considere investigar el uso de un mecanismo de almacenamiento en
caché compartido o distribuido.

Cuándo utilizar este patrón


Considere los siguientes puntos en el momento de decidir cómo implementar este patrón:
Vida útil de los datos almacenados en la memoria caché. Muchas memorias caché implementan una
directiva de caducidad que invalida los datos y los retira de la memoria caché si no se puede acceder por
un período determinado. Para que cache-aside sea eficaz, asegúrese de que la directiva de caducidad
coincida con el patrón de acceso para las aplicaciones que utilizan los datos. No configure el período
de caducidad muy breve porque esto puede provocar que las aplicaciones recuperen continuamente
los datos desde el almacén de datos y los agreguen a la memoria caché. Del mismo modo, no configure
el período de caducidad tan prolongado como para que los datos en la memoria caché lleguen a ser
obsoletos. Recuerde que el almacenamiento en caché es más eficaz para datos relativamente estáticos o
datos que se leen con frecuencia.
Expulsión de datos. La mayoría de las memorias caché tienen un tamaño limitado en comparación con
el almacén de datos donde se originan los datos, y expulsarán los datos si fuese necesario. La mayoría de
las memorias caché adoptan una directiva menos usada recientemente para seleccionar los elementos
que se expulsarán, pero esto podría ser personalizable. Configure la propiedad de caducidad global y
otras propiedades de la memoria caché y la propiedad de caducidad de cada elemento almacenado en
caché para que la memoria caché sea rentable. No siempre es apropiado aplicar una directiva global de
expulsión a cada elemento de la memoria caché. Por ejemplo, si un elemento almacenado en memoria
caché es muy costoso para recuperarlo desde el almacén de datos, puede ser beneficioso mantenerlo en
la memoria caché a expensas de elementos de acceso más frecuente pero menos costosos.
Limpieza de la memoria caché. Muchas soluciones llenan previamente la memoria caché con los datos
que una aplicación probablemente necesite como parte del proceso de arranque. El patrón Cache-Aside
todavía puede ser útil si algunos de estos datos caducan o son expulsados.
Coherencia. Implementar el patrón Cache-Aside no garantiza la coherencia entre el almacén de datos y la
memoria caché. Un elemento en el almacén de datos puede cambiarse en cualquier momento mediante
un proceso externo y este cambio puede no reflejarse en la memoria caché hasta la próxima vez que se
cargue el elemento. En un sistema que replica los datos a través de almacenes de datos, este problema
puede llegar a ser grave si la sincronización se produce con frecuencia.
Almacenamiento en memoria caché local (in-memory). Una memoria caché puede ser local para
una instancia de aplicación y almacenarse in-memory. Cache-aside puede ser útil en este entorno si una
aplicación accede repetidamente a los mismos datos. Sin embargo, una memoria caché local es privada y,
por lo tanto, cada una de las diferentes instancias de la aplicación podría tener una copia de la misma en
los datos almacenados en caché. Estos datos podrían ser rápidamente incoherentes entre memorias caché,
por lo que podría ser necesario caducar los datos almacenados en una memoria caché privada y actualizar
más a menudo. En estos escenarios, considere 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
Use este patrón cuando:
• Una memoria caché no proporciona operaciones de lectura y escritura nativas.
• La demanda de recursos es impredecible. Este patrón permite que las aplicaciones carguen los
datos on-demand. No hace ninguna suposición acerca de los datos que una aplicación requiere
por adelantado.

Este patrón puede no ser adecuado:


• Cuando el conjunto de datos almacenado en memoria caché es estático. Si los datos se ajustan al
espacio de memoria caché disponible, limpie la memoria caché con los datos de inicio y aplique
una directiva que impida que los datos caduquen.
• Para almacenar en memoria caché información de estado de la sesión en una aplicación
web hospedada en una granja de servidores web. En este entorno, se debe evitar introducir
dependencias basadas en afinidad de cliente-servidor.

Ejemplo
En Microsoft Azure puede utilizar Azure Redis Cache para crear una memoria caché distribuida que
se puede compartir con varias instancias de una aplicación.
Para conectarse a una instancia de Azure Redis Cache, llame al método estático de Connect y
apruebe la cadena de conexión. El método devuelve un ConnectionMultiplexer que representa
la conexión. Un enfoque para compartir una instancia de ConnectionMultiplexer en su aplicación
es contar con una propiedad estática que devuelva una instancia de conexión, similar al ejemplo
siguiente. Este enfoque proporciona una manera segura de subprocesos para inicializar una instancia
de conexión única.

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 memoria
caché utilizando el método de lectura.
Un objeto se identifica mediante un ID de entero como la clave. El método GetMyEntityAsync intenta
recuperar un elemento con esta clave de la memoria caché. Si se encuentra un elemento coincidente,
se devuelve. Si no hay ninguna coincidencia en la memoria caché, el método GetMyEntityAsync
recupera el objeto de un almacén de datos, lo agrega a la memoria caché y lo devuelve. El código
que realmente lee 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 memoria caché se configura
para que caduque a fin de evitar que quede 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 memoria 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
memoria caché cuando se cambia el valor por la aplicación. El código actualiza el almacén de datos
original y luego elimina el elemento en caché de la memoria 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 memoria caché.
Si quita el elemento almacenado en la memoria caché en primer lugar, hay una pequeña ventana de tiempo en que un
cliente puede recuperar el elemento antes de que el almacén de datos se actualice. Ello provocará una falta de memoria
caché (porque el elemento se eliminó de la memoria caché) haciendo que se recupere la versión anterior del elemento
en el almacén de datos y se agregue a la memoria caché. El resultado de ello son datos obsoletos de la memoria caché.

123 CAPÍTULO 6 | Catálogo de patrones


Guía relacionada
La siguiente información puede ser pertinente al implementar este patrón:
• Guía de almacenamiento en memoria caché. Proporciona información adicional sobre cómo
puede almacenar datos en memoria caché en una solución de nube y los problemas que debe
considerar al implementar una memoria caché.
• Manual básico de coherencia de datos. Por lo general, las aplicaciones en la nube utilizan
datos que se transmiten a través de almacenes de datos. Administrar y mantener la coherencia
de los datos en este entorno es un aspecto crítico del sistema, en especial los problemas de
concurrencia y disponibilidad que pueden surgir. Este manual básico describe los problemas
de coherencia en los datos distribuidos y resume cómo una aplicación puede implementar
coherencia final para mantener la disponibilidad de los datos.

Patrón de interruptor
Maneje los errores que podrían requerir una cantidad variable de tiempo para recuperarse al
conectarse a un recurso o servicio 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 recursos sobrepasados
o temporalmente no disponibles. Estos errores suelen corregirse por sí solos después de un breve
período y una aplicación de nube robusta debe estar preparada para manejarlos mediante el uso de
una estrategia como el patrón de reintento.

Sin embargo, también puede haber situaciones donde los errores se deben a acontecimientos
imprevistos, y ello podría tardar mucho más tiempo en corregirse. Estos errores pueden variar
en gravedad de una pérdida parcial de la conectividad al error completo de un servicio. En estas
situaciones podría ser inútil para una aplicación reintentar continuamente una operación que sea
poco probable que tenga éxito, y en cambio la aplicación debe aceptar con rapidez que la operación
ha fallado y encargarse de este error por consiguiente.

Además, si un servicio está muy ocupado, el error en una parte del sistema podría llevar a 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 que se bloqueen muchas solicitudes
simultáneas para la misma operación hasta que caduque el periodo de espera. Estas solicitudes
bloqueadas podrían mantener recursos críticos del sistema como la memoria, los subprocesos, las
conexiones de base de datos y así sucesivamente. En consecuencia, estos recursos podrían agotarse,
causando un error de otras piezas posiblemente no relacionadas del sistema que deben usar los
mismos recursos. En estas situaciones, sería preferible para la operación que genere inmediatamente
el error y solo intente invocar el servicio si es probable que tenga éxito. Tenga en cuenta que
establecer un tiempo de espera más breve podría ayudar a resolver este problema, pero el tiempo de
espera no debe ser tan breve como para que la operación falle la mayor parte del tiempo, aunque la
solicitud para el servicio tenga éxito finalmente.

124 CAPÍTULO 6 | Catálogo de patrones


Solución
El patrón de interruptor puede evitar que una aplicación intente varias veces ejecutar una operación
que es probable que falle. Permitir que continúe sin necesidad de esperar la corrección de la falla o
malgastar ciclos de CPU mientras determina que la falla es larga duración. El patrón de interruptor
también permite que una aplicación detecte si el error se resolvió. Si el problema parece haberse
resuelto, la aplicación puede intentar invocar la operación.

El propósito del patrón de interruptor de circuito es diferente del patrón de reintento. El patrón de
reintento permite que una aplicación reintente una operación con la esperanza de que tendrá éxito.
El patrón de interruptor impide que una aplicación realice una operación que es probable que falle.
Una aplicación puede combinar estos dos patrones mediante el patrón de reintento para invocar una
operación a través de un interruptor. Sin embargo, la lógica de reintento debe responder a cualquier
excepción devuelta por el interruptor y abandonar los intentos de reintento si el interruptor indica
que es un error no transitorio.

Un interruptor actúa como un proxy para las operaciones que podrían fallar. El proxy debe
monitorear el número de errores recientes que ocurrieron y utilizar esta información para decidir si
se permite que la operación proceda o simplemente devolver una excepción inmediatamente.

El proxy se puede implementar como una máquina de estado con los siguientes estados que imitan
la funcionalidad de un interruptor eléctrico:

• Cerrado: 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 conteo. Si el número de errores recientes supera un umbral especificado dentro de un
período determinado, el proxy se coloca en el estado Abierto. En este punto el proxy inicia un
temporizador de tiempo de espera, y cuando este temporizador caduca el proxy se coloca en el
estado Semiabierto.

• El objetivo del temporizador de tiempo de espera es dar tiempo al sistema para


solucionar el problema que causó el error antes de permitir que la aplicación intente
realizar nuevamente la operación.

• Abierto: la solicitud de la aplicación arroja un error inmediatamente y devuelve una excepción a


la aplicación.

• Semiabierto: se permite que un número limitado de solicitudes de la aplicación pase e invoque


la operación. Si estas solicitudes tienen éxito, se supone que se corrigió el error que previamente
estaba causando la falla y el interruptor cambia al estado Cerrado (se restablece el contador de
errores). Si cualquier petición falla, el interruptor supone que el error todavía está presente por lo
que vuelve al estado Abierto y reinicia el temporizador de tiempo de espera para dar al sistema
un período adicional para recuperarse del error.

• El estado Semiabierto es útil para evitar que un servicio de recuperación se inunde


de repente con solicitudes. A medida que se recupera un servicio, podría ser capaz de
admitir un volumen limitado de solicitudes hasta que se complete la recuperación, pero
mientras la recuperación está en curso, una inundación de trabajo puede causar que el
servicio agote el tiempo de espera o vuelva a fallar.

125 CAPÍTULO 6 | Catálogo de patrones


En la figura, el contador de errores utilizado por el estado Cerrado se basa en el tiempo. Se
restablece automáticamente a intervalos periódicos. Esto ayuda a evitar que el interruptor ingrese
en el estado Abierto si experimenta errores ocasionales. El umbral del error que activa el interruptor
en el estado Abierto solo se alcanza cuando ha ocurrido un número especificado de errores durante
un intervalo especificado. El contador utilizado por el estado Semiabierto registra el número de
intentos correctos para invocar la operación. El interruptor vuelve al estado Cerrado después de
que un número especificado de invocaciones de operación consecutiva ha tenido éxito. Si cualquier
invocación falla, el interruptor ingresa en el estado Abierto inmediatamente y el contador de éxito se
restablecerá la próxima vez que ingrese en el estado Semiabierto.
La forma en que se recupera el sistema se maneja desde el exterior, posiblemente al restaurar o
reiniciar un componente con errores o reparar una conexión de red.

El patrón del interruptor proporciona estabilidad mientras que el sistema se recupera de un error
y minimiza el impacto en el rendimiento. Puede ayudar a mantener el tiempo de respuesta del
sistema al rechazar rápidamente la solicitud de una operación que es susceptible de fallar, en lugar
de esperar que la operación agote su tiempo o no vuelva nunca más. Si el interruptor provoca un
evento cada vez que cambia de estado, esta información puede utilizarse para monitorear el estado
de la parte del sistema protegido por el interruptor o para avisar a un administrador cuando un
interruptor se activa en el estado Abierto.

El patrón es personalizable y se puede adaptar según el tipo de error posible. Por ejemplo, puede
aplicar un temporizador de tiempo de espera cada vez mayor a un interruptor. Podría colocar el
interruptor en el estado Abierto por unos segundos al inicio y luego, si el error no se ha resuelto,
aumentar el tiempo de espera a unos minutos y así sucesivamente. En algunos casos, en lugar de
que el estado Abierto devuelva un error y genere una excepción, podría ser útil devolver un valor
predeterminado que es significativo para la aplicación.

126 CAPÍTULO 6 | Catálogo de patrones


Problemas y consideraciones
Debe considerar los siguientes puntos en el momento de decidir cómo implementar este patrón:
Manejo de excepciones. Una aplicación que invoca una operación a través de un interruptor debe
estar preparada para manejar las excepciones que surgen si la operación no está disponible. La
forma en que se manejan las excepciones será específica de la 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 podría fallar por muchas razones, algunas de las cuales podrían
indicar un tipo más grave de error que otros. Por ejemplo, una solicitud podría fallar debido a que
un servicio remoto colapsó y tardará varios minutos en recuperarse, o debido al tiempo de espera
porque el servicio está sobrecargado temporalmente. Un interruptor podrían examinar los tipos
de excepciones que se producen y ajustar su estrategia según la naturaleza de estas excepciones.
Por ejemplo, podría requerir un número mayor de excepciones de tiempo de espera para activar el
interruptor en el estado Abierto en comparación con el número de errores debido a que el servicio
no está disponible en lo absoluto.
Registro. Un interruptor debe registrar todas las solicitudes con errores (y posiblemente las
solicitudes correctas) para permitir que un administrador monitoree el estado de la operación.
Capacidad de recuperación. Debe configurar el interruptor para que coincida con el patrón
de recuperación probable de la operación que está protegiendo. Por ejemplo, si el interruptor
permanece en el estado Abierto por un largo período, podría plantear excepciones aunque la causa
de la avería se haya resuelto. Del mismo modo, un interruptor podría fluctuar y reducir los tiempos
de respuesta de las aplicaciones si cambia del estado Abierto al estado Semiabierto demasiado
rápido.
Pruebas de operaciones con errores. En el estado Abierto, en lugar de utilizar un temporizador
para determinar cuándo cambiar al estado Semiabierto, un interruptor puede hacer ping
periódicamente del servicio remoto o un recurso para determinar si está disponible otra vez. Este
ping podría adoptar la forma de un intento de invocar una operación que antes falló, o podría utilizar
una operación especial proporcionada por el servicio remoto específicamente para probar el estado
del servicio, según lo descrito por el patrón de monitoreo del estado del punto de conexión.
Anulación manual. En un sistema donde el tiempo de recuperación para una operación con errores
es muy variable, resulta beneficioso entregar una opción de restablecimiento manual que permita
a un administrador cerrar un interruptor (y restablecer el contador de errores). Del mismo modo,
un administrador podría forzar un interruptor en el estado Abierto (y reiniciar el temporizador de
tiempo de espera) si la operación protegida por el interruptor no está disponible temporalmente.
Concurrencia. Un gran número de instancias simultáneas de una aplicación podría acceder al mismo
interruptor. La aplicación no debería bloquear solicitudes simultáneas o agregar una sobrecarga
excesiva a cada llamada a una operación.
Diferenciación de recursos. Tenga cuidado cuando use un único interruptor 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 otro está experimentando un problema temporal. Si las respuestas de error en estos escenarios
se combinan, una aplicación podría intentar acceder a algunas particiones aun cuando la falla sea
muy probable, mientras que el acceso a otras particiones se bloquearía a pesar de que es probable
que sea exitoso.
Interruptor acelerado. A veces una respuesta de error puede contener suficiente información para
que el interruptor se active inmediatamente y permanezca desconectado por un tiempo mínimo. Por
ejemplo, la respuesta de errores desde un recurso compartido que está sobrecargado podría indicar
que no se recomienda un reintento inmediato y que la aplicación, en cambio, debe volver a intentar
en pocos minutos.
127 CAPÍTULO 6 | Catálogo de patrones
Notas:
Un servicio puede devolver HTTP 429 (demasiadas solicitudes) si está regulando el cliente o HTTP 503
(servicio no disponible) si el servicio no está disponible actualmente. La respuesta puede incluir
información adicional, como la duración prevista de la demora.

Reproducción de solicitudes con error. En el estado Abierto, en lugar de simplemente fallar


rápidamente, un interruptor podría registrar los detalles de cada solicitud a un diario y organizar
estas solicitudes para que se reproduzcan cuando el recurso remoto o servicio esté disponible.
Tiempos de espera inadecuados en servicios externos. Un interruptor no podría ser capaz de
proteger por completo las aplicaciones de operaciones con errores en los servicios externos que
se configuran con un período de espera prolongado. Si el tiempo de espera es demasiado largo,
un subproceso que ejecuta un interruptor podría bloquearse durante un largo período antes de
que el interruptor indique que la operación ha fallado. En este tiempo, muchas otras instancias
de aplicaciones también podrían tratar de invocar el servicio a través del interruptor y enlazar 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 esta operación falle.
Este patrón no se recomienda:
• Para manejar el acceso a los recursos privados locales en una aplicación, como la estructura de
datos in-memory. En este entorno, utilizar un interruptor sobrecargaría su sistema.
• Como un sustituto para el manejo de excepciones en la lógica de negocio de sus aplicaciones.

Ejemplo
En una aplicación web, varias de las páginas se llenan con los datos obtenidos de un servicio externo.
Si el sistema implementa una memoria caché mínima, la mayoría de coincidencias con estas páginas
provocará un vuelta completa al servicio. Las conexiones de la aplicación web para 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 web supone que el servicio no estará disponible y
arrojará una excepción.
Sin embargo, si el servicio falla y el sistema está muy ocupado, los usuarios podrían verse obligados a
esperar 60 segundos antes de que se produzca una excepción. Finalmente recursos como la memoria,
las conexiones y los subprocesos podrían agotarse, impidiendo que otros usuarios se conecten al
sistema, aunque no estén accediendo a las páginas que recuperan los datos desde el servicio.
Podría retrasarse el escalamiento del sistema al agregar más servidores web e implementar el
equilibrio de carga cuando se agoten los recursos, pero no resolverá el problema porque las
solicitudes de usuario seguirán sin capacidad de respuesta y todos los servidores web podrían
quedarse en algún momento sin recursos.
En resumen, la lógica que se conecta con el servicio y recupera los datos de un interruptor podría
ayudar a solucionar este problema y encargarse del error en el servicio de manera más elegante. Las
solicitudes de usuario seguirán con errores, pero fallarán más rápido y no bloquearán los recursos.

128 CAPÍTULO 6 | Catálogo de patrones


La clase CircuitBreaker mantiene información de estado acerca de un interruptor 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, y será abierto, semiabierto o cerrado según
se defina en la enumeración CircuitBreakerStateEnum. La propiedad IsClosed debe ser verdadera si el
interruptor está cerrado, pero falsa si está abierto o semiabierto. El método Trip cambia el estado del
interruptor al estado abierto y registra la excepción que provocó el cambio en el estado, junto con la
fecha y hora en que ocurrió la excepción. Las propiedades LastException y LastStateChangedDateUtc
devuelven esta información. El método Reset cierra el interruptor y el método HalfOpen establece el
interruptor en semiabierto.

La clase InMemoryCircuitBreakerStateStore en el ejemplo contiene una implementación de la interfaz


ICircuitBreakerStateStore. La clase CircuitBreaker crea una instancia de esta clase para mantener el
estado del interruptor.
El método ExecuteAction en la clase CircuitBreaker resume una operación, especificada como un
delegado de acción. Si el interruptor está cerrado, ExecuteAction invoca al delegado de acción. Si la
operación falla, un controlador de excepciones llama a TrackException, que establece el estado del
interruptor en abierto. En el ejemplo de código siguiente se destaca este flujo.

El método ExecuteAction en la clase CircuitBreaker resume una operación, especificada como un


delegado de acción. Si el interruptor está cerrado, ExecuteAction invoca al delegado de acción. Si la
operación falla, un controlador de excepciones llama a TrackException, que establece el estado del
interruptor en 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 omitió 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 este es el caso, el método ExecuteAction establece el interruptor en medio abierto y luego intenta
realizar la operación que se especifica en el delegado Action.

Si la operación se realiza correctamente, el interruptor se restablece en el estado cerrado. Si no


puede realizarse la operación, el estado abierto se vuelve a activar y la hora de 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 por un período de tiempo breve, menos que lo que se
indica en el valor de OpenToHalfOpenWaitTime, el método ExecuteAction simplemente genera una
excepción CircuitBreakerOpenException y devuelve el error que provocó que el interruptor hiciera la
transición al estado abierto.

Además, usa un bloqueo para impedir que el interruptor intente hacer llamadas simultáneas a la
operación mientras está medio abierto. Un intento simultáneo de invocar la operación se tratará
como si el interruptor estuviera abierto y generará un error con una excepción, tal 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


Con el fin de usar 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 la operación
que se realizará como parámetro. La aplicación debe estar preparada para capturar la excepción
CircuitBreakerOpenException si la operación no se puede realizar correctamente porque el
interruptor está abierto. En el siguiente código se 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 siguientes patrones también podrían resultar útiles al implementar este patrón:
• Patrón de reintento. Describe cómo una aplicación puede manejar los errores temporales
previstos cuando intenta conectarse a un servicio o recurso de red al reintentar de manera
transparente una operación que presentó anteriormente un error.
• Patrón de supervisión de punto 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 que indique su estado.

Patrón CQRS (segregación de responsabilidad de


consultas y comandos)
Segregue las operaciones que leen los datos de las operaciones que actualizan los datos mediante
interfaces independientes. Esto puede maximizar el rendimiento, la escalabilidad y la seguridad,
admite la evolución del sistema con el tiempo gracias a una mayor flexibilidad y evita que los
comandos de actualización provoquen conflictos de combinación en el nivel de dominio.

Contexto y problema
En los sistemas de administración de datos tradicionales, tanto los comandos (actualizaciones de los
datos) como las consultas (solicitudes de datos) se ejecutan contra el mismo conjunto de entidades
en un repositorio de datos único. 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


En estos sistemas, es habitual que todas las operaciones de creación, lectura, actualización y
eliminación (CRUD) se apliquen a la misma representación de la entidad. Por ejemplo, la capa de
acceso a datos (DAL) recupera desde el almacén de datos un objeto de transferencia de datos (DTO)
que representa a un cliente y aparece en la pantalla. Un usuario actualiza algunos campos del DTO
(posiblemente mediante un enlace de datos) y luego la DAL vuelve a guardarlo el DTO en el almacén
de datos. El mismo DTO se usa tanto para las operaciones de lectura como de escritura. La figura
muestra una arquitectura tradicional de CRUD.

Los diseños tradicionales de CRUD funcionan bien cuando se aplica una lógica de negocios limitada
exclusivamente 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, el que luego se puede
modificar según sea necesario.
Sin embargo, el enfoque tradicional de CRUD tiene algunas desventajas:
• Esto a menudo significa que existe un error de coincidencia entre las representaciones de lectura
y escritura de los datos, como columnas o propiedades adicionales que se deben actualizar
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 de un dominio de colaboración, donde varios actores trabajan en paralelo
en el mismo conjunto de datos. O bien hay conflictos de actualización provocados por las
actualizaciones simultáneas cuando se usa 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 la capa de acceso a datos
y el almacén de datos, además de complejidad de las consultas que se requieren para recuperar
la información.
• Puede hacer más compleja la administración de la seguridad y los permisos, porque cada entidad
está sujeta a operaciones de lectura y escritura que podrían exponer los datos en el contexto
erróneo.
Para comprender en mayor profundidad los límites del enfoque CRUD, consulte
CRUD, solo cuando puede permitírselo.

Solución
CQRS (segregación de responsabilidad de consultas y comandos) es un patrón que segrega las
operaciones que leen los datos (consultas) de las operaciones que actualizan los datos (comandos)
mediante interfaces separadas. Esto significa que los modelos de datos que se usan para realizar
consultas y actualizaciones son diferentes. Por lo tanto, los modelos se pueden aislar, tal como se
muestra en la figura siguiente, a pesar de que este no sea un requisito absoluto.

133 CAPÍTULO 6 | Catálogo de patrones


En comparación con el modelo de datos único que se usa en los sistemas basados en CRUD, el uso
de modelos independientes 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 lo que
ocurre con los diseños de CRUD, el código de CQRS no se puede generar automáticamente con los
mecanismos de Scaffold.
El modelo de consulta para leer datos y el modelo de actualización para escribir datos puede obtener
acceso al mismo almacén físico, quizás mediante el uso de vistas SQL o la generación de proyecciones
sobre la marcha. Sin embargo, es común separar los datos en almacenes físicos distintos para
maximizar el rendimiento, la escalabilidad y la seguridad, tal como se muestra en la figura siguiente.

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 distinta. Usar varias
réplicas de solo lectura del almacén de lectura puede aumentar en gran medida el rendimiento de
las consultas y la capacidad de respuesta de la interfaz de usuario de la aplicación, especialmente
en escenarios distribuidos donde las réplicas de solo lectura se ubican cerca de las instancias de
aplicación. Algunos sistemas de bases de datos (SQL Server) proporcionan características adicionales,
como las réplicas de conmutación por error, con el fin de maximizar la disponibilidad.
La separación de los almacenes de lectura y escritura también permite que cada uno se pueda
escalar de manera adecuada para coincidir con la carga. Por ejemplo, los almacenes de lectura
habitualmente se encuentran con una carga mucho mayor que los almacenes de escritura.
Cuando el modelo de consulta/lectura contiene datos no normalizados (consulte el patrón Vista
materializada), el rendimiento se maximiza 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
Considere los siguientes puntos en el momento de decidir cómo implementar este patrón:
• Dividir el almacén de datos en almacenes físicos independientes para las operaciones de lectura
y escritura puede aumentar el rendimiento y la seguridad de un sistema, pero también podría
agregar complejidad en términos de resistencia y coherencia final. El almacén de modelos de
lectura se debe actualizar para reflejar los cambios que se realizan en el almacén de modelos de
escritura, y puede resultar difícil de detectar cuándo un usuario emite una solicitud basada 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 el Manual básico de coherencia de
datos.
• Considere la posibilidad de aplicar CQRS en secciones limitadas del sistema, donde será más valioso.
• Un enfoque típico para la implementación de la coherencia final consiste en usar el
abastecimiento de eventos en conjunto con CQRS, de manera tal que el modelo de escritura sea
una secuencia de eventos solo para anexar impulsada por la ejecución de los comandos. Estos
eventos se usan para actualizar las vistas materializadas que sirven como el modelo de lectura.
Para obtener más información, consulte Abastecimiento de eventos y CQRS.

Cuándo utilizar este patrón


Use este patrón en las siguientes situaciones
• Dominios de colaboración donde varias operaciones se realizan en paralelo en los mismos datos.
CQRS permite definir comandos con la granularidad suficiente para minimizar los conflictos de
combinación en el nivel de dominio (los conflictos que sí aparezcan 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, resulta útil
para los equipos que ya están 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 negocios, validación de entrada y validación empresarial para asegurarse de que todo
siempre sea coherente para cada uno de los agregados (cada clúster 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 pila de validación ni lógica de negocios y solo devuelve un DTO para
usarlo en un modelo de vista. El modelo de lectura presenta coherencia final con el modelo de
escritura.
• Escenarios 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 se requiere realizar el escalado horizontal. Por ejemplo, en muchos sistemas,
el número de operaciones de lectura es muchas veces mayor que el número de operaciones de
escritura. Para adaptarse a esto, piense en la posibilidad de escalar horizontalmente el modelo
de lectura, pero ejecutando el modelo de escritura en solo una o unas pocas instancias. Un
pequeño número de instancias del modelo de escritura también ayuda a minimizar la existencia
de conflictos de combinación.
• Escenarios donde un equipo de desarrolladores se puede centrar en el modelo de dominios
complejo que forma parte del modelo de escritura y otro equipo se puede centrar en el modelo
de lectura y las interfaces de usuario.
• Escenarios donde se espera que el sistema evolucione con el tiempo y pueda contener varias
versiones del modelo, o bien donde las reglas de negocio cambian regularmente.
• Integración con otros sistemas, especialmente en combinación con el abastecimiento de eventos,
donde el error temporal de un subsistema no debería afectar 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 basta una interfaz de usuario estilo 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. Hay componentes específicos de un escenario de
administración de datos general donde CQRS puede resultar útil, pero puede aumentar la
complejidad de una manera considerable e innecesaria cuando no es necesario.

Abastecimiento de eventos y CQRS


El patrón de CQRS se usa a menudo junto con el patrón de abastecimiento de eventos. Los sistemas
basados en CQRS usan modelos de datos de lectura y escritura independientes, cada uno adaptado
a las tareas pertinentes y a menudo ubicado en almacenes separados físicamente. Cuando se usa
en conjunto 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, habitualmente como vistas enormemente no
normalizadas. Estas vistas se adaptan a las interfaces y muestran los requisitos de la aplicación, lo
que ayuda a maximizar tanto el rendimiento de la pantalla como de la consulta.
El uso de la secuencia de eventos como almacén de escritura en lugar de los datos reales en
un momento dado evita los conflictos de actualización en un agregado único y maximiza el
rendimiento y la escalabilidad. Los eventos se pueden usar para generar de manera asincrónica vistas
materializadas de los datos que se usan para rellenar el almacén de lectura.
Debido a que el almacén de eventos es la fuente oficial de información, es posible eliminar 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 de los datos.

Cuando use CQRS en combinación con el patrón de abastecimiento de eventos, considere lo


siguiente:
• Como ocurre con cualquier sistema donde los almacenes de escritura y lectura son
independientes, los sistemas basados en este patrón solo presentan coherencia final. Habrá
cierto retraso entre el evento que se está generando y el almacén de datos que se está
actualizando.
• El patrón aumenta la complejidad porque se debe crear código para iniciar y controlar los
eventos y montar o actualizar las vistas o los objetos apropiados requeridos por 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 hacer que una implementación correcta sea más difícil y
requiere un enfoque distinto ante los sistemas de diseño. Sin embargo, el abastecimiento de
eventos puede facilitar el modelado del dominio y la recompilación de las vistas o la creación de
nuevas porque se conserva la intención de los cambios en los datos.
• La generación de vistas materializadas para usarlas en el modelo de lectura o las proyecciones
de los datos mediante la reproducción y el manejo de los eventos para entidades o colecciones
de entidades específicas puede requerir un importante tiempo de procesamiento y uso de
recursos. Esto resulta especialmente cierto si requiere la suma o el análisis de valores durante
largos períodos, porque es posible que sea necesario 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
que usa distintas 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 pueden
ajustarse 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 que los usuarios califiquen los productos. Para esto, el código de la aplicación usa
el comando RateProduct que se muestra en el código siguiente.

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 usa la clase ProductsCommandHandler para controlar los comandos que envía
la aplicación. Los clientes habitualmente envían comandos al dominio mediante un sistema de
mensajería como una cola. El controlador de comandos acepta estos comandos e invoca los
métodos de la interfaz de dominio. La granularidad de cada comando está diseñada para reducir la
posibilidad de que haya solicitudes en conflicto. El código siguiente muestra un esquema 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 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 IProductsDomain contiene métodos que tienen un significado
en el dominio. Habitualmente, en un entorno de CRUD, estos métodos tendrían nombres genéricos
como Guardar o Actualizar y un DTO como único argumento. El enfoque CQRS se puede diseñar
para cumplir con las necesidades de los sistemas de administración del inventario y empresarial de
esta organización.

Guías y patrones relacionados


Los siguientes patrones y guías resultan útiles cuando se implementa este patrón:
• Para ver una comparación de CQRS con otros estilos de arquitectura, consulte Estilos de
arquitectura y Estilo de arquitectura de CQRS.
• Manual básico de coherencia de datos. Explica los problemas que se suelen encontrar debido
a la coherencia final entre los almacenes de lectura y escritura cuando se usa el patrón CQRS y
cómo se pueden resolver.
• Guía de partición de datos. Describe cómo los almacenes de datos de lectura y escritura que se
usan en el patrón de CQRS se pueden dividir en particiones a las que se puede obtener acceso
y administrar por separado para mejorar la escalabilidad, reducir la contención y optimizar el
rendimiento.
• Patrón de abastecimiento de eventos. Describe con mayor detalle cómo se puede usar el
abastecimiento de eventos con el patrón CQRS para simplificar tareas en dominios complejos
a la vez que se mejora el rendimiento, la escalabilidad y la capacidad de respuesta. Además de
cómo proporcionar coherencia para los datos transaccionales, mientras se mantienen auditorías
e historiales completos que permiten acciones compensatorias.
• 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 el modelo de lectura
se puede usar para generar vistas materializadas.
• La guía de patrones y prácticas sobre el recorrido de CQRS. En particular, la introducción al
patrón de segregación de responsabilidades de consultas y comandos explora el patrón y
cuándo es útil. El epílogo sobre las lecciones aprendidas ayuda a comprender algunos de los
problemas que surgen cuando se usa este patrón.
• La publicación CQRS de Martin Fowler, donde se explican los aspectos fundamentales del patrón
y se brindan vínculos a otros recursos útiles.
• Las publicaciones de Greg Young, que exploran muchos aspectos del patrón de CQRS.

Patrón de transacción compensatoria


Deshaga el trabajo realizado en una serie de pasos, que en conjunto definen una operación con
coherencia final, si se produce un error en uno o más de los pasos. Las operaciones que siguen
el modelo de coherencia final suelen encontrarse en aplicaciones hospedadas en la nube que
implementan flujos de trabajo y procesos de negocio complejos.

Contexto y problema
Las aplicaciones que se ejecutan en la nube modifican los datos con frecuencia. Estos datos
podrían propagarse en distintos orígenes de datos en ubicaciones geográficas diferentes. Para
evitar la contención y mejorar el rendimiento en un entorno distribuido, una aplicación no debería
intentar proporcionar una coherencia transaccional sólida. Por el contrario, la aplicación debería
implementar la coherencia final. En este modelo, una operación comercial típica consta de 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 completa y se han ejecutado todos los pasos, el
sistema debería volver a ser coherente. En el Manual básico de coherencia de datos, se proporciona
información sobre por qué las transacciones distribuidas no se escalan correctamente, además de los
principios del modelo de coherencia final.

139 CAPÍTULO 6 | Catálogo de patrones


Uno de los desafíos del modelo de coherencia final es la manera de controlar un paso que no se
pudo realizar. En este caso, podría ser necesario deshacer todo el trabajo que se completó en los
pasos anteriores de la operación. Sin embargo, los datos no se pueden simplemente deshacer
porque es posible 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
sería una simple cuestión de restablecer el estado original. Podría ser necesario aplicar varias reglas
específicas del negocio (consulte 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á necesario visitar cada uno de los almacenes de datos a
la vez. El trabajo que se realiza en cada almacén de datos se debe deshacer de manera confiable para
evitar que el sistema siga siendo incoherente.

No todos los datos que se ven afectados por una operación que implementa la coherencia final se
pueden mantener en una base de datos. En un entorno de 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, también se debe deshacer este cambio de estado.
Esto puede implicar que se debe volver a invocar el servicio y realizar otra acción que revierta los
efectos de la primera.

Solución
La solución es implementar una transacción compensatoria. Los pasos de una transacción
compensatoria deben deshacer los efectos de los pasos de la operación original. Es posible que una
transacción compensatoria no sea capaz de simplemente reemplazar el estado actual por el estado
en que estaba el sistema cuando empezó la operación, porque este enfoque podría sobrescribir los
cambios realizados por otras instancias simultáneas de una aplicación. Por el contrario, debe ser
un proceso inteligente que tenga en cuenta cualquier trabajo realizado por instancias simultáneas.
Este proceso habitualmente será específico de la aplicación, impulsado por la naturaleza del trabajo
realizado por la operación original.

Un enfoque común consiste en usar un flujo de trabajo para implementar una operación con
coherencia final que requiera compensación. A medida que la operación original avanza, el sistema
registra la información sobre cada paso y la manera en que se puede deshacer el trabajo realizado
en ese paso. Si la operación presenta un error en algún punto, el flujo de trabajo vuelve atrás en los
pasos que ha completado y realiza el trabajo para revertir cada paso. Tenga en cuenta que es posible
que una transacción compensatoria 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 para deshacer en paralelo.

Este enfoque es similar a la estrategia de Sagas que se explica en el blog de Clemens Vasters.

Una transacción compensatoria también es una operación con coherencia final y también podrían
surgir errores. El sistema debe ser capaz de reanudar la transacción compensatoria en el punto de
error y continuar. Podría ser necesario repetir un paso que no se pudo realizar, por lo que los pasos
de una transacción compensatoria deben definirse como comandos idempotentes. Para obtener más
información, consulte los patrones de idempotencia en el blog de Jonathan Oliver.

En algunos casos, no sería posible recuperarse de un paso que no se realizó correctamente, excepto
mediante la intervención manual. En estas situaciones, el sistema deberá generar 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
Considere los siguientes puntos en el momento de decidir cómo implementar este patrón:
No resulta fácil determinar cuando no se realiza correctamente un paso de una operación que
implementa coherencia final. Es posible que un paso no presente inmediatamente un error, pero se
podría bloquear. Es posible que sea necesario implementar algún tipo de mecanismo de tiempo de
espera.

La lógica de compensación no se generaliza fácilmente. Una transacción compensatoria es específica


para la aplicación. Confía en que la aplicación tiene la información suficiente para poder deshacer los
efectos de cada paso en una operación con errores.

Debe definir los pasos de una transacción compensatoria como comandos idempotentes. Esto
permite que se repitan los pasos si la transacción compensatoria misma presenta un error.

La infraestructura que controla los pasos de la operación original y la transacción compensatoria


deben ser resistentes. No debe perder la información que se requiere para compensar un paso
con errores y debe ser capaz de supervisar de manera confiable el progreso de la lógica de
compensación.

Una transacción compensatoria no devuelve necesariamente los datos del sistema al estado que
tenían al comienzo de la operación original. En cambio, compensa el trabajo realizado en los pasos
que se completaron correctamente antes del error en la operación.

El orden de los pasos de la transacción compensatoria no tiene que ser necesariamente el opuesto
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 lo tanto, los pasos de la transacción compensatoria que
deshacen los cambios realizados en este almacén se deben realizar primero.

Colocar un bloqueo basado en un tiempo de espera a corto plazo en cada recurso que se requiere
para completar una operación y obtener 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 se deben
finalizar antes de que expiren los bloqueos.

Considere usar una lógica de reintento que sea más tolerante de lo habitual para minimizar los
errores que desencadenan una transacción compensatoria. Si un paso de una operación que
implementa la coherencia final presenta un error, intente tratar el error como una excepción
transitoria y repita el paso. Solo debe detener la operación e iniciar una transacción compensatoria si
un paso presenta un error repetidamente o de manera irreversible.

Muchos de los desafíos de implementar una transacción compensatoria son los mismos que los que
se producen en la implementación de la coherencia final. Para obtener más información, consulte
la sección de consideraciones para la implementación de la coherencia final en el Manual básico de
coherencia de datos.

Cuándo utilizar este patrón


Use este patrón únicamente para las operaciones que se deben deshacer si presentan un error.
Si es posible, diseñe soluciones para evitar la complejidad que implica requerir transacciones
compensatorias.

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 al crear un itinerario:
1. Reservar un asiento en el vuelo F1 desde Seattle hasta Londres.

2. Reservar un asiento en el vuelo F2 desde Londres hasta París.

3. Reservar un asiento en el vuelo F3 desde París hasta 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, a pesar de que 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. Luego, los pasos necesarios para realizar las operaciones de contador se
pueden ejecutar como una transacción compensatoria.
Observe que los pasos de la transacción compensatoria podrían no ser los opuestos exactos de los
pasos originales y la lógica en cada paso de la transacción compensatoria debe tener en cuenta las
reglas específicas del negocio. Por ejemplo, anular la reserva de un asiento en un vuelo no da derecho
al cliente al reembolso completo del dinero pagado. La figura muestra cómo generar una transacción
compensatoria para deshacer una transacción de larga ejecución para reservar un itinerario de viaje.

Es posible que los pasos de la transacción compensatoria se realicen en paralelo, según cómo se
haya diseñado la lógica compensatoria para cada paso.

En muchas soluciones de negocio, el error de un solo paso no siempre implica que haya que revertir
el sistema mediante una transacción compensatoria. Por ejemplo, si después de haber reservado los
vuelos F1, F2 y F3 en el escenario del sitio web de viajes, el cliente no puede reservar una habitación
en el hotel H1, es preferible ofrecerle una habitación en otro hotel de la misma ciudad, en lugar
de cancelar los vuelos. El cliente puede decidir cancelar la reserva de todos modos (en cuyo caso
la transacción compensatoria se ejecuta y deshace las reservas de los vuelos F1, F2 y F3), pero esta
decisión la debe tomar el cliente, no el sistema.

142 CAPÍTULO 6 | Catálogo de patrones


Guías y patrones relacionados
Los siguientes patrones y guías también pueden ser pertinentes al implementar este patrón:
• Manual básico de coherencia de datos. El patrón de transacción compensatoria se suele usar
para deshacer las operaciones que implementan el modelo de coherencia final. Este manual
básico proporciona información sobre los beneficios y las compensaciones de la coherencia final.
• Patrón programador-agente-supervisor. Describe cómo se implementan los sistemas resistentes
que realizan operaciones comerciales que usan recursos y servicios distribuidos. A veces puede
ser necesario deshacer el trabajo que realizó una operación mediante el uso de una transacción
compensatoria.
• Patrón de reintento. Ejecutar transacciones compensatorias puede ser caro y podría ser posible
reducir al mínimo su uso mediante la implementación de una directiva eficaz que consiste en
reintentar las operaciones erróneas siguiendo el patrón de reintento.

Patrón de consumidores competitivos


Permita que varios consumidores simultáneos procesen los mensajes recibidos en el mismo canal
de mensajería. Esto permite que un sistema procese varios mensajes de manera simultánea 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 la nube controle una gran cantidad de solicitudes. En
lugar de procesar sincrónicamente cada solicitud, una técnica común 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
controla de manera asincrónica. Esta estrategia ayuda a garantizar que la lógica de negocios 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 las solicitudes agregadas que provienen de varios
inquilinos puede causar una carga de trabajo imprevisible. En las horas punta, 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 controlar estas solicitudes
puede ser muy variable. Usar una sola instancia del servicio de consumidor puede hacer que esa
instancia se inunde de solicitudes, o bien se podría sobrecargar el sistema de mensajería debido
a una afluencia de mensajes provenientes de la aplicación. Para controlar esta carga de trabajo
fluctuante, el sistema puede ejecutar varias instancias del servicio de consumidor. Sin embargo, estos
consumidores se deben coordinar 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
Use una cola de mensajes para implementar el canal de comunicación entre la aplicación y las
instancias del servicio de consumidor. La aplicación publica las solicitudes como mensajes en la cola
y las instancias del servicio de consumidor reciben mensajes de la cola y los procesan. Este enfoque
permite que el mismo conjunto de instancias del servicio de consumidor administre mensajes de
cualquier instancia de la aplicación. La figura ilustra cómo usar 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 administrar amplias variaciones en el
volumen de las solicitudes que las instancias de la aplicación envían. La cola actúa como un búfer
entre las instancias de la aplicación y las instancias del servicio de consumidor. Esto puede ayudar
a minimizar el impacto en la disponibilidad y la capacidad de respuesta tanto de la aplicación
como de las instancias de servicio, tal como se describe en el patrón de nivelación de carga
basado en cola. Controlar un mensaje que requiere un procesamiento de larga ejecución no evita
que otras instancias del servicio de consumidor administren simultáneamente otros mensajes.
• Mejora la confiabilidad. Si un productor se comunica directamente con un consumidor en lugar
de usar este patrón, pero no supervisa al consumidor, existe una alta probabilidad de que los
mensajes se pierdan o no se procesen si el consumidor comete un error. En este patrón, los
mensajes no se envían a una instancia de servicio específica. Una instancia de servicio con errores
no bloqueará 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 de consumidor a medida que fluctúa el volumen de los mensajes.
• Puede mejorar la resistencia si la cola de mensajes proporciona las operaciones de lectura
transaccionales. Si una instancia del servicio de consumidor lee y procesa el mensaje como parte
de una operación transaccional y la instancia del servicio de consumidor presenta un error, este
patrón puede garantizar que el mensaje se devolverá a la cola para que lo recoja y lo controle
otra instancia del servicio de consumidor.

Problemas y consideraciones
Considere los siguientes puntos en el momento de decidir cómo implementar este patrón:
• Orden de mensajes. El orden en el que las instancias del servicio de consumidor reciben
los mensajes no está garantizado y no refleja necesariamente el orden en 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 del orden en que se administran 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 del tipo primero
en entrar, primero en salir 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 con errores, podría ser necesario implementar el procesamiento que
realizan las instancias de servicio como operaciones idempotentes para minimizar los efectos de
recuperar y procesar más de una vez un único mensaje.
• Detección de mensajes dudosos. Un mensaje con formato incorrecto o una tarea que requiere
acceso a recursos que no están disponibles pueden provocar un error en 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 en
caso de que sea necesario.
• Control de los resultados. La instancia de servicio que controla un mensaje se desacopla
completamente de la lógica de aplicación que genera el mensaje y es posible que no sean
capaces de comunicarse directamente. Si la instancia de servicio genera resultados que deben
pasarse a la lógica de la aplicación, esta información se debe almacenar en una ubicación a la
que ambos puedan tener acceso. Para impedir que la lógica de la aplicación recupere datos
incompletos, el sistema debe indicar cuando se completó el procesamiento.
Si usa Azure, un proceso de trabajo puede pasar de nuevo los resultados a la lógica de la
aplicación mediante 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 el Manual básico sobre la mensajería asincrónica.
• Escalado del sistema de mensajería. En una solución a gran escala, una cola de mensajes única
podría verse sobrepasada por la cantidad de mensajes y convertirse en un cuello de botella
en el sistema. En esta situación, considere la posibilidad de crear particiones en el sistema de
mensajería para enviar mensajes de productores específicos a una cola determinada, o bien use
el equilibrio de carga para distribuir los mensajes entre varias colas de mensajes.
• Garantía de la confiabilidad del sistema de mensajería. Se necesita un sistema de mensajería
confiable que garantice que, después de que la aplicación ponga un mensaje en la cola, este no
se perderá. Esto resulta fundamental para garantizar que todos los mensajes se entreguen al
menos una vez.

Cuándo utilizar este patrón

Use este patrón cuando:


• La carga de trabajo de una aplicación se divide en tareas que se pueden ejecutar de manera
asincrónica.
• Las tareas son independientes y se pueden ejecutar en paralelo.
• El volumen de trabajo es muy variable, por lo que requiere una solución escalable.
• La solución debe proporcionar alta disponibilidad y debe ser resistente si no se puede realizar 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 se deben realizar sincrónicamente y la lógica de la aplicación debe esperar a que se
complete una tarea antes de continuar.
• Las tareas se deben realizar en una secuencia específica.

145 CAPÍTULO 6 | Catálogo de patrones


Algunos sistemas de mensajería admiten sesiones que permiten que un productor agrupe los
mensajes y se asegure de que el mismo consumidor los controle todos. Este mecanismo puede
usarse con los mensajes priorizados (si son compatibles) para implementar una manera de ordenar
los mensajes que entrega los mensajes en secuencia desde un productor a un solo consumidor.

Ejemplo
Azure proporciona colas de almacenamiento y colas de Service Bus que pueden servir 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 implementaron como tareas en uno o más roles pueden recuperar
los mensajes de esta cola y procesarlos. Por motivos de resistencia, una cola de Service Bus permite
que un consumidor use el modo PeekLock cuando recupera un mensaje de la cola. Este modo en
realidad no quita el mensaje, sino que solo se lo oculta a otros consumidores. El consumidor original
puede eliminar el mensaje una vez que termine de procesarlo. Si el consumidor presenta un error,
el tiempo de espera del modo 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 las 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 Azure
Storage, consulte Introducción a Azure Queue Storage con .NET.
El siguiente código de la clase QueueManager de la solución CompetingConsumers disponible
en GitHub muestra cómo puede crear una cola mediante una instancia de QueueClient en el
controlador de eventos Start en un rol web o de trabajo.

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 de consumidor puede recibir mensajes
de la cola si sigue 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 características de escalado automático, como las que están disponibles en
Azure, se pueden usar para iniciar y detener instancias de rol a medida que varí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 uno a uno entre las instancias de rol y los procesos de trabajo: una
sola instancia de rol puede implementar varios procesos de trabajo. Para obtener más información,
consulte el patrón de consolidación de recursos informáticos.

Guías y patrones relacionados


Los siguientes patrones y guías pueden ser pertinentes al implementar este patrón:
• Manual básico sobre la mensajería asincrónica. Las colas de mensajes son un mecanismo de
comunicaciones asincrónicas. Si un servicio de consumidor debe enviar una respuesta a una
aplicación, podría ser necesario implementar alguna forma de mensajes de respuesta. El manual
básico sobre la mensajería asincrónica proporciona información sobre cómo implementar mensajes
de solicitud/respuesta mediante las colas de mensajes.
• Guía de escalado automático. Podría ser posible iniciar y detener las instancias de un servicio
de consumidor, ya que la longitud de los mensajes de las publicaciones de las aplicaciones 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 informáticos. Es posible consolidar varias instancias de un
servicio de consumidor en un solo proceso para reducir los costos y la sobrecarga administrativa.
El patrón de consolidación de recursos informáticos describe los beneficios y las compensaciones
de este enfoque.
• Patrón de nivelación de carga basado en cola. Cuando se introduce una cola de mensajes, puede
aumentar la resistencia del sistema, lo que permite que las instancias de servicio controlen
volúmenes muy variados de solicitudes desde las 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 basado en cola
describe este escenario con más detalle.
Este patrón tiene asociada una aplicación de ejemplo.

Patrón de consolidación de recursos informáticos


Consolide varias tareas u operaciones en una sola unidad computacional. De esta manera, puede
aumentar el uso de los recursos informáticos y reducir los costos y la sobrecarga administrativa que
se asocian a la realización del procesamiento de cálculos en aplicaciones hospedadas en la nube.

Contexto y problema
A menudo, una aplicación de nube implementa una variedad de operaciones. En algunas soluciones,
se recomienda seguir el principio de diseño de separación inicial de los problemas y dividir estas
operaciones en unidades computacionales diferentes que se hospedan e implementan de manera
individual (por ejemplo, como roles independientes de App Service Web Apps, de Virtual Machines
o de Cloud Services). Sin embargo, si bien esta estrategia puede ayudar a simplificar el diseño lógico
de la solución, implementar un gran número de unidades computacionales como parte de la misma
aplicación puede aumentar los costos de hospedaje en tiempo de ejecución y hacer que administrar
el sistema sea una operación más compleja.

148 CAPÍTULO 6 | Catálogo de patrones


Por ejemplo, en la figura se muestra la estructura simplificada de una solución hospedada en la nube
que se implementó 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 independiente
(denominada desde Tarea A hasta Tarea E) en su propia unidad computacional.

Cada unidad computacional consume recursos cobrables, incluso cuando está inactiva o se usa muy
poco. Por lo tanto, esta no siempre es la solución más rentable.

En Azure, esta preocupación se aplica a los roles de Cloud Services, App Services y Virtual Machines.
Estos elementos se ejecutan en su propio entorno virtual. No se realiza un uso eficiente de los
recursos si se ejecuta una colección de roles, sitios web o máquinas virtuales independientes 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 costos, aumentar el uso, 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 se pueden agrupar según criterios basados en las características que proporciona el
entorno y los costos asociados con estas características. Un enfoque común consiste en buscar
tareas que tengan un perfil similar en lo que se refiere a la escalabilidad, la duración y los requisitos
de procesamiento. Al agruparlos, se pueden escalar como una unidad. La elasticidad que ofrecen
muchos entornos de nube permite iniciar y detener las 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 una instancia de Cloud Services, App Services y Virtual Machines. Para
obtener más información, consulte Guía de escalado automático.
Como contraejemplo, para ver cómo se puede usar la escalabilidad para determinar las operaciones
que no se deberían agrupar, considere las dos tareas siguientes:
• La Tarea 1 hace 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 implicar que hay que iniciar y detener una gran
cantidad de instancias de la unidad computacional. Aplicar el mismo escalado a la primera tarea
simplemente haría que hubiese más tareas que recibirían mensajes infrecuentes en la misma cola, lo
que sería un desperdicio de recursos.
En muchos entornos de nube, es posible especificar los recursos disponibles para una unidad
computacional en términos de la cantidad de núcleos de CPU, la memoria, el espacio en disco, etc.

149 CAPÍTULO 6 | Catálogo de patrones


Por lo general, cuantos más recursos se especifiquen, mayor será el costo. Para ahorrar dinero, es
importante maximizar el trabajo que realiza una unidad computacional costosa y no dejar que 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 unidad computacional única que proporcione la potencia
necesaria. Sin embargo, es importante equilibrar esta necesidad para mantener ocupados los
recursos más costosos y la contención que se podría producir si se sobrecargan demasiado. Por
ejemplo, las tareas de larga duración y que consumen muchos recursos informáticos no deben
compartir la misma unidad computacional.

Problemas y consideraciones
Considere los puntos siguientes cuando implemente este patrón:

Escalabilidad y elasticidad. Muchas soluciones de nube implementan escalabilidad y elasticidad


en el nivel de la unidad computacional mediante el inicio y la detención de las instancias de las
unidades. Evite las tareas de agrupación que tengan requisitos de escalabilidad en conflicto en la
misma unidad computacional.
Duración. La infraestructura de nube recicla periódicamente el entorno virtual que hospeda
una unidad computacional. Cuando hay muchas tareas de larga ejecución dentro de una unidad
computacional, es posible que sea necesario configurar la unidad para impedir que se recicle hasta
que estas tareas se completen. De manera alternativa, puede diseñar las tareas mediante un enfoque
dirigido a las comprobaciones que les permita detenerse de manera limpia 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,
es posible que sea necesario detener la unidad computacional que hospeda el código actualizado,
volver a configurarla, volver a implementar y, luego, reiniciarla. Este proceso también requerirá que
todas las demás tareas de la misma unidad computacional se detengan, se vuelvan a implementar y
se reinicien.
Seguridad. Puede que las tareas de la misma unidad computacional compartan el mismo contexto
de seguridad y puedan tener acceso a los mismos recursos. Debe haber un alto grado de confianza
entre las tareas y la seguridad de que una tarea no va a dañar ni afectar negativamente a otra.
Además, el creciente número de tareas que se ejecutan en una unidad computacional aumenta la
superficie expuesta a ataques de la unidad. Cada tarea tiene la misma seguridad que la que posee
más vulnerabilidades.
Tolerancia a errores. Si una de las tareas de una unidad computacional presenta un error o no tiene
un comportamiento normal, puede afectar a las demás tareas que se ejecutan en la misma unidad.
Por ejemplo, si una tarea no se inicia correctamente, puede producir un error en toda la lógica de
inicio de la unidad computacional y evitar que se ejecuten otras tareas de la misma unidad.
Contención. Evite introducir contención entre las tareas que compiten por los recursos de la misma
unidad computacional. Idealmente, las tareas que comparten la misma unidad computacional deben
tener otras características de uso de recursos. Por ejemplo, dos tareas que requieren gran cantidad
de recursos informáticos probablemente no deben residir en la misma unidad computacional, así
como tampoco dos tareas que consumen grandes cantidades de memoria. Sin embargo, mezclar
una tarea que requiere gran cantidad de recursos informáticos 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
costos asociados a la ejecución simultánea de varias tareas. Requiere una planificación y una
supervisión cuidadosas para asegurarse de que siga siendo 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 como se describe en la guía de partición de procesos) podría ser una mejor opción.

Problemas y consideraciones
Use este patrón para las tareas que no son rentables si se ejecutan en sus propias unidades
computacionales. Si una tarea pasa inactiva gran parte del tiempo, puede resultar costoso ejecutarla
en una unidad dedicada.
Es posible que este patrón no sea adecuado para las tareas que realizan operaciones tolerante a
errores críticas o las tareas que procesan datos altamente confidenciales o privados y que requieren
su propio contexto de seguridad. Estas tareas se deben ejecutar en su propio entorno aislado, en una
unidad computacional independiente.

Ejemplo

Si compila un servicio en la nube en Azure, es posible consolidar el procesamiento que varias


tareas realizan en un solo rol. Se trata habitualmente de un rol de trabajo 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 web. Esta técnica permite reducir los costos y simplificar la implementación, aunque puede afectar
a la escalabilidad y la capacidad de respuesta de la interfaz pública que el rol web proporciona. En el
artículo sobre cómo combinar varios roles de trabajo de Azure en un rol 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 web.

El rol es responsable de iniciar y detener las tareas. Cuando el controlador del tejido de Azure carga
un rol, se genera el evento Start del rol. Puede anular el método OnStart de la clase WebRole o
WorkerRole para controlar este evento, quizás 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 las solicitudes. Puede
encontrar más información y guías sobre cómo usar 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 Migración de
aplicaciones a la nube.

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
las solicitudes de red que recibe hasta que se complete este método.

Una vez que se complete el método OnStart, el rol ejecutará el método Run. En este punto, el
controlador de tejido puede empezar a enviar solicitudes al rol.

Ubique el código que realmente crea las tareas en el método Run. Tenga en cuenta que el método
Run define la duración de la instancia de rol. Cuando se complete este método, el controlador de
tejido se encargará de que se cierre el rol.

Cuando un rol se cierra o se recicla, el controlador de tejido impide que se reciba cualquier otra
solicitud entrante proveniente del equilibrador de carga y genera el evento Stop. Para capturar este
evento, invalide 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 se realice en el método OnStop se debe completar en 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 paralizó y lo forzará a detenerse.
Las tareas se inician mediante el método Run, que espera a que las tareas se completen. Las
tareas implementan la lógica de negocios del servicio en la nube y pueden responder a mensajes
publicados en el rol mediante Azure Load Balancer. En la figura se muestra el ciclo de vida de las
tareas y los recursos de un rol de un servicio en la nube 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 en la nube 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 distintas tareas en el mismo
rol de trabajo. El siguiente código muestra MyWorkerTask1. Se trata de una tarea simple que está
inactiva durante 30 segundos y luego produce un mensaje de seguimiento. La tarea repite este
proceso hasta que se cancela. 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, pueden seguir esta misma estructura, excepto en que deberá ubicar su propia lógica
de procesamiento en el cuerpo del bucle que espera la solicitud de cancelación.

153 CAPÍTULO 6 | Catálogo de patrones


Después de que el rol de trabajo haya inicializado los recursos que usa, el método Run inicia las dos
tareas de manera simultánea, tal 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 finalizar (espera un máximo de cinco minutos antes de terminar). Si una tarea presenta un
error 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 control de excepciones y supervisión completas en el
método Run, como el reinicio de las tareas con errores o la inclusión del 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). Para detener sin problemas cada tarea,
el código la cancela. 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 se termina.

// 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 pertinentes al implementar este patrón:

• Guía de escalado automático. El escalado automático se puede usar para iniciar y detener
instancias de los recursos informáticos de hospedaje de servicios, en función de la demanda de
procesamiento prevista.
• Guía de partición de procesos. Describe cómo asignar los servicios y los componentes de
un servicio en la nube de una manera que ayuda a minimizar los costos, 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, use un almacén solo para
anexar para registrar toda la serie de acciones que se realizan en esos datos. El almacén actúa como
sistema de registro y se puede usar para materializar los objetos de dominio. Esto puede simplificar
las tareas en dominios complejos, evitando la necesidad de sincronizar el modelo de datos y el
dominio del negocio, a la vez que se mejora el rendimiento, la escalabilidad y la capacidad de
respuesta. También puede proporcionar coherencia en los datos transaccionales y mantener pistas de
auditoría completas y un historial que permitan acciones compensatorias.

Contexto y problema
La mayoría de las aplicaciones trabaja con datos. El enfoque típico es que la aplicación mantenga el
estado actual de los datos al actualizarlos 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 ellos y
la actualización del estado actual de los datos con los valores nuevos, a menudo mediante el uso de
transacciones que bloquean los datos.
El enfoque CRUD tiene algunas limitaciones:
• Los sistemas CRUD hacen operaciones de actualización directamente en un almacén de datos,
lo que puede ralentizar el rendimiento y la capacidad de respuesta, además de limitar la
escalabilidad, debido a la sobrecarga de procesamiento que requiere.
• 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 independiente, el historial se pierde.
Para comprender en mayor profundidad los límites del enfoque CRUD, consulte CRUD, solo cuando
puede permitírselo.

Solución
El patrón de abastecimiento de eventos define un enfoque del control de las operaciones en datos
que está basados en una secuencia de eventos, cada uno de los cuales se registra en un almacén
solo para anexar. El código de la aplicación envía una serie de eventos que obligatoriamente describe
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 (el origen
de datos autorizado) sobre el estado actual de los datos. El almacén de eventos habitualmente
publica estos eventos para que los consumidores reciban notificaciones y puedan controlarlos en
caso de que sea necesario. Por ejemplo, los consumidores podrían iniciar las tareas que se aplican a
las operaciones en los eventos en otros sistemas, o bien 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 las 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 usa para rellenar las partes de la interfaz de usuario. A medida que la
aplicación agrega pedidos nuevos, agrega o quita artículos del pedido y agrega información de
envío, los eventos que describen estos cambios se pueden controlar y usar para actualizar la
vista materializada.

156 CAPÍTULO 6 | Catálogo de patrones


Además, en cualquier punto, las aplicaciones pueden leer el historial de eventos y usarlo para
materializar el estado actual de una entidad mediante la reproducción y el consumo de todos los
eventos relacionados con dicha entidad. Esto puede producirse a petición para materializar un objeto
de dominio cuando se controla una solicitud, o bien por medio de una tarea programada para que
el estado de la entidad se pueda almacenar como una vista materializada para admitir la capa de
presentación.
La figura muestra un resumen del patrón e incluye algunas de las opciones para usar el flujo de
eventos, como crear una vista materializada, integrar los eventos con aplicaciones y sistemas
externos 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 con una operación solo para anexar. La interfaz
de usuario, el flujo de trabajo o el proceso que iniciaron un evento pueden continuar y las tareas
que controlan los eventos se pueden ejecutar en segundo plano. Esto, en combinación con el hecho
de que no hay ninguna contención durante el procesamiento de las transacciones, puede mejorar
considerablemente el rendimiento y la escalabilidad de las aplicaciones, en especial para el nivel de
presentación o la interfaz de usuario.
Los eventos son objetos simples que describen alguna acción que produjo junto con cualquier dato
asociado que se requiere para describir la acción que representa el evento. Los eventos no actualizan
directamente un almacén de datos. Simplemente se registran para administrarlos en el momento
adecuado. Esto puede simplificar la implementación y la administración.
Los eventos habitualmente tienen significado para un experto en dominios, mientras que la falta
de correspondencia de la impedancia de las bases de datos de objetos relaciones 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 generado.
El abastecimiento de eventos puede ayudar a impedir que las actualizaciones simultáneas provoquen
conflictos, porque evita la necesidad de actualizar directamente los objetos en el almacén de
datos. Sin embargo, de todos modos hay que diseñar el modelo de dominio para protegerlo de las
solicitudes que podrían resultar un estado incoherente.

157 CAPÍTULO 6 | Catálogo de patrones


El almacenamiento solo para anexar de los eventos proporciona una pista de auditoría que se puede
usar para supervisar las acciones que se realizan respecto de un almacén de datos, regenerar el
estado actual como vistas materializadas o proyecciones mediante la reproducción de los eventos en
cualquier momento, y ayudar a probar y depurar el sistema. Además, el requisito de usar eventos de
compensación para cancelar cambios proporciona un historial de los cambios que se han revertido,
lo que no podría realizarse si el modelo simplemente almacenara el estado actual. La lista de eventos
también se puede usar 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 para responder a esos eventos.
Este desacoplamiento de las tareas de los eventos proporciona flexibilidad y extensibilidad. Las tareas
conocen el tipo de evento y los datos del evento, pero no la operación que desencadenó el evento.
Además, son varias las tareas que pueden controlar cada evento. Esto permite una integración sencilla
con otros servicios y sistemas que solo reciben los eventos nuevos que genera el almacén de eventos.
Sin embargo, los eventos de abastecimiento de eventos suelen ser de muy bajo nivel y, en su lugar,
podría ser necesario generar eventos de integración específicos.
El abastecimiento de eventos habitualmente se combina con el patrón CQRS mediante la realización de
las tareas de administración de datos en respuesta a los eventos y por medio de la materialización de
las vistas desde los eventos almacenados.

Problemas y consideraciones
Considere los siguientes puntos en el momento de decidir cómo implementar este patrón:
El sistema solo tendrá coherencia final cuando se creen vistas materializadas o se generen proyecciones
de datos mediante la reproducción de eventos. Hay cierto retraso entre el momento en que una
aplicación agrega eventos al almacén de eventos como resultado del control de una solicitud, la
publicación de los eventos y el control de estos por parte de los consumidores. Durante este período, es
posible que nuevos eventos hayan llegado al almacén de eventos, los que describen más cambios en las
entidades.

Notas:
Consulte el Manual básico de coherencia de datos para obtener información sobre la 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
agregar un evento de compensación al almacén de eventos. Si necesita cambiar el formato (en lugar
de los datos) de los eventos persistentes, tal vez durante una migración, puede ser difícil combinar los
eventos existentes del almacén con la versión nueva. Es posible que sea necesario recorrer en iteración
todos los eventos, realizando cambios para que sean compatibles con el nuevo formato, o bien agregar
nuevos eventos que usen el formato nuevo. Considere la posibilidad de usar de una marca de versión en
cada versión del esquema de eventos para mantener tanto el formato de evento antiguo como el nuevo.
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
fundamental, al igual que el orden de los eventos que afectan a una entidad específica (el orden en
que se producen los cambios en una entidad afecta a su estado actual). Agregue una marca de tiempo
a cada evento para evitar problemas. Otra práctica común es comentar cada evento que resulte a partir
de una solicitud con un identificador incremental. Si dos acciones intentan agregar 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 existen mecanismos, como consultas SQL, para leer los
eventos y obtener información. Los únicos datos que se pueden extraer es un flujo de eventos con
un identificador de eventos como criterio. El ID del evento habitualmente se asigna a entidades
individuales. El estado actual de una entidad solo se puede determinar mediante la reproducción de
todos los eventos que se relacionan 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 la administración y actualización del sistema. Si los flujos
son grandes, considere la posibilidad de crear instantáneas a intervalos específicos, como a un
determinado número de eventos. El estado actual de la entidad se puede obtener de la instantánea
y mediante la reproducción de cualquier evento que se haya producido después de ese momento
específico. 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 Replicación de instantáneas
maestro-subordinado.
Si bien el abastecimiento de eventos reduce al mínimo la posibilidad de que haya actualizaciones en
conflicto de los datos, la aplicación aún debe ser capaz de tratar 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
resulta en el requisito de conciliar las dos operaciones, ya sea informando al cliente o creando un
pedido pendiente.
La publicación del evento podría realizarse "al menos una vez" y, por lo tanto, los consumidores de
los eventos deben ser idempotentes. No deben volver a aplicar la actualización que se describe en
un evento si el evento se controla más de una vez. Por ejemplo, si varias instancias de un consumidor
mantienen el agregado de la propiedad de una entidad, como el número total de pedidos realizados,
solo una debe lograr incrementar ese agregado cuando se produce un evento de realización de un
pedido. Aunque esta no es una característica clave del abastecimiento de eventos, es la decisión de
implementación habitual.

Cuándo utilizar este patrón


Use este patrón en los siguientes escenarios:

• Cuando desee capturar la intención, el propósito o el motivo en los datos. Por ejemplo, los
cambios en una entidad se pueden capturar como una serie de tipos de eventos específicos,
como Cambio de casa, Cierre de cuenta o Defunción.
• Cuando sea fundamental minimizar o evitar por completo la aparición de actualizaciones en
conflicto de los datos.
• Cuando desee registrar los eventos que se producen y poder reproducirlos a fin de restablecer
el estado de un sistema, revertir los cambios o mantener un historial y un registro de auditoría.
Por ejemplo, cuando una tarea implica varios pasos, es posible que necesite ejecutar acciones
para revertir las actualizaciones y, luego, reproducir 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 esfuerzo de desarrollo o implementación adicional.
• Cuando sea necesario desacoplar el proceso de introducción o actualización de los datos de
las tareas que se requieren para aplicar estas acciones. Esto se podría realizar para mejorar el
rendimiento de la interfaz de usuario o para distribuir eventos a otros agentes de escucha que
actúan cuando se producen los eventos. Por ejemplo, mediante la integración de un sistema
de nóminas con un sitio web de envío de gastos, con el fin de que los eventos que el almacén
de datos genera en respuesta a las actualizaciones de datos que se realicen en el sitio web, los
consuma tanto el sitio web con el sistema 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, si se usa en conjunto con CQRS, se necesita
adaptar un modelo de lectura o las vistas que exponen los datos.
• Cuando se usa junto con CQRS y la coherencia final es aceptable mientras se actualiza un
modelo de lectura, o bien es aceptable el impacto que la rehidratación de entidades y datos de
un flujo de eventos tiene en el rendimiento.

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 negocios o
sistemas sin dominios que funcionan bien de manera natural con los 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 pistas de auditoría, historiales ni funcionalidades para revertir y
reproducir acciones.
• Sistemas donde la aparición de actualizaciones en conflicto de los datos subyacentes es muy
limitada. Por ejemplo, sistemas que principalmente agregan datos en lugar de actualizarlos.

Ejemplo
Un sistema de administración de conferencias debe hacer un seguimiento del número de reservas
completadas para una conferencia a fin de poder comprobar si todavía quedan asientos disponibles
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 las reservas como una
entidad independiente en una base de datos que contiene información de reservas. A medida
que se realizan o cancelan reservas, el sistema puede incrementar o disminuir este número según
sea necesario. Este enfoque en teoría es sencillo, pero puede causar problemas de escalabilidad
si un gran número de asistentes intenta realizar reservas durante un corto período de tiempo.
Por ejemplo, el último día antes del cierre del 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 reproducir estos eventos para calcular el
número de asientos disponibles. 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 eventos sobre reservas y cancelaciones nunca se
modifica.

En el diagrama siguiente se muestra cómo podría implementarse el subsistema de reservas de


asientos del sistema de administración de conferencias mediante el abastecimiento de eventos.

160 CAPÍTULO 6 | Catálogo de patrones


La secuencia de acciones para reservar dos asientos es la siguiente:

1. La interfaz de usuario emite un comando para reservar asientos para dos asistentes. El comando
lo administra un controlador de comandos independiente. Parte de la lógica que se desacopla de
la interfaz de usuario y es responsable de controlar las solicitudes publicadas como comandos.
2. Se construye un agregado que contiene información sobre todas las reservas para la conferencia
mediante la consulta de los eventos que describen las reservas y las cancelaciones. Este
agregado se llama SeatAvailability y está contenido dentro de un modelo de dominio que
expone métodos para consultar y modificar los datos en el agregado.
Algunas optimizaciones que se deben tener en cuenta son el uso de instantáneas (de modo que
no necesite consultar y reproducir la lista completa de eventos para obtener el estado actual del
agregado) y el mantenimiento de una copia en caché del agregado in-memory.
3. El controlador de comandos invoca a un método que el modelo de dominio expone para realizar
las reservas.

4. El agregado SeatAvailability registra un evento que contiene el número de asientos que se


reservó. La próxima vez que el agregado aplique eventos, todas las reservas se usarán para
calcular cuántos asientos quedan.

5. El sistema anexa 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 un asiento, el sistema sigue un proceso similar, excepto en que el controlador
de comandos emite un comando que genera un evento de cancelación de asiento y lo anexa al
almacén de eventos.
Además de aumentar el ámbito de la escalabilidad, usar un almacén de eventos proporciona también
un historial completo, o una pista de auditoría, de las reservas y cancelaciones para una conferencia.
Los eventos del almacén de eventos son el registro exacto. No es necesario que los agregados
persistan de otra manera, porque el sistema puede reproducir fácilmente los eventos y restablecer el
estado a cualquier momento dado.
Puede 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 pertinentes al implementar este patrón:
• Patrón CQRS (segregación de responsabilidad de consultas y comandos). El almacén de escritura
que proporciona la fuente permanente de información para una implementación de CQRS a
menudo está basado en una implementación del patrón de abastecimiento de eventos. Describe
cómo segregar las operaciones que leen los datos en una aplicación de las operaciones que
actualización los datos mediante interfaces separadas.
• Patrón de vistas materializadas. El almacén de datos que se usa en un sistema basado en el
abastecimiento de eventos habitualmente no es adecuado para realizar consultas eficientes. En
cambio, un enfoque común es generar vistas de los datos rellenadas previamente a intervalos
regulares o cuando cambian los datos. Muestra cómo se puede hacer esto.
• Patrón de transacción compensatoria. Los datos existentes en un almacén de abastecimiento de
eventos no se actualizan; en su lugar, se agregan nuevas entradas que sirven para la transición
del estado de las entidades a los nuevos valores. Para revertir un cambio, se usan las entradas
de compensación, ya que no es posible simplemente revertir el cambio anterior. Describe cómo
deshacer el trabajo que una operación anterior realizó.
• Manual básico de coherencia de datos. Cuando se usa el abastecimiento de eventos con
un almacén de lectura o vistas materializadas independientes, los datos de lectura no
serán coherentes de inmediato, sino que solo tendrán coherencia final. Resume los asuntos
relacionados con el mantenimiento de la coherencia en los datos distribuidos.
• Guía de partición de datos. Los datos a menudo se particionan cuando el abastecimiento de
eventos se usa 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.
• Publicación de Greg Young: ¿Por qué usar el abastecimiento de eventos?

Patrón del almacén de configuración externa


Mueva la información de configuración del paquete de implementación de la aplicación hacia
una ubicación centralizada. Este patrón puede proporcionar más oportunidades para facilitar la
administración y el control de datos de los datos de configuración y para compartir los datos de
configuración en aplicaciones e instancias de aplicación.

Contexto y problema
La mayoría de los entornos de tiempo de ejecución de las aplicaciones incluye información de
configuración que está contenida en los archivos implementados con la aplicación. En algunos
casos, es posible editar estos archivos para cambiar el comportamiento de la aplicación una vez
implementada. Sin embargo, los cambios en la configuración requieren que se vuelva a implementar
la aplicación, lo que a menudo genera un tiempo de inactividad inaceptable y otro tipo de
sobrecarga administrativa.

162 CAPÍTULO 6 | Catálogo de patrones


Los archivos de configuración local también limitan la configuración a una sola aplicación, pero a
veces podría ser útil compartir las opciones 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 usa un conjunto de aplicaciones relacionado.
Es todo un desafío administrar los cambios en las configuraciones locales en varias instancias en
ejecución de la aplicación, en especial si se trata de un escenario hospedado en la nube. Esto puede
resultar en que las instancias usen distintas opciones de configuración mientras se implementa 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 versiones distintas 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 se pueda usar para leer y actualizar de manera rápida y eficaz las opciones de configuración. El tipo
de almacén externo depende del entorno de hospedaje y de tiempo de ejecución de la aplicación. En
un escenario hospedado en la nube, habitualmente se trata de un servicio de almacenamiento basado
en la nube, pero podría tratarse de una base de datos hospedada u otro sistema.

La memoria auxiliar que elija para la información de configuración debe tener una interfaz que
proporcione un acceso constante y fácil de usar. 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 para permitir el almacenamiento de varias versiones de la configuración (como el desarrollo,
el ensayo o la producción, incluidas las diversas versiones de cada uno de estos elementos).

Muchos sistemas de configuración integrados leen los datos cuando se inicia la aplicación y los
almacenan in-memory para proporcionar un acceso rápido y minimizar el impacto en el rendimiento
de la aplicación. Según cuál sea el tipo de memoria auxiliar que se use y su latencia, podría resultar
útil implementar un mecanismo de almacenamiento en caché dentro del 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 sobre el patrón del almacén de configuración externa con una caché local
opcional.

163 CAPÍTULO 6 | Catálogo de patrones


Problemas y consideraciones

Considere los siguientes puntos en el momento de decidir cómo implementar este patrón:
Elija una memoria auxiliar que ofrezca un rendimiento aceptable, alta disponibilidad y solidez,
y que se pueda respaldar como parte del proceso de mantenimiento y administración de las
aplicaciones. En una aplicación hospedada en la nube, una buena opción es usar un mecanismo de
almacenamiento en nube para cumplir estos requisitos.
Diseñe el esquema de la memoria auxiliar para permitir la 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 escritos, colecciones de configuraciones, varias versiones de las configuraciones y
cualquier otra característica que necesiten las aplicaciones que la usan. El esquema debería ser fácil
de extender para admitir configuraciones adicionales a medida que cambian los requisitos.
Piense en las capacidades físicas de la memoria auxiliar, cómo se relaciona con la manera en que se
almacena la información de la configuración y los efectos sobre el rendimiento. Por ejemplo, para
almacenar 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 la configuración
individual. Esto hará que sea más difícil actualizar una configuración, aunque el almacenamiento en
caché de la misma 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 las
opciones de configuración. Por ejemplo, podría ser un requisito determinar el ámbito de las opciones
de la configuración en el nivel de organización, aplicación y 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 la configuración.
Asegúrese de que la interfaz de configuración puede exponer los datos de configuración en los
formatos requeridos, como valores con tipo, colecciones, pares clave/valor o contenedores de
propiedades.
Piense en cómo se comportará la interfaz del almacén de configuración cuando la configuración
contenga errores o no exista en la memoria auxiliar. Es posible que sea adecuado devolver los
ajustes predeterminados y registrar los errores. Considere también aspectos como la distinción entre
mayúsculas y minúsculas en las claves o los nombres de configuración, el almacenamiento y control
de datos binarios, y en cómo se controlan los valores nulos o vacíos.
Considere cómo proteger los datos de configuración para permitir el acceso solo a los usuarios y
las aplicaciones apropiados. Probablemente esta sea una característica de la interfaz del almacén
de configuración, pero también es necesario asegurarse de que no se pueda obtener acceso
directamente a los datos de la memoria auxiliar sin el permiso correspondiente. Asegúrese de que
haya una separación estricta entre los permisos necesarios para leer y para escribir los datos de
configuración. Piense también si necesita cifrar todos o algunos de las opciones de 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
con los mismos mecanismos que cuando se implementa el código de la aplicación. Por ejemplo,
se deben realizar los cambios que pueden afectar a más de una aplicación mediante una prueba
completa y un enfoque de implementación de ensayo para asegurar que el cambio sea apropiado
para todas las aplicaciones que usan esta configuración. Si un administrador edita una configuración
para actualizar una aplicación, podría afectar negativamente a otras aplicaciones que usan la misma
configuración.
Si una aplicación almacena en caché la información de configuración, se debe enviar una alerta a la
aplicación si cambia la configuración. Sería posible implementar una directiva de expiración sobre
los datos de la configuración en caché para que esta información se actualice automáticamente de
manera periódica y se recojan todos los cambios (y se realicen las acciones adecuadas).

164 CAPÍTULO 6 | Catálogo de patrones


Cuándo utilizar este patrón
Este patrón es útil para:

• Las opciones 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 las opciones de
configuración requeridas, como almacenar imágenes o tipos de datos complejos.
• Como un almacén complementario para algunas configuraciones de las aplicaciones, quizás
para permitir que las aplicaciones anulen algunas o todas las configuraciones almacenadas
centralmente.
• Como una manera de simplificar la administración de varias aplicaciones y, de manera opcional,
para supervisar el uso de las opciones de configuración mediante el registro de algunos o todos
los tipos de acceso al almacén de configuración.

Ejemplo

En una aplicación hospedada de Microsoft Azure, una opción típica para almacenar de manera
externa la información de configuración es usar Azure Storage. Es resistente, ofrece alto rendimiento
y se replica tres veces con conmutación por error automática para ofrecer alta disponibilidad. Azure
Table Storage proporciona un almacén de claves/valores con la capacidad de usar un esquema
flexible para los valores. Azure Blob Storage 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
Blob Storage para almacenar y exponer la información de configuración. La clase BlobSettingsStore
abstrae Blob Storage 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 las opciones de configuración que se
guardan en el almacén de configuración e incluye un número de versión que se puede usar para
detectar si las opciones de configuración se modificaron recientemente. La clase BlobSettingsStore
usa 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 solución sencilla expone todas las opciones 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 usar esta clase para almacenar y recuperar información de
configuración. Esta clase usa la biblioteca de extensiones reactivas de Microsoft para exponer los
cambios hechos en la configuración mediante una implementación de la interfaz IObservable. Si se
modifica un ajuste llamando al método SetAppSetting, se genera el evento Changed y se notificará a
todos los suscriptores de 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 tener acceso a ellos rápidamente. El método
GetSetting que se usa para recuperar una opción de configuración lee los datos de la caché. Si el
ajuste no se encuentra en la caché, se recupera del objeto BlobSettingsStore en su lugar.
El método GetSettings invoca al método CheckForConfigurationChanges para detectar si la
información de configuración de Blob Storage cambió. 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 produjo uno o más cambios, se genera el evento Changed y se actualizan las opciones de
configuración almacenados en caché en el objeto Dictionary. Esta es una aplicación del
patrón Cache-Aside.
En el ejemplo de código siguiente se muestra cómo se implementan 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 distintas configuraciones para la ejecución de una aplicación en
diferentes entornos, como los de ensayo y producción.

Un objeto ExternalConfigurationManager también puede consultar el objeto BlobSettingsStore de


manera periódica para comprobar si hay cambios. En el código siguiente, el método StartMonitor
llama a CheckForConfigurationChanges en un intervalo para detectar cualquier cambio y genera el
evento Changed, como se describió 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 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 tomó de la clase WorkerRole en el proyecto de ExternalConfigurationStore.


Cloud. Muestra cómo la aplicación usa la clase ExternalConfiguration para leer una configuración.

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


• Hay disponible un ejemplo que muestra este patrón en GitHub.

Patrón de identidad federada


Delegue la autenticación a un proveedor de identidad externo. Esto puede simplificar el desarrollo,
minimizar el requisito de administración de usuarios y mejorar la experiencia del usuario de la
aplicación.

Contexto y problema
Los usuarios habitualmente necesitan trabajar con varias aplicaciones que proporcionan y hospedan
diferentes organizaciones con las que tienen relaciones comerciales. Es posible que estos usuarios
tengan que usar credenciales específicas (y diferentes) para cada una. Esto puede:

• Provocar una experiencia de usuario inconexa. Los usuarios se olvidan a menudo de las
credenciales de inicio de sesión cuando tienen muchas distintas.
• Exponer las vulnerabilidades de seguridad. Cuando un usuario deja la empresa, la cuenta
se debe desaprovisionar de inmediato. Es fácil pasar por alto esto en organizaciones de gran
tamaño.
• Complicar la administración de usuarios. Los administradores deben administrar las
credenciales de todos los usuarios y realizar tareas adicionales, como proporcionar recordatorios
de contraseña.
Los usuarios habitualmente prefieren usar 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 usar identidades federadas. Separe la
autenticación de usuario del código de la aplicación y delegue la autenticación a un proveedor
de identidades de confianza. Esto puede simplificar el desarrollo y permitir que los usuarios se
autentiquen mediante una amplia variedad de proveedores de identidades (IdP), a la vez que se
minimiza la sobrecarga administrativa. También le permite desacoplar 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 usa cuando una aplicación cliente necesita
tener acceso a un servicio que requiere autenticación. La autenticación la realiza 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 otro tipo de información, como la pertenencia a rol y derechos de acceso más
pormenorizados.

Este modelo a menudo se denomina control de acceso basado en notificaciones. Las aplicaciones
y servicios autorizan el acceso a características y funcionalidades en función de 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 (observe 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 devolverlo 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 es responsable de tener acceso a un proveedor de
identidades para autenticar al usuario. Este enfoque es común en escenarios empresariales donde
hay un directorio y STS local.

171 CAPÍTULO 6 | Catálogo de patrones


La autenticación federada proporciona una solución basada en estándares para el problema que
implica confiar en identidades en distintos 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 las aplicaciones hospedadas
en la nube, porque admite el inicio de sesión único sin que sea necesaria una conexión de red directa
a los proveedores de identidades. No es necesario que el usuario escriba las credenciales para cada
aplicación. Con esto aumenta la seguridad, porque impide la creación de las credenciales necesarias
para tener acceso a muchas aplicaciones distintas y también oculta las credenciales del usuario
de todos los usuarios, excepto del proveedor de identidades original. Las aplicaciones solo ven la
información de 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 necesita
proporcionar características de administración de identidades. Además, en escenarios corporativos,
no es necesario que el directorio corporativo conozca al usuario si confía en el proveedor de
identidades. Con esto se quita toda la sobrecarga administrativa que implica administrar la identidad
del usuario dentro del 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 el mecanismo de administración de
identidades en los mismos centros de datos para mantener la disponibilidad y la confiabilidad de
la aplicación.
• Las herramientas de autenticación hacen posible configurar el control de acceso basado en las
notificaciones de rol que contiene el token de autenticación. A menudo, esto se conoce como
control de acceso basado en rol (RBAC) y puede permitir un nivel de control más pormenorizado
del acceso a características y recursos.
• A diferencia de un directorio corporativo, la autenticación basada en notificaciones mediante
proveedores de identidades sociales no suele proporcionar información sobre el usuario
autenticado que no sea 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. La aplicación habitualmente necesita mantener cierta información sobre
los usuarios registrados y ser capaces de hacer coincidir esta información con el identificador
contenido en las notificaciones del token. Por lo general, esto se hace a través del registro
cuando el usuario obtiene acceso por primera vez a la aplicación. Luego, la información se
inserta en el token como notificaciones adicionales después de cada autenticación.
• Si hay más de un proveedor de identidades configurado 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 hacerlo automáticamente
basándose en una dirección de correo electrónico o nombre de usuario que proporcione el
usuario, un subdominio de la aplicación a la que obtiene acceso el usuario, el ámbito de la
dirección IP del usuario o en el contenido de una cookie almacenada en el explorador del
usuario. Por ejemplo, si el usuario escribe una dirección de correo electrónico en el dominio de
Microsoft, como user@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 usar una cookie para indicar que el
último inicio de sesión fue 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 muestra los proveedores de identidades de confianza y el usuario debe seleccionar el que
desea usar.

172 CAPÍTULO 6 | Catálogo de patrones


Cuándo utilizar este patrón
Este patrón es útil para escenarios como:
• Inicio de sesión único en la empresa. En este escenario se necesita autenticar los empleados
para aplicaciones corporativas que se hospedan en la nube 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 usan 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 varios socios. En este escenario, es necesario autenticar los empleados
corporativos y socios empresariales 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 distintos sistemas de TI o recursos
compartidos.
• Identidad federada en aplicaciones SaaS. En este escenario, los fabricantes de software
independientes proporcionan un servicio listo para usar a varios clientes o inquilinos. Cada
inquilino se autentica con un proveedor de identidades adecuado. Por ejemplo, los usuarios
profesionales usarán sus credenciales corporativas, mientras que los consumidores y clientes del
inquilino usarán sus credenciales de identidad social.

Este patrón podría no ser útil en las siguientes situaciones:


• Un proveedor de identidades puede autenticar a todos los usuarios de la aplicación y no es
necesario autenticarlos con otro proveedor de identidades. Esto es típico en las aplicaciones
empresariales que usan un directorio corporativo (al que se puede tener acceso dentro de
la aplicación) para la autenticación, con una VPN o (en un escenario hospedado en la nube)
mediante una conexión de red virtual entre el directorio local y la aplicación.
• La aplicación se compiló originalmente con un mecanismo de autenticación distinto, quizá con
almacenes de usuarios personalizados, o bien no tiene la capacidad de controlar los estándares
de negociación que las tecnologías basada en notificaciones usan. Retroadaptar la autenticación
basada en notificaciones y el control de acceso en las aplicaciones existentes puede ser complejo
y probablemente no sea rentable.

Ejemplo
Una organización hospeda una aplicación multiinquilino 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 que los inquilinos tengan acceso 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 explorador del cliente reenvía este token al proveedor de federación de la aplicación SaaS, que
confía en los tokens que emite el ADFS del inquilino, para devolver un token válido para el proveedor
de federación de SaaS (paso 2). Si es necesario, el proveedor de federación de SaaS realiza una
transformación de las notificaciones en el token en notificaciones que la aplicación reconoce (paso
3) antes de devolver el token nuevo al explorador del cliente. La aplicación confía en los tokens que
emite el proveedor de federación de SaaS y usa las notificaciones en el token para aplicar las reglas
de autorización (paso 4).
Los inquilinos no necesitan recordar credenciales distintas para tener acceso a la aplicación y un
administrador de la empresa del inquilino puede configurar la lista de los usuarios que pueden tener
acceso a la aplicación en su propio ADFS.

Guía relacionada
• Microsoft Azure Active Directory
• Active Directory Domain Services
• Servicios de federación de Active Directory
• Administración de identidades para aplicaciones multiinquilino en Microsoft Azure
• Aplicaciones multiinquilino en Azure

Patrón del equipo selector (gatekeeper)


Proteja aplicaciones y servicios mediante el uso de una instancia de host dedicado que actúe como
un intermediario entre clientes y la aplicación o servicio, valide y desinfecte las solicitudes, y apruebe
las solicitudes y los datos entre ellos. Puede proporcionar una capa adicional de seguridad y limitar la
superficie expuesta a ataques del sistema.

Contexto y problema
Las aplicaciones exponen su funcionalidad a los clientes cuando aceptan y procesan las solicitudes.
En escenarios hospedados en la nube, las aplicaciones exponen los puntos de conexión a los que se
conectan los clientes y habitualmente incluyen el código para controlar las solicitudes de los clientes.
Este código realiza la autenticación y la validación, parte o la totalidad del procesamiento de las
solicitudes y es probable que tenga acceso al almacenamiento y otros servicios en nombre del cliente.
Si un usuario malintencionado puede comprometer el sistema y obtener acceso al entorno de
hospedaje de la aplicación, quedan expuestos los mecanismos de seguridad que usa, como
credenciales y claves de almacenamiento, y los servicios y datos a los que tiene acceso. 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 obtengan acceso a la información confidencial y a los
servicios, desacople los hosts o las tareas que exponen los puntos de conexión públicos del código
que procesa las 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 las tareas que controlarán la solicitud. La figura ofrece una descripción de
alto nivel de este patrón.

174 CAPÍTULO 6 | Catálogo de patrones


Este patrón se puede usar sencillamente para proteger el almacenamiento, o bien como una fachada
más completa para proteger todas las funciones de la aplicación. Los factores importantes son:
• Validación controlada. El equipo selector (gatekeeper) valida todas las solicitudes y rechaza las
que no cumplen con los requisitos de validación.
• Exposición y riesgo limitados. El equipo selector no tiene acceso a las credenciales o claves
que usa el host de confianza para tener acceso al almacenamiento y los servicios. Si el equipo
selector se ve comprometido, el atacante no obtiene acceso a estas credenciales o claves.
• Seguridad apropiada. El equipo selector se ejecuta en un modo con privilegios limitados,
mientras que el resto de la aplicación se ejecuta en el modo de plena confianza que es necesario
para tener acceso al almacenamiento y los servicios. Si el equipo selector se ve comprometido,
no puede tener acceso directo a los datos o los servicios de aplicaciones.
Este patrón actúa como un firewall en una topografía de red típica. Permite que el equipo selector
examine las solicitudes y tome una decisión sobre si pasar la solicitud al host de confianza (que a veces
se denomina maestro de claves) que realiza las tareas requeridas. Esta decisión habitualmente requiere
que el equipo selector valide y corrija el contenido de la solicitud antes de pasarla al host de confianza.

Problemas y consideraciones
Considere los siguientes puntos en el momento de decidir cómo implementar este patrón:

• Asegúrese de que los hosts de confianza al que el equipo selector pasa las solicitudes solo
exponga los puntos de conexión internos o protegidos y conéctese solo al equipo selector. Los
hosts de confianza no deberían exponer ninguna interfaz ni punto de conexión externo.
• El equipo selector se debe ejecutar en el modo con privilegios limitados. Esto habitualmente
significa que el equipo selector y el host de confianza se ejecutan en máquinas virtuales o
servicios hospedados independientes.
• El equipo selector no debe realizar ningún procesamiento relacionado con las aplicaciones o
los servicios ni tampoco tener acceso a los datos. Su función es simplemente validar y corregir
las solicitudes. Puede que los hosts de confianza deban realizar una validación adicional de las
solicitudes, pero la validación principal debe hacerla el equipo selector.
• Usa un canal de comunicación segura (HTTPS, SSL o TLS) entre el equipo selector y los hosts de
confianza o las tareas donde esto es posible. Sin embargo, algunos entornos de hospedaje no
admiten HTTPS en los puntos de conexión internos.
Es probable agregar una capa adicional a la aplicación para implementar el patrón del equipo
selector 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 del equipo selector 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 asegurar la capacidad a fin de mantener la disponibilidad.

Cuándo utilizar este patrón


Este patrón es útil para:
• Aplicaciones que controlan información confidencial, exponen servicios que deben tener un alto
grado de protección contra ataques maliciosos, o bien realizan operaciones críticas que no se
deben interrumpir.
• Aplicaciones distribuidas en las que es necesario realizar la validación de solicitudes por
separado de las tareas principales o centralizar esta validación para simplificar el mantenimiento
y la administración.

Ejemplo
En un escenario hospedado en la nube, este patrón se puede implementar si se desacopla el equipo
selector o la máquina virtual de los roles y servicios de confianza de una aplicación. Para ello, usa un
punto de conexión interno, una cola o el almacenamiento como un mecanismo de comunicación
intermedio En la figura se ilustra el uso de un punto de conexión interno.

Patrones relacionados
El patrón de clave de valet también podría ser pertinente cuando se implementa el patrón del equipo
selector. Al comunicarse entre el equipo selector y los roles de confianza, se recomienda mejorar
la seguridad mediante el uso de claves o tokens que limitan los permisos para tener acceso a los
recursos.

Patrón de agregación de gateway


Use un gateway para agregar varias solicitudes individuales a una sola solicitud. Este patrón es útil
cuando un cliente debe hacer varias llamadas a distintos sistemas de back-end para realizar una
operación.

176 CAPÍTULO 6 | Catálogo de patrones


Contexto y problema
Para realizar una sola tarea, es posible que un cliente tenga que hacer varias llamadas a distintos
servicios de back-end. Una aplicación que se basa en muchos servicios para realizar una tarea
debe dedicar recursos a cada solicitud. Cuando se agrega una nueva característica o servicio a la
aplicación, se necesitan solicitudes adicionales, lo que aumenta aún más las necesidades de recursos
y llamadas de red. Esta consumo entre un cliente y un back-end puede afectar negativamente el
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 creadas en torno a muchos servicios más
pequeños tienen, 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, usar las solicitudes individuales de esta manera no resulta eficaz y se podría
interrumpir la conectividad o generar solicitudes incompletas. Si bien es posible realizar cada
solicitud en paralelo, la aplicación debe enviar, esperar y procesar los datos para cada solicitud, todo
en conexiones independientes, lo que aumenta la posibilidad de que se produzca un error.

Solución
Use un gateway para disminuir el consumo entre el cliente y los servicios. El gateway recibe
solicitudes de los clientes, envía solicitudes a los diferentes sistemas de back-end y luego agrega los
resultados y los devuelve al cliente que genera la solicitud.

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 al gateway (1). La solicitud contiene un
paquete de solicitudes adicionales. El gateway los descompone y procesa cada solicitud enviándola
al servicio correspondiente (2). Cada servicio devuelve una respuesta al gateway (3). El gateway
combina las respuestas de cada servicio y envía la respuesta a la aplicación (4). La aplicación hace
una sola solicitud y recibe una única respuesta por parte del gateway.

177 CAPÍTULO 6 | Catálogo de patrones


Problemas y consideraciones
• El gateway no debe introducir el acoplamiento de servicios en los servicios de back-end.
• El gateway se debe ubicar cerca de los servicios de back-end para reducir la latencia cuanto sea
posible.
• El servicio de gateway puede introducir un único punto de error. Asegúrese de que el gateway
esté diseñado correctamente para satisfacer los requisitos de disponibilidad de la aplicación.
• El gateway puede introducir un cuello de botella. Asegúrese de que el gateway tiene un
rendimiento suficiente para controlar la carga y se puede escalar cumplir con el crecimiento
esperado.
• Realice pruebas de carga en el gateway para asegurarse de que no introducir errores en cascada
en los servicios.
• Implemente un diseño resistente, mediante técnicas como cierres, interrupciones de circuitos,
reintentos y tiempos de espera.
• Si una o más llamadas al servicio tarda demasiado, puede ser aceptable que se agote el tiempo
de espera y se devuelva un conjunto parcial de datos. Piense cómo la aplicación controlará este
escenario.
• Use una E/S asincrónica para asegurarse de que un retraso en el back-end no cause problemas
de rendimiento en la aplicación.
• Implemente el seguimiento distribuido con ID de correlación para hacer el seguimiento de cada
llamada individual.
• Supervise las métricas de solicitud y los tamaños de las respuestas.
• Considere devolver los datos en caché como estrategia de conmutación por error para controlar
los errores.
• En lugar de compilar la agregación en el gateway, considere ubicar un servicio de agregación
detrás del gateway. Es probable que la agregación de solicitudes tenga requisitos de recursos
distintos de los otros servicios en el gateway y podría afectar la funcionalidad de descarga y
enrutamiento del gateway.

178 CAPÍTULO 6 | Catálogo de patrones


Cuándo utilizar este patrón
Use este patrón cuando:
• Un cliente necesite comunicarse con varios servicios de back-end para realizar una operación.
• El cliente puede usar redes con latencia considerable, como las redes móviles.

Es posible que este patrón no sea adecuado cuando:

• Desea reducir el número de llamadas entre un cliente y un servicio único a través de varias
operaciones. En ese escenario, es posible que sea mejor agregar una operación por lotes al
servicio.
• El cliente o la aplicación se ubica 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 gateway 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


Guía relacionada

• Patrón de back-ends para front-ends


• Patrón de descarga de gateway
• Patrón de enrutamiento de gateway

Patrón de descarga de gateway


Descargue una funcionalidad de servicio compartido o especializado en un proxy de gateway.
Este patrón puede simplificar el desarrollo de aplicaciones si mueve la funcionalidad de servicio
compartido, como el uso de certificados SSL, de otras partes de la aplicación al gateway.

Contexto y problema
Algunas características son de uso general en los distintos servicios y estas características 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 error de implementación. Cualquier actualización a una característica compartida se
debe implementar a través de todos los servicios que comparten esa característica.
Controlar adecuadamente los problemas de seguridad (validación de token, cifrado, administración
de certificados SSL) y otras tareas complejas puede requerir que los miembros del equipo
tengan aptitudes altamente especializadas. Por ejemplo, un certificado que una aplicación
necesita debe estar configurado e implementado en todas las instancias de aplicación. Con cada
nueva implementación, es necesario controlar el certificado para asegurarse de que no expire.
Todo certificado común que esté por expirar se debe actualizar, probar y comprobar en cada
implementación de la aplicación.
Otros servicios comunes, 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. Probablemente sería mejor consolidar este tipo de funcionalidad, con el fin de
reducir la sobrecarga y la posibilidad de errores.

Solución
Descargue algunas características en un gateway de API, en especial las preocupaciones
transversales, como la administración de certificados, la autenticación, la terminación de SSL, la
supervisión, la translación de protocolos o las limitaciones. Descargue algunas características en un
gateway de API, en especial las preocupaciones transversales, como la administración de certificados,
la autenticación, la terminación de SSL, la supervisión, la translación de protocolos o las limitaciones.

En el siguiente diagrama se muestra un gateway de API que termina las conexiones SSL entrantes.
Solicita datos en nombre del solicitante original desde cualquier servidor HTTP ascendente del
gateway de API.

180 CAPÍTULO 6 | Catálogo de patrones


Los beneficios de este patrón incluyen:

• Simplificar el desarrollo de los servicios mediante la eliminación de la necesidad de distribuir y


mantener los recursos de apoyo,como certificados de servidor web y la configuración de sitios
web seguro. Una configuración más sencilla resulta en una administración y escalabilidad más
sencillas y simplifica las actualizaciones del servicio.
• Permita que equipos dedicados a implementar características que requieren conocimientos
especializados, como la seguridad. Esto permite que su equipo principal se centre en la
funcionalidad de la aplicación, dejando estas cuestiones especializadas pero transversales a los
expertos pertinentes.
• Proporcione cierta coherencia para el registro y seguimiento de solicitudes y respuestas. Incluso
si un servicio no está correctamente instrumentado, el gateway no se puede configurar para
asegurar un nivel mínimo de supervisión y registro.

Problemas y consideraciones
• Asegúrese de que el gateway API esté altamente disponible y que sea resistente a errores. Evite
los únicos puntos de error mediante la ejecución de varias instancias de su gateway API.
• Asegúrese de que el gateway está diseñado para cumplir con los requisitos de capacidad y
escalado de la aplicación y sus puntos de conexión. Asegúrese de que la gateway no se convierte
en un cuello de botella para la aplicación y es lo suficientemente escalable.
• Descargue solo las características que se usan en toda la aplicación, como la seguridad o la
transferencia de datos.
• La lógica de negocios nunca se debe descargar en el gateway API.
• Si necesita hacer seguimiento de las transacciones, considere generar los ID de correlación con
fines de registro.

Cuándo utilizar este patrón


Use este patrón cuando:

• La implementación de una aplicación tiene un interés común como cifrado o certificados SSL.
• Una característica que es común entre las implementaciones de aplicaciones que pueden tener
distintos requisitos de recursos, como recursos de memoria, capacidad de almacenamiento o las
conexiones de red.

181 CAPÍTULO 6 | Catálogo de patrones


• Desea migrar la responsabilidad de problemas como la seguridad de red, limitación u otro
problema relacionado con los límites de red a un equipo más personalizado.

Es posible que este patrón no sea conveniente si introduce el acoplamiento entre los servicios.

Ejemplo
Con Nginx como el dispositivo de descarga de SSL, la siguiente configuración finaliza 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;
}
}

Guía relacionada
• Patrón de back-ends para front-ends
• Patrón de agregación de gateway
• Patrón de enrutamiento de gateway

Patrón de enrutamiento de gateway


Enrute solicitudes a varios servicios con un solo punto de conexión. Este patrón es útil cuando se
desea exponer varios servicios en un solo punto de conexión y enrutar al correspondiente servicio en
función de la solicitud.

Contexto y problema
Cuando un cliente necesita consumir varios servicios, establecer un punto de conexión independiente
para cada servicio y hacer que el cliente administre cada extremo pueden resultar todo un desafío.
Por ejemplo, una aplicación de comercio electrónico podría proporcionar servicios como búsqueda,
opiniones, carro, checkout e historial de pedidos. Cada servicio tiene una API distinta con la que debe
interactuar el cliente y este debe tener información sobre cada punto de conexión a fin de conectarse
a los servicios. Si se cambia o actualiza uno, el cliente también se debe actualizar. Si refactoriza un
servicio en dos o más servicios distintos, debe cambiar el código tanto en el servicio como en el cliente.

182 CAPÍTULO 6 | Catálogo de patrones


Solución
Ubique un gateway frene a un conjunto de aplicaciones, servicios o implementaciones. Use el
enrutamiento de aplicaciones de capa 7 para enrutar la solicitud a las instancias correspondientes.
Con este patrón, la aplicación cliente solo necesita conocer y comunicarse con un solo punto
de conexión. Si se consolida o descompone un servicio, el cliente no necesariamente requiere
actualizarse. Puede seguir haciendo solicitudes al gateway y solo cambia el enrutamiento.
Un gateway también permite abstraer los servicios de back-end de los clientes, lo que permite que
las llamadas de cliente sigan siendo sencillas a la vez que se permite hacer cambios en los servicios
de back-end que se encuentran detrás del gateway. Las llamadas de cliente se pueden enrutar al
servicio o los servicios que sean necesarios para controlar el comportamiento esperado del cliente, lo
que le permite agregar, dividir y reorganizar los servicios que se encuentran detrás del gateway sin
cambiar al cliente.

Este patrón también puede ayudar con la implementación, porque le permite administrar cuántas
actualizaciones se implementan en los usuarios. Cuando se implementa una versión nueva del
servicio, se puede implementar en paralelo con la versión existente. El enrutamiento permite controlar
la versión del servicio que se presenta a los clientes, lo que brinda la flexibilidad de usar varias
estrategias de versiones, ya sean implementaciones incrementales, en paralelo o completas de las
actualizaciones. Cualquier problema que se detecte una vez que se implemente el servicio nuevo se
puede revertir rápidamente con un cambio de configuración en el gateway, sin afectar a los clientes.

Problemas y consideraciones

• El servicio de gateway puede introducir un único punto de error. Asegúrese de que esté diseñado
correctamente para cumplir con los requisitos de disponibilidad. Considere funcionalidades de
resistencia y tolerancia a errores cuando implemente.
• El servicio de gateway puede introducir un cuello de botella. Asegúrese de que el gateway tiene
el rendimiento suficiente para controlar la carga y que pueda escalar fácilmente de manera
alineada con las expectativas de crecimiento.

183 CAPÍTULO 6 | Catálogo de patrones


• Realice pruebas de carga en el gateway para asegurarse de que no introducir errores en cascada
en los servicios.
• El enrutamiento de gateway es de nivel 7. Puede basarse en dirección IP, puerto, encabezado o
dirección URL.

Cuándo utilizar este patrón

Use este patrón cuando:


• Un cliente necesite consumir varios servicios a los que se puede tener acceso detrás de un
gateway.
• Desee simplificar las aplicaciones cliente mediante el uso de un solo punto de conexión.
• Necesite enrutar las solicitudes desde puntos de conexión abordables de manera externa a
puntos de conexión virtuales internos, como la exposición de puertos en una máquina virtual a
direcciones IP virtuales de clúster.

Es posible que este patrón no sea conveniente cuando se tiene una aplicación simple que solo usa
uno o dos servicios.

Ejemplo
Con Nginx como el enrutador, se muestra un archivo de configuración de ejemplo sencillo para un
servidor que enruta las solicitudes de aplicaciones que residen en directorios virtuales distintos a
máquinas diferentes 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;
}
}

Guía relacionada
• Patrón de back-ends para front-ends
• Patrón de agregación de gateway
• Patrón de descarga de gateway

184 CAPÍTULO 6 | Catálogo de patrones


Patrón de supervisión de punto de conexión de estado
Implemente comprobaciones funcionales en una aplicación a la que puedan acceder las herramientas
externas a través de puntos de conexión expuestos a intervalos periódicos. Este patrón puede ayudar
a comprobar que las aplicaciones y servicios funcionan correctamente.

Contexto y problema
Es recomendable, y a menudo se trata de un requisito de negocios, supervisar las aplicaciones
web y los servicios de back-end para garantizar que se encuentran disponibles y que funcionan
correctamente. Sin embargo, resulta más difícil supervisar los servicios que se ejecutan en la nube
que supervisar los servicios locales. Por ejemplo, no tiene el control total del entorno de hospedaje
y los servicios habitualmente dependen de otros servicios que proporcionan los proveedores de
plataformas y otros.
Son muchos los factores que afectan a las aplicaciones hospedadas en la nube, como la latencia de
red, el rendimiento y la disponibilidad de los sistemas de cálculo y almacenamiento subyacentes, y
el ancho de banda de red entre ellos. El servicio puede presentar un error completo o parcial debido
a cualquiera de estos factores. Por lo tanto, debe comprobar a intervalos regulares que el servicio
funciona correctamente para garantizar el nivel de disponibilidad requerido, que puede ser parte del
acuerdo de nivel de servicio (SLA).

Solución
Implemente el seguimiento de 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.

Una comprobación del seguimiento de estado habitualmente combina dos factores:


• Las comprobaciones (si corresponde) que la aplicación o el servicio realizó como respuesta a la
solicitud que se envió al punto de conexión de comprobación de estado.
• El análisis de los resultados por parte de la herramienta o del marco que realiza la comprobación
de estado.
El código de respuesta indica el estado de la aplicación y, de manera opcional, cualquier componente
o servicio que use. El marco o la herramienta de supervisión realiza la comprobación del tiempo de
respuesta o latencia. La figura proporciona información general del patrón.

185 CAPÍTULO 6 | Catálogo de patrones


Otras comprobaciones que puede realizar el código de seguimiento de estado en la aplicación
incluyen:
• Comprobación de la disponibilidad y el tiempo de respuesta del almacenamiento de nube o de
una base de datos.
• Comprobación de otros recursos o servicios ubicados en la aplicación o ubicados en otro lugar,
pero que los usa la aplicación.
Hay disponibles servicios y herramientas que supervisar las aplicaciones web mediante el envío de
una solicitud a un conjunto de puntos de conexión que se puede configurar y mediante la evaluación
de los resultados contra un conjunto de reglas configurables. Resulta relativamente sencillo crear un
punto de conexión de servicio cuyo único propósito es realizar algunas pruebas funcionales en el
sistema.
Las comprobaciones típicas que las herramientas de supervisión pueden realizar incluyen:
• La validación del código de respuesta. Por ejemplo, una respuesta HTTP de 200 (Correcto) indica
que la aplicación respondió sin errores. El sistema de supervisión también podría comprobar
otros códigos de respuesta para dar resultados más completos.
• La comprobación del contenido de la respuesta para detectar errores, incluso cuando se
devuelve un código de estado 200 (Correcto). Con esto se pueden detectar errores que afectan
solo una sección de la respuesta del servicio o de la página web que se devuelve. Por ejemplo,
comprobar el título de una página o buscar una frase específica que indica que se devolvió la
página correcta.
• La medición del tiempo de respuesta, que indica una combinación de la latencia de red y el
tiempo que la aplicación tardó en ejecutar la solicitud. Un valor creciente puede indicar un
problema incipiente con la aplicación o la red.
• La comprobación de recursos o servicios ubicados fuera de la aplicación, como una red de
entrega de contenido que usa la aplicación para entregar contenido de cachés globales.
• La comprobación de la expiración de los certificados SSL.
• La medición del tiempo de respuesta de una búsqueda DNS de la dirección URL de la aplicación
para medir la latencia y los errores de DNS.
• La validación de la dirección URL que la búsqueda DNS devuelve para garantizar entradas
correctas. Esto puede ayudar a prevenir el redireccionamiento de solicitudes malintencionadas a
través de un ataque exitoso en el servidor DNS.
También es útil, siempre que sea posible, ejecutar estas comprobaciones desde distintas ubicaciones
locales u hospedadas para medir y comparar los tiempos de respuesta. Idealmente, debería
supervisar las aplicaciones desde las ubicaciones que se encuentran cerca de los clientes para
obtener un panorama preciso del rendimiento de cada ubicación. Además de proporcionar un
mecanismo de comprobación más eficaz, estos resultados pueden ayudarlo a decidir la ubicación de
la implementación para la aplicación y si implementarla en más de un centro de datos.
También se deben ejecutar pruebas en todas las instancias de servicio que los clientes usan para
garantizar que la aplicación funciona correctamente para todos los clientes. Por ejemplo, si el
almacenamiento de cliente se distribuye en más de una cuenta de almacenamiento, el proceso de
supervisión debe comprobarlas todas.

Problemas y consideraciones
Considere los siguientes puntos en el momento de decidir cómo implementar este patrón:
Cómo validar la respuesta. Por ejemplo: ¿un solo código de estado 200 (Correcto) es suficiente para
comprobar que la aplicación funciona correctamente? Si bien esto proporciona la medida más básica
de disponibilidad de la aplicación y se trata de la implementación mínima de este patrón, brinda poca
información sobre las operaciones, las tendencias y los próximos problemas siguientes en la aplicación.

186 CAPÍTULO 6 | Catálogo de patrones


Asegúrese de que la aplicación devuelva correctamente un estado 200 (Correcto) solo cuando se
encuentre y procese el recurso de destino. En algunos escenarios, como cuando se usa una página
maestra para hospedar la página web de destino, el servidor devuelve un código de estado 200
(Correcto) en lugar de un código 404 (No se encuentra), incluso cuando no se encontró 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 que usa la aplicación y otro
para servicios de menor prioridad, lo que permite 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, con el fin de obtener granularidad de
supervisión adicional. Por ejemplo, una comprobación de estado podría analizar la base de datos, el
almacenamiento y un servicio de geocodificación externo que una aplicación usa, en que todo aquello
requiere 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 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 en la aplicación, como agregar el registro de un
usuario nuevo, iniciar sesión y hacer un pedido de prueba, a la vez que también se comprueba 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 solo analizan 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 personalizado.
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 toma podría exceder 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 resultar suficiente en lugar de
devolver información adicional de una comprobación de estado.
Almacenamiento en caché del estado del punto de conexión. Podría ser costoso ejecutar la
comprobación de estado con demasiada frecuencia. Por ejemplo, si se informa del estado de
mantenimiento a través de un panel, no desea que cada solicitud del panel desencadene una
comprobación de estado. En su lugar, compruebe de manera 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 con el fin de protegerlos del
acceso público, lo que podría exponer la aplicación a los ataques malintencionados, arriesgar la exposición
de información confidencial o atraer los ataques por denegación de servicio (DoS). Esto debería hacerse
habitualmente en la configuración de la aplicación, de modo que pueda actualizarse de manera sencilla
sin reiniciar la aplicación. Considere la posibilidad de usar una o más de las siguientes técnicas:
• Requiera autenticación para proteger el punto de conexión. Para ello, use una clave de seguridad de
autenticación en el encabezado de la solicitud o pase credenciales con la solicitud, siempre que el
servicio o la herramienta de supervisión admita la autenticación.
• Use un punto de conexión poco conocido u oculto. Por ejemplo, exponga el punto de
conexión de una dirección IP distinta de la que usa por la dirección URL de la aplicación
predeterminada, configure el punto de conexión en un puerto HTTP estándar o use
una ruta de acceso compleja a la página de prueba. Habitualmente, 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. En función 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 se encuentra) si
no se reconoce el valor del parámetro. 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. Idealmente, evite usar 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 depender solo de un punto de
conexión que no sea visible. También debería considerar la posibilidad de usar una
conexión HTTPS y cifrar la información confidencial, 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
comprobación de estado. Por ejemplo, las características integradas de comprobación de estado
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 agente de supervisión funcione correctamente. Un enfoque consiste en
exponer un punto de conexión que solo devuelve un valor de la configuración de la aplicación o
un valor aleatorio que se puede usar para probar el 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 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 supervisa 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 ejemplos de código siguientes, que se tomaron de la clase HealthCheckController (un ejemplo
que muestra que este patrón está disponible en GitHub), se puede ver la exposición de un punto de
conexión para realizar una variedad de comprobaciones de estado.
El método CoreServices, que a continuación se muestra 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 manera opcional,
devolver información adicional cuando se produce un error, si el marco o la herramienta 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 de 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 ID 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


comprueba si se especificó un valor de parámetro de configuración.

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 los puntos de conexión en aplicaciones de Azure son:

• Usar las características de supervisión integradas de Azure.


• Usar un servicio o marco de terceros como Microsoft System Center Operations Manager.
• Crear una utilidad personalizada o un servicio que ejecute por su cuenta o en un servidor
hospedado.

Si bien Azure proporciona un conjunto razonablemente completo de opciones de supervisión, puede


usar servicios y herramientas adicionales para brindar información adicional. Azure Management
Services proporciona un mecanismo de supervisión integrado para las reglas de alertas. La sección
sobre alertas de la página de servicios de administración de Azure Portal le permite configurar hasta
diez reglas de 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 de correo electrónico a las
direcciones que define en cada regla.

Las condiciones que puede supervisar varían en función del mecanismo de hospedaje 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 de alertas que usa un punto de conexión web que
se especifica en la configuración del servicio. Este punto de conexión debe responder de manera
oportuna para 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
usar uno de los servicios integrados de Azure llamado Traffic Manager. Traffic Manager es un servicio
de enrutamiento y equilibrio de carga que puede distribuir las solicitudes a instancias específicas de
la aplicación hospedada de Cloud Services según 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 se especifica de manera 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 consultar el estado
en la consola de Traffic Manager y configurar la regla para volver a enrutar las solicitudes a otras
instancias de la aplicación que están respondiendo.

Sin embargo, Traffic Manager solo esperará diez segundos para recibir una respuesta desde la
dirección URL de supervisión. Por lo tanto, debe asegurar que el código de comprobación de estado
se ejecuta en este momento, lo que permite 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. Traffic
Manager también se analiza en la guía de implementación de varios centros de datos.

190 CAPÍTULO 6 | Catálogo de patrones


Guía relacionada
La siguiente guía puede resultar útil cuando se implementa este patrón:
• Guía de telemetría e instrumentación. La comprobación del estado de los servicios y
componentes habitualmente se realiza por sondeo, pero también es útil contar con información
para supervisar el rendimiento de la aplicación y detectar los eventos que se producen en tiempo
de ejecución. Estos datos se pueden transmitir de vuelta 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 que reúne 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


Cree índices sobre los campos en los almacenes de datos a los que las consultas hacen referencia
con frecuencia. Este patrón puede mejorar el rendimiento de las consultas al permitir que las
aplicaciones encuentren rápidamente los datos que se van a recuperar de un almacén de datos.

Contexto y problema
Muchos almacenes de datos organizan los datos para una colecció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 la información del cliente organizada por la clave principal (ID
de cliente).

Aunque la clave principal es valiosa para las consultas que capturan datos según el valor de esta
clave, es posible que una aplicación no pueda 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 de ID de cliente para recuperar clientes si consulta los 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, es posible que la aplicación tenga 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
clave no principales (secundarios) e indica dónde se almacenan los datos de cada valor indexado. Los

191 CAPÍTULO 6 | Catálogo de patrones


elementos de un índice secundario habitualmente se ordenan por el valor de las claves secundarias
para habilitar la búsqueda rápida de datos. Habitualmente, el sistema de administración de bases de
datos mantiene automáticamente estos índices.
Puede crear tantos índices secundarios como necesite para admitir las distintas 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, resulta beneficioso agregar un índice secundario sobre el campo de
ciudad si la aplicación busca frecuentemente 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 la nube no proporcionan una
característica equivalente.

Solución
Si el almacén de datos no admite los índices secundarios, puede crear sus propias tablas de índice
para emularlos manualmente. Una tabla de índice organiza los datos según una clave especificada.
Habitualmente se usan tres estrategias para estructurar una tabla de índice, en función del número
de índices secundarios necesarios y la naturaleza de las consultas que realiza una aplicación.

La primera estrategia es duplicar los datos de cada tabla de índice, pero organizarlos según distintas
claves (desnormalización completa). En la figura siguiente se muestran las tablas de índice que
organizan la misma información de cliente por el valor Town y LastName.

Esta estrategia es adecuada si los datos son relativamente estáticos en comparación con la cantidad
de veces que se consultan con cada clave. Si los datos son más dinámicos, la sobrecarga de
procesamiento que implica el mantenimiento de cada tabla de índice resulta demasiado grande para
que este enfoque sea útil. Además, si el volumen de datos es muy grande, la cantidad de espacio que
se requiere 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 con la clave principal en lugar de duplicarlos, como se muestra
en la siguiente figura. Los datos originales se denominan "tabla de hechos".

192 CAPÍTULO 6 | Catálogo de patrones


Esta técnica ahorra espacio y reduce la sobrecarga que implica el mantenimiento de los datos
duplicados. La desventaja es que una aplicación debe realizar dos operaciones de búsqueda para
encontrar los datos mediante una clave secundaria. Debe 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 parcialmente normalizadas que estén organizadas
por claves distintas que duplican campos recuperados con frecuencia. Haga referencia a la tabla de
hechos para tener acceso a campos a los que se tiene acceso con menos frecuencia. En la siguiente
figura se muestra cómo se duplican los datos a los que se tiene acceso con menos frecuencia 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 es tan significativa como la duplicación de todo el
conjunto de datos.

Si una aplicación consulta datos con frecuencia mediante la especificación de una combinación de
valores (por ejemplo, "Buscar a todos los clientes que viven en Redmond y tienen el apellido Smith"),
podría implementar las claves en los elementos de la tabla de índice como una concatenación del
atributo Town y el atributo LastName. En la siguiente figura se muestra una tabla de índice basada en
claves compuestas. Las claves se ordenan por Town y, a continuación, por LastName en el caso de los
registros que tienen el mismo valor de Town.

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 donde se aplicó un algoritmo hash a la clave de partición. En la figura
siguiente 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 sin hash (Town y LastName) y proporcionar la
clave de partición con hash como los datos de búsqueda. Esto permite que la aplicación no tenga
que hacer el cálculo repetido de claves hash (una operación que es costosa) si necesita recuperar
los datos que caen dentro de un rango o necesita capturar datos por orden de la clave sin hash. Por
ejemplo, una consulta como "Buscar a todos los clientes que viven en Redmond" se puede resolver
rápidamente localizando los elementos coincidentes en la tabla de índice, donde todos se almacenan
en un bloque contiguo. Luego, siga las referencias a los datos de los clientes con las claves de
partición almacenadas en la tabla de índice.

Problemas y consideraciones
Considere los siguientes puntos en el momento de decidir cómo implementar este patrón:
• La sobrecarga de mantenimiento de los índices secundarios puede ser considerable. Debe
analizar y comprender las consultas que usa la aplicación. Cree solo tablas de índice cuando sea
posible usarlas de manera habitual. No cree tablas de índice especulativas para respaldar las
consultas que una aplicación no realiza o que solo realiza de manera 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
costos 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 final. Por ejemplo,
para insertar, actualizar o eliminar datos, una aplicación podría enviar un mensaje a una cola y
permitir que una tarea independiente realice la operación y mantenga 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 final, consulte el Manual básico de coherencia de datos.
• Las tablas de almacenamiento de Microsoft Azure admiten las actualizaciones transaccionales
para los cambios realizados en los datos contenidos en la misma partición, que se denominan
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 clave principal (o de partición).
Este patrón podría no ser útil cuando:

• Los datos son volátiles. Una tabla de índice puede quedar desactualizada muy rápido, lo que
hace que sea ineficaz o que la sobrecarga de mantenimiento de la tabla de índice sea mayor que
el ahorro que se destina a su uso.
• Un campo seleccionado como clave secundaria de una tabla de índice no discrimina 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 contiene 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 comprender las consultas que
realiza la 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 la nube. Las aplicaciones almacenan
y recuperan los valores de los datos mediante la especificación de 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 simplemente 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 por orden de clave de fila en una partición. El almacenamiento de tablas se optimiza para

195 CAPÍTULO 6 | Catálogo de patrones


realizar consultas que capturan datos que se encuentran dentro un rango contiguo de valores de fila
dentro de una partición. Si compila aplicaciones de nube que almacenan información en tablas de
Azure, debe estructurar los datos considerando esta característica.

Por ejemplo, piense en una aplicación que almacena información sobre películas. La aplicación
consulta frecuentemente 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 con este como clave de
partición y especificando el nombre de la película como clave de fila, tal 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 sirva 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 contenidos en cada partición si adopta 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 manera parcial los datos para respaldar las consultas (como los nombres de los otros actores)
y permitir que una aplicación recupere cualquier detalle restante, incluida la clave de partición
necesaria para encontrar la información completa en las particiones de género. Este enfoque se
describe en la tercera opción de la sección Solución. En la figura siguiente 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 pertinentes al implementar este patrón:
• Manual básico de coherencia de datos. Una tabla de índice se debe mantener a medida que
cambian los datos que indexa. En la nube, 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 con coherencia final es más adecuado. Proporciona información sobre los
problemas relacionados con la coherencia final.
• 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 admitir 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 mediante la generación de vistas previamente
rellenadas sobre los datos.

Patrón de elección del líder


Coordine las acciones realizadas por una colección de instancias de colaboración en una aplicación
distribuida mediante la elección de una instancia como el líder que asume la responsabilidad de
administrar las otras. Esto puede ayudar a garantizar que las instancias no entren en conflicto entre
sí, causen una contención considerable para recursos compartidos o interfieran accidentalmente con
el trabajo que realizan otras instancias.

Contexto y problema
Una aplicación de nube 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, causen contención para recursos compartidos o interfieran accidentalmente con el trabajo
que realizan otras instancias de tarea.
Por ejemplo:
• En un sistema basado en la nube que implementa el 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 se
deben agregar cuando se completen totalmente.
Todas las instancias de tarea son del mismo nivel, por lo que no hay un líder natural que pueda
actuar como coordinador o agregador.

Solución
Se debe elegir una sola instancia de tarea para que actúe 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 es capaz de 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 eficaz para seleccionar el líder. Este método debe
enfrentar eventos como interrupciones de red o errores del proceso. En muchas soluciones, las
instancias de tarea subordinadas supervisan al líder a través de algún tipo de método de latido o por
sondeo. Si el líder designado finaliza 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 elegir un nuevo líder.
Existen varias estrategias para elegir un líder entre un conjunto de tareas en un entorno distribuido,
entre las que se incluyen:

• La selección de la instancia de tarea con el ID de proceso o la instancia de menor rango.


• La 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 finaliza o se desconecta del resto del sistema, se libera la exclusión mutua para
permitir a otra instancia de tarea convertirse en el líder.
• La implementación de uno de los algoritmos de elección de líder habituales como Bully
Algorithm o Ring Algorithm. Estos algoritmos presuponen que cada candidato de la elección
tiene un ID único y que puede comunicarse con los otros candidatos de manera confiable.

Problemas y consideraciones
Considere los siguientes puntos en el momento 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 presentó un error o, de lo contrario, dejó de estar disponible

198 CAPÍTULO 6 | Catálogo de patrones


(por ejemplo, debido a un error de comunicaciones). La rapidez con que se necesita la detección
que depende del sistema. Algunos sistemas podrían funcionar durante un breve período de
tiempo sin un líder. En este tiempo, se podría corregir un error transitorio. En otros casos,
podría ser necesario detectar un error del líder de manera inmediata y desencadenar una nueva
elección.
• En un sistema que implementa el escalado automático horizontal, el líder se podría finalizar 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 bastante simple. Sin embargo, si
el proceso no se realiza correctamente, podría producirse un retraso considerable 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 la nube) necesiten una coordinación cuidadosa y no haya un líder natural.

Evite que el líder se convierta en 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 resultar útil si:

• Existe un líder natural o un 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 que proporciona Apache Zookeeper para coordinar
la asignación y reducir las tareas que recopilan y resumen datos.

Ejemplo
El proyecto DistributedMutex de la solución LeaderElection (un ejemplo donde se muestra que
este patrón está disponible en GitHub) 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 en la nube 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 renovarla. 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 sujeto de solo una concesión en cualquier momento dado. Una instancia de rol puede solicitar una
concesión sobre un blob especificado, la que se le concederá si ninguna otra instancia de rol cuenta
con una concesión sobre el mismo blob. De lo contrario, la solicitud generará una excepción.

Para evitar que una instancia de rol con errores retenga la concesión de manera indefinida,
especifique un ciclo de vida para la concesión. Cuando este expire, la concesión pasará a estar
disponible. Sin embargo, aunque una instancia de rol tiene la concesión, puede solicitar que esta
se renueve y se le concederá 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 la
concesión de un blob, consulte el artículo relacionado con la concesión de blobs (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 cuando se crea 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 nivel inferior 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 que se muestra 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 eligió 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 eligió 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án totalmente coherentes 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 competitivos) 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 la nube son frecuetes 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,

225 CAPÍTULO 6 | Catálogo de patrones


suele deberse a que el servicio ha agotado sus recursos. Para reducir la frecuencia de estos errores,
escale el servicio 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.

233 CAPÍTULO 6 | Catálogo de patrones


• Patrón de elección del líder. Puede ser necesario para coordinar las acciones de varias instancias
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 los 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,
240 CAPÍTULO 6 | Catálogo de patrones
probablemente 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 la 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ística 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.Demoloreped que cust doluptios

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 donde 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

253 CAPÍTULO 6 | Catálogo de patrones


A o C, por tanto se deshabilita provisionalmente y se liberan los recursos que se estaban usando.
Entre los tiempos T1 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 las explosiones de actividad.

• Para ayudar a optimizar los costos 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.
Validar y, opcionalmente, sanear 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í cómo 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.

La 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 para
revisión del diseño

263 CAPÍTULO 7 | Listas de comprobación para 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 para 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, plantéese transferir directamente el
código probado y confirmado a producción. plantéese
• Utilice la alternancia de características para controlar las características que están
habilitadas.

265 CAPÍTULO 7 | Listas de comprobación para 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 para 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 para 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 para 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 para 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 para 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 para 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 para 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 para 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
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 para 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ñe para el escalado

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 puede 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 para 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 para 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 para 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 para 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 para 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 para 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 para 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 para 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 para 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 para 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 para 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 para 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 para 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 aprovisione 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 FDs y UDs, 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 para 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 para 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 Obtenga


gratuita de Azure ayuda de los
Si aún no lo ha hecho, abra una cuenta de Azure gratuita para beneficiarse
de muchas ventajas, incluyendo:
expertos
●● U
 n crédito de 200 dólares para utilizar con cualquier producto de Azure
durante 30 días.
●● A
 cceso gratuito durante 12 meses a la mayoría de nuestros productos Póngase en contacto con
más populares de distintas categorías, como proceso, almacenamiento,
nosotros en
redes y bases de datos.
aka.ms/azurespecialist
●● Más de 25 productos que siempre son gratuitos.

291
9

Arquitecturas de
referencia 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
Parathis
estescenario
escenario Considere esta estructura

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


AD DS que no estén implementadas Amplíe AD DS a Azure
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:
●  Autenticar y autorizar usuarios de Ampliar AD FS a Azure
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 Considere esta estructura

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.

Hay aplicaciones híbridas que necesitan el ExpressRoute con conmutación por recuperación a VPN
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.
DMZ de red
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.

Вам также может понравиться