Hoy, cuando un pasajero pulsa «Confirmar reserva» en app-contoso-reservas-pro, dentro de esa única petición HTTP pasan seis cosas seguidas: se cobra en la pasarela, se actualiza la disponibilidad, se encola la emisión de la tarjeta, se acreditan las millas, se avisa al sistema de facturación y se envía el correo de confirmación. Todo síncrono, todo en línea. La consecuencia es brutal y Contoso Airlines ya la ha sufrido: si el servicio de millas está lento, la compra falla. Un componente accesorio tumba el ingreso principal de la compañía.
Esta lección resuelve ese problema de raíz con desacoplamiento: los componentes dejan de llamarse unos a otros y pasan a intercambiar mensajes y eventos a través de una infraestructura intermedia. Verás los tres servicios que Azure ofrece para ello, cuándo usar cada uno, y las consecuencias de diseño que trae este modelo, que no son gratis.
Contenido
- Por qué desacoplar
- Mensaje frente a evento
- Los tres servicios comparados
- Azure Service Bus
- Azure Event Grid
- Azure Event Hubs
- El flujo completo de Contoso
- Entrega al menos una vez, ordenación e idempotencia
- Coste de cada servicio
- Errores Comunes y Consejos
- Ejercicios
- Conclusión
- Por qué desacoplar
Encadenar seis llamadas síncronas tiene tres defectos acumulativos. La disponibilidad se multiplica: si cada componente tiene un 99,9 %, la cadena de seis baja al 99,4 %, más de cuatro horas de caída al mes. La latencia se suma: el pasajero espera lo que tardan todos. Y el acoplamiento se congela: añadir un séptimo paso obliga a tocar y redesplegar el código de la compra.
Con un intermediario, la compra hace lo imprescindible —cobrar y reservar la plaza— y publica un hecho. Los demás reaccionan a su ritmo. Si el servicio de millas está caído, su mensaje espera en la cola y se procesa cuando vuelve; el pasajero ni se entera. Eso es el desacoplamiento: en el tiempo, en el espacio y en la disponibilidad.
- Mensaje frente a evento
Esta distinción es la que casi todo el mundo confunde, y de ella depende elegir bien el servicio.
| Mensaje | Evento | |
|---|---|---|
| Qué expresa | Una instrucción: haz esto | Un hecho: esto ha ocurrido |
| Intención del emisor | Espera que alguien lo procese | No sabe ni le importa quién reacciona |
| Contenido | La carga completa que hace falta | Ligero: qué pasó, cuándo y sobre qué |
| Acoplamiento | El emisor conoce al destinatario | Ninguno |
| Si nadie lo consume | Es un error: se queda en la cola | Es normal: se descarta |
| Ejemplo en Contoso | «Emite la tarjeta del localizador XR7742» | «La reserva XR7742 se ha confirmado» |
La regla mnemotécnica: un mensaje tiene destinatario e intención; un evento tiene emisor y pasado. La forma verbal lo delata —EmitirTarjeta frente a ReservaConfirmada— y no es cosmética: el mensaje va a Service Bus porque alguien tiene que procesarlo y no se puede perder; el evento va a Event Grid porque cero, uno o siete suscriptores pueden interesarse y el emisor sigue igual.
- Los tres servicios comparados
| Service Bus | Event Grid | Event Hubs | |
|---|---|---|---|
| Para qué | Mensajería empresarial fiable | Distribución de eventos discretos | Ingesta masiva de telemetría |
| Unidad | Mensaje (hasta 256 KB / 100 MB) | Evento (64 KB) | Registro (pequeño, en flujo) |
| Volumen típico | Miles por segundo | Millones por segundo | Millones por segundo, sostenidos |
| Modelo de entrega | El consumidor extrae | El servicio empuja | El consumidor lee por desplazamiento |
| Orden | Sí, con sesiones | No garantizado | Sí, dentro de la partición |
| Reintentos y mensajes fallidos | Completos | Sí, con reintentos y cola | El consumidor gestiona su posición |
| Retención | Hasta que se consume | 24 horas de reintentos | Ventana de tiempo (1-7 días o más) |
| Caso en Contoso | reservas-confirmadas |
ReservaConfirmada, blob nuevo |
Telemetría de mostradores |
Y no olvides las colas de Azure Storage de 02-04: simples, muy baratas, sin temas ni sesiones ni transacciones, con mensajes de 64 KB y siete días de retención. Bastan cuando hay un solo consumidor, el orden da igual y no necesitas filtros ni entrega transaccional. cola-emision-tarjetas es exactamente ese caso y por eso sigue siendo una cola de Storage. En cuanto necesites temas, sesiones, detección de duplicados o transacciones, es Service Bus.
- Azure Service Bus
az servicebus namespace create -g rg-contoso-reservas-pro -n sb-contoso-pro \
--location westeurope --sku Premium --capacity 1 \
--tags entorno=produccion proyecto=contoso-reservas \
centro-coste=CC-1042 [email protected]
# Un TEMA: un publicador, varios interesados con criterios distintos
az servicebus topic create -g rg-contoso-reservas-pro --namespace-name sb-contoso-pro \
-n reservas-confirmadas --max-size 5120 --enable-duplicate-detection true
az servicebus topic subscription create -g rg-contoso-reservas-pro \
--namespace-name sb-contoso-pro --topic-name reservas-confirmadas \
-n tarjetas --max-delivery-count 5 --dead-lettering-on-message-expiration true
# Filtro: fidelizacion solo quiere las reservas de socios
az servicebus topic subscription rule create -g rg-contoso-reservas-pro \
--namespace-name sb-contoso-pro --topic-name reservas-confirmadas \
--subscription-name fidelizacion -n solo-socios \
--filter-sql-expression "esSocio = true AND importe > 0"Las piezas que hay que conocer:
- Cola frente a tema. Una cola es punto a punto: cada mensaje lo procesa un consumidor. Un tema es publicación y suscripción: cada suscripción recibe su propia copia. Contoso usa un tema con las suscripciones
tarjetas,facturacionyfidelizacion, de modo que añadir un cuarto interesado no toca el emisor. - Filtros de suscripción. Reglas SQL sobre las propiedades del mensaje.
fidelizacionsolo recibe reservas de socios, así que no ve lo que no le concierne: menos proceso y menos coste. - Sesiones. Sin ellas no hay orden garantizado. Con
SessionId—por ejemplo, el localizador— todos los mensajes de esa sesión los procesa un solo consumidor y en orden. Es lo que permite garantizar que «reserva creada» se procese antes que «reserva modificada». - Bloqueo con vistazo. El modo de recepción correcto: el consumidor bloquea el mensaje sin borrarlo, trabaja, y solo entonces lo completa. Si el proceso muere, el bloqueo caduca y el mensaje vuelve a estar disponible. El modo alternativo, recibir y borrar, pierde mensajes ante cualquier fallo.
- Aplazamiento. Apartar un mensaje que no se puede procesar todavía —falta un dato que llegará después— sin bloquear la cola, para recuperarlo luego por su número de secuencia.
- Cola de mensajes con problemas de entrega. La pieza que salva a los equipos. Tras
max-delivery-countintentos fallidos, el mensaje se mueve a una subcola en lugar de bloquear el proceso para siempre. Ahí se inspecciona, se corrige y se reenvía. Sin ella, un mensaje envenenado atasca la cola indefinidamente. - Transacciones y detección de duplicados. Se pueden agrupar varias operaciones —completar un mensaje y publicar otro— en una unidad atómica dentro del mismo espacio de nombres. Y con la detección de duplicados activada, el servicio descarta durante una ventana de tiempo los mensajes con el mismo
MessageId.
| Nivel | Cuándo |
|---|---|
| Básico | Solo colas. Ni temas ni sesiones: casi nunca sirve |
| Estándar | Temas, sesiones, transacciones. Capacidad compartida y variable |
| Premium | Recursos dedicados, rendimiento predecible, puntos privados. Producción |
Emisión y consumo, con identidad administrada y sin cadenas de conexión:
// Publicar: el hecho lleva propiedades para que los filtros puedan actuar
var cliente = new ServiceBusClient("sb-contoso-pro.servicebus.windows.net",
new DefaultAzureCredential());
var emisor = cliente.CreateSender("reservas-confirmadas");
var mensaje = new ServiceBusMessage(BinaryData.FromObjectAsJson(reserva))
{
MessageId = reserva.Localizador, // detección de duplicados
SessionId = reserva.Localizador, // orden por reserva
ContentType = "application/json"
};
mensaje.ApplicationProperties["esSocio"] = reserva.EsSocio; // usado por el filtro
mensaje.ApplicationProperties["importe"] = reserva.Importe;
await emisor.SendMessageAsync(mensaje);
// Consumir con bloqueo con vistazo: se completa SOLO si el trabajo terminó bien
var procesador = cliente.CreateProcessor("reservas-confirmadas", "tarjetas",
new ServiceBusProcessorOptions { MaxConcurrentCalls = 10, AutoCompleteMessages = false });
procesador.ProcessMessageAsync += async args =>
{
var reserva = args.Message.Body.ToObjectFromJson<Reserva>();
try
{
await _emisorTarjetas.EmitirAsync(reserva); // idempotente (06-03)
await args.CompleteMessageAsync(args.Message);
}
catch (DatoInvalidoException ex)
{
// Error permanente: no tiene sentido reintentar, va directo a la subcola
await args.DeadLetterMessageAsync(args.Message, "DatoInvalido", ex.Message);
}
// Cualquier otra excepción: no se completa, el bloqueo caduca y se reintenta
};
await procesador.StartProcessingAsync();Fíjate en la distinción del catch: ante un error permanente se envía a la subcola de inmediato, porque reintentar cinco veces un dato malformado solo gasta tiempo; ante un error transitorio se deja que el bloqueo caduque y el mensaje se reintente solo.
- Azure Event Grid
Event Grid es un enrutador de eventos con publicación y suscripción por envío: el servicio empuja el evento al destino y gestiona los reintentos. No hay que sondear nada.
- Temas del sistema: los propios servicios de Azure publican eventos sin configuración. Un blob nuevo en
sttarjetascontosoproemiteMicrosoft.Storage.BlobCreated, y Contoso lo usa para disparar la indexación de la tarjeta recién generada. Lo mismo con Key Vault avisando de un certificado a punto de caducar, o con ACR avisando de una imagen nueva. - Temas personalizados: los publicas tú, y ahí va
ReservaConfirmada. - Esquema CloudEvents: el estándar de la CNCF, y el recomendado hoy frente al esquema propio de Event Grid, porque hace los eventos portables.
- Filtros: por tipo de evento, por prefijo o sufijo del sujeto y por valores de propiedades avanzadas. El filtrado ocurre en el servicio, así que el suscriptor solo recibe lo que le interesa.
- Reintentos y cola de eventos fallidos: si el destino no responde, reintenta con espera exponencial durante 24 horas y luego deposita el evento en una cuenta de almacenamiento configurada. Si no la configuras, el evento se pierde en silencio, que es la trampa más común del servicio.
az eventgrid topic create -g rg-contoso-reservas-pro -n evgt-contoso-reservas-pro \
--location westeurope --input-schema cloudeventschemav1_0
az eventgrid event-subscription create --name sub-motor-disponibilidad \
--source-resource-id $(az eventgrid topic show -g rg-contoso-reservas-pro \
-n evgt-contoso-reservas-pro --query id -o tsv) \
--endpoint-type azurefunction \
--endpoint "<id de la funcion>" \
--included-event-types Contoso.Reservas.ReservaConfirmada \
--advanced-filter data.importe NumberGreaterThan 500 \
--deadletter-endpoint "<id del contenedor de eventos fallidos>"El evento en CloudEvents, mostrando lo esencial: es ligero y describe un hecho, no lleva la reserva entera.
{
"specversion": "1.0",
"type": "Contoso.Reservas.ReservaConfirmada",
"source": "/contoso/reservas/app-contoso-reservas-pro",
"id": "b41f7c22-9e0a-4a11-9a3c-7d2f5c1e8a90",
"subject": "reservas/XR7742",
"time": "2026-08-15T09:14:22Z",
"datacontenttype": "application/json",
"data": { "localizador": "XR7742", "vuelo": "CA-1187",
"origenDestino": "BCN-CDG", "importe": 214.50, "esSocio": true }
}type permite filtrar por clase de evento, subject por recurso concreto —con filtros de prefijo como reservas/XR—, e id es el identificador que el consumidor usa para detectar duplicados.
- Azure Event Hubs
Event Hubs no es para eventos de negocio sino para flujos de datos a gran escala: telemetría, registros, series temporales. Contoso lo usa para los mostradores de facturación, que emiten sin parar el estado de la cola de pasajeros, el tiempo de atención y las incidencias de equipaje.
- Particiones: el flujo se divide en particiones que se leen en paralelo. El orden se garantiza dentro de una partición, así que la clave de partición —el identificador del mostrador— determina qué queda ordenado. El número de particiones fija el paralelismo máximo y conviene pensarlo al crear el recurso.
- Grupos de consumidores: cada aplicación consumidora tiene su propia vista del flujo con su propio avance. El panel en tiempo real y el proceso analítico leen los mismos datos sin estorbarse.
- Retención: los registros no se borran al leerse; permanecen una ventana de tiempo, de 1 a 7 días en Estándar y más en Premium. Eso permite reprocesar volviendo atrás en el desplazamiento, algo imposible con una cola.
- Captura: escribe automáticamente el flujo en Avro sobre
stlagocontosopro, en la capabroncedel Data Lake (03-06), sin escribir una línea de código. Esta es la integración clave con la analítica. - Compatibilidad con Kafka: un productor o consumidor de Kafka apunta a Event Hubs cambiando la configuración de conexión, sin tocar el código. Es la vía de migración sin operar un clúster.
az eventhubs namespace create -g rg-contoso-reservas-pro -n evhns-contoso-telemetria-pro \
--location westeurope --sku Standard --capacity 2 --enable-auto-inflate --maximum-throughput-units 10
az eventhubs eventhub create -g rg-contoso-reservas-pro \
--namespace-name evhns-contoso-telemetria-pro -n facturacion-mostradores \
--partition-count 8 --retention-time-in-hours 72 \
--enable-capture true --capture-interval 300 \
--destination-name EventHubArchive.AzureBlockBlob \
--storage-account stlagocontosopro --blob-container bronce--enable-auto-inflate sube las unidades de rendimiento automáticamente ante un pico —y aquí va el aviso de coste: sube solas, pero no bajan solas, así que un pico puntual deja el recurso facturando al máximo hasta que alguien lo ajusta—.
- El flujo completo de Contoso
graph LR WEB["app-contoso-reservas-pro<br/>Confirmar reserva"] -->|1 mensaje| SB["Service Bus<br/>tema reservas-confirmadas"] WEB -->|2 evento| EG["Event Grid<br/>evgt-contoso-reservas-pro"] SB --> S1["Suscripcion tarjetas"] --> F1["Function<br/>GenerarTarjetaEmbarque"] SB --> S2["Suscripcion facturacion"] --> CA["ca-motor-facturacion"] SB --> S3["Suscripcion fidelizacion<br/>filtro esSocio = true"] --> F2["Function<br/>AcreditarMillas"] F1 --> BLOB["sttarjetascontosopro<br/>tarjetas-embarque"] BLOB -->|BlobCreated| EG2["Event Grid<br/>tema del sistema"] --> LA["Logic App:<br/>notificar al pasajero"] EG --> ML["Modelo de demanda"] MOS["Mostradores de facturacion"] -->|telemetria| EH["Event Hubs<br/>facturacion-mostradores"] EH -->|captura| LAGO["stlagocontosopro / bronce"] EH --> PANEL["Panel de operaciones<br/>en tiempo real"] SB -.->|fallos| DLQ["Cola de mensajes<br/>con problemas de entrega"]
Léelo como una decisión de diseño en cada flecha. La confirmación publica un mensaje en el tema, porque emitir la tarjeta, facturar y acreditar millas tienen que ocurrir y no pueden perderse; y publica un evento en Event Grid, porque puede haber interesados que hoy no existen —el modelo de predicción de demanda es uno— sin que el emisor los conozca. Cuando la función deja el PDF en el contenedor, el tema del sistema de Storage emite BlobCreated y una aplicación lógica (06-04) notifica al pasajero: nadie tuvo que programar esa conexión. La telemetría de los mostradores va por Event Hubs, con captura automática hacia el Data Lake y lectura simultánea del panel en tiempo real desde otro grupo de consumidores. Y la petición del pasajero termina en cuanto se cobra y se reserva la plaza: si fidelización está caído, su mensaje espera en la suscripción y la compra no se entera. Ese era el problema del principio, y está resuelto.
- Entrega al menos una vez, ordenación e idempotencia
Estas son las tres consecuencias inevitables del modelo, y conviene aceptarlas como parte del diseño y no como sorpresas.
Entrega al menos una vez. Los tres servicios garantizan que el mensaje llega, no que llega una sola vez. Un consumidor puede recibir un duplicado si se cae tras procesar y antes de completar, si el bloqueo caduca por tardar demasiado, o si el emisor reintenta al no recibir la confirmación. La entrega exactamente una vez de extremo a extremo no existe en un sistema distribuido; lo que existe es procesamiento idempotente, que la vuelve innecesaria.
Ordenación. Por defecto no hay orden global. Se consigue orden por sesión en Service Bus y por partición en Event Hubs, y Event Grid no lo garantiza de ninguna forma. Diseña para que el orden no importe siempre que puedas —incluyendo una marca de tiempo o un número de versión en la carga y descartando lo viejo—, y reserva las sesiones para los casos en que de verdad haga falta, porque limitan el paralelismo.
Idempotencia, ya vista en 06-03 y ahora obligatoria en todo consumidor. Las tres herramientas: MessageId con detección de duplicados en Service Bus, que resuelve la ventana corta; el identificador de evento registrado por el consumidor, con la tabla registroembarques de stoperacionescontosopro; y las operaciones naturalmente idempotentes, escribir con clave determinista o upsert en lugar de insert. Esta última es siempre la mejor porque no necesita infraestructura adicional.
- Coste de cada servicio
| Servicio | Cómo se factura | Qué dispara la factura |
|---|---|---|
| Colas de Storage | Por transacción y almacenamiento | Casi nada; es la opción más barata |
| Service Bus Estándar | Por operación, con base mensual | El sondeo agresivo: cada intento de recepción vacío es una operación |
| Service Bus Premium | Por unidad de mensajería y hora | Dimensionar de más «por si acaso» |
| Event Grid | Por operación (millón de operaciones) | Suscripciones sin filtrar que reciben todo |
| Event Hubs | Por unidad de rendimiento y hora + eventos | El escalado automático que sube y no baja, y la captura |
Tres consejos concretos: usa recepción con espera larga en Service Bus en lugar de sondear en bucle, porque una recepción vacía cada segundo son 2,6 millones de operaciones al mes por consumidor; filtra en el servicio, no en el consumidor, porque lo que se filtra en Event Grid no se factura como entrega; y revisa las unidades de rendimiento de Event Hubs después de cada pico, porque auto-inflate sube pero no baja. En desarrollo, elimina los espacios de nombres que no se usen: un Service Bus Premium o un Event Hubs con unidades reservadas facturan estén vacíos o llenos.
Errores Comunes y Consejos
- Usar Event Hubs para mensajería de negocio. No tiene bloqueo por mensaje ni cola de mensajes fallidos: la gestión de fallos es tuya y acabarás reimplementando Service Bus mal.
- No configurar la cola de mensajes fallidos en Service Bus ni el destino de eventos fallidos en Event Grid. Los fallos se vuelven invisibles y los datos se pierden.
- Meter la carga completa en el evento. Un evento describe un hecho y lleva una referencia; si necesitas mandar 5 MB, súbelo a
sttarjetascontosoproy manda su URI. - Asumir orden donde no lo hay. Si el orden es imprescindible, sesiones o particiones; si no, marca de versión en la carga.
- No hacer idempotente el consumidor. Duplicados esporádicos, irreproducibles y siempre en producción.
- Reintentar indefinidamente un error permanente. Distingue lo transitorio de lo permanente y manda lo segundo a la subcola de inmediato.
- Sondear en bucle cerrado. Dispara la factura de operaciones. Recepción con espera larga.
- Consejo: pon nombre de hecho en pasado a los eventos (
ReservaConfirmada) y de instrucción a los mensajes (EmitirTarjeta). El nombre obliga a decidir cuál es cuál, y con eso el servicio se elige solo. - Consejo: versiona el esquema desde el primer evento, con
Contoso.Reservas.ReservaConfirmada.v1. El día que cambie la carga, los consumidores antiguos seguirán funcionando.
Ejercicios
Ejercicio 1. Contoso Millas quiere que, al completarse un vuelo, se acrediten las millas al socio, se recalcule su nivel y se envíe un resumen mensual; además, el equipo de datos quiere todos los eventos de vuelo completado para su modelo de predicción, y los tornos de embarque emiten 400 lecturas por segundo que hay que archivar y visualizar en directo.
- Asigna cada necesidad a un servicio y justifica la elección con la distinción mensaje/evento.
- Diseña el tema, las suscripciones y sus filtros para la parte de Service Bus.
- Indica las etiquetas obligatorias y una medida de ahorro para el entorno de desarrollo.
Ejercicio 2. La suscripción facturacion acumula 12.000 mensajes en su cola de mensajes con problemas de entrega. Al inspeccionarlos, todos son del mismo día y tienen DeadLetterReason: MaxDeliveryCountExceeded.
- ¿Qué ocurrió y por qué la cola principal siguió funcionando?
- Describe el procedimiento para recuperarlos, indicando qué hay que verificar antes de reenviar.
- Propón dos medidas para que no vuelva a pasar y una alerta.
Ejercicio 3. Un consumidor de reservas-confirmadas tarda unos 8 minutos en procesar cada mensaje porque llama a un sistema externo lento. El bloqueo de Service Bus es de 5 minutos. Explica qué está pasando, qué se ve en producción y da dos soluciones de naturaleza distinta.
Soluciones
Solución 1:
- Acreditar millas y recalcular el nivel son mensajes: son instrucciones que tienen que ejecutarse y no pueden perderse, así que Service Bus. El aviso al equipo de datos es un evento —«vuelo completado», un hecho al que se suscribe quien quiera, y mañana quizá otros— así que Event Grid. Las 400 lecturas por segundo de los tornos son telemetría de alto volumen que hay que archivar y ver en directo: Event Hubs con captura al Data Lake y un segundo grupo de consumidores para el panel.
- Tema
vuelos-completadosensb-contoso-pro, con la suscripciónmillassin filtro y la suscripciónnivel-fidelidadcon el filtroesSocio = true AND millasAcreditadas > 0. Ambas conmax-delivery-count 5y envío a la subcola activado. El resumen mensual no es una suscripción: es un proceso programado, y meterlo aquí sería confundir mensajería con planificación. entorno,proyecto=contoso-millas,centro-coste=CC-2077ypropietario. Ahorro en desarrollo: nivel Estándar en lugar de Premium para Service Bus, una sola unidad de rendimiento sin auto-inflate en Event Hubs con retención de 24 horas, y eliminar los espacios de nombres cuando no se usen, porque facturan aunque estén vacíos.
Solución 2:
- El consumidor de facturación falló de forma sistemática durante ese día —un despliegue con un error, o el sistema de facturación caído—. Cada mensaje se reintentó 5 veces y, al superar
max-delivery-count, Service Bus lo movió automáticamente a la subcola. Justo por eso la cola principal siguió funcionando: sin ese mecanismo, el primer mensaje envenenado habría bloqueado el proceso y el resto de suscripciones también se habrían visto afectadas. La subcola hizo exactamente su trabajo. - Antes de reenviar nada, hay que verificar tres cosas: que la causa raíz está corregida y desplegada, que el consumidor es idempotente —porque parte de esos mensajes pueden haberse procesado parcialmente— y qué dice
DeadLetterReasonyDeadLetterErrorDescriptionde una muestra representativa, por si hay más de una causa mezclada. Después se leen los mensajes de la subcola y se reenvían al tema en lotes controlados, vigilando la cola principal para no repetir el atasco. - Medidas: (a) distinguir en el código los errores permanentes de los transitorios y mandar los primeros a la subcola de inmediato, sin agotar cinco intentos; (b) un interruptor que pause el consumidor cuando la tasa de fallos supera un umbral, para que los mensajes esperen en la cola en lugar de quemar sus reintentos contra un sistema caído. La alerta: sobre la longitud de la subcola, con umbral bajo —diez mensajes— y aviso al equipo de guardia, porque 12.000 mensajes significan que nadie miraba.
Solución 3: El bloqueo caduca a los 5 minutos mientras el consumidor sigue trabajando; Service Bus da el mensaje por no procesado y lo entrega a otro consumidor. Cuando el primero termina y llama a CompleteMessageAsync, falla con un error de bloqueo perdido. En producción se ve como mensajes procesados dos y tres veces, un contador de entregas que crece hasta agotarse y mensajes que acaban en la subcola pese a haberse procesado correctamente. Solución (a), táctica: renovar el bloqueo automáticamente mientras se trabaja (MaxAutoLockRenewalDuration) y ampliar la duración del bloqueo de la suscripción. Solución (b), de diseño y mejor: sacar la llamada lenta del consumidor; completar el mensaje enseguida y delegar la parte lenta en un flujo aparte —una orquestación de Durable Functions o una segunda cola—, porque tener un mensaje bloqueado ocho minutos limita el rendimiento del sistema entero.
Conclusión
Has resuelto el problema con el que empezaba la lección. Sabes por qué desacoplar: la disponibilidad se multiplica a la baja, la latencia se suma y el acoplamiento congela la evolución; con un intermediario, la compra hace lo imprescindible y publica, y un componente accesorio caído deja de tumbar el ingreso principal. Tienes clara la distinción que casi todo el mundo confunde: un mensaje es una instrucción con destinatario que no se puede perder; un evento es un hecho en pasado que el emisor publica sin saber quién reacciona. Y sabes que el nombre lo delata.
Manejas los tres servicios con criterio. Service Bus para mensajería empresarial fiable: colas frente a temas con suscripciones y filtros SQL, sesiones para ordenar, bloqueo con vistazo como modo de recepción correcto, aplazamiento, cola de mensajes con problemas de entrega —la pieza que salva a los equipos—, transacciones, detección de duplicados y sus tres niveles, con el código de emisión y consumo de reservas-confirmadas distinguiendo el error permanente del transitorio. Event Grid para distribuir eventos discretos: temas del sistema que emiten sin configurar nada —el BlobCreated de sttarjetascontosopro—, temas personalizados, esquema CloudEvents, filtrado en el servicio y el destino de eventos fallidos que hay que configurar o los eventos se pierden en silencio. Y Event Hubs para ingesta masiva: particiones y orden dentro de la partición, grupos de consumidores independientes, retención que permite reprocesar, captura automática hacia la capa bronce de stlagocontosopro y compatibilidad con Kafka. Sin olvidar que las humildes colas de Storage bastan cuando hay un solo consumidor y ninguna exigencia.
Has diseñado el flujo completo de Contoso viendo cada decisión, y te llevas las tres consecuencias inevitables: entrega al menos una vez —la entrega exactamente una vez no existe, lo que existe es procesamiento idempotente—, ordenación solo por sesión o por partición, e idempotencia obligatoria en todo consumidor. Más el coste de cada servicio y lo que lo dispara: el sondeo en bucle, las suscripciones sin filtrar y el escalado automático que sube y no baja.
Con esto, la plataforma de Contoso está modernizada: contenedores, orquestación, funciones, integraciones y una arquitectura de eventos que la sostiene. Queda dar el último paso del módulo, y es de otra naturaleza. Contoso tiene ahora datos que antes no podía aprovechar —miles de reclamaciones de pasajeros escritas en texto libre, pasaportes que un agente teclea a mano en el check-in, avisos de embarque que solo se dan en dos idiomas, un portal que no se entiende fuera de España— y para todo eso existen servicios listos. La lección siguiente, Servicios de IA de Azure, recorre ese catálogo, implementa los casos concretos de Contoso, explica Azure OpenAI Service y la generación aumentada por recuperación para el asistente de atención al pasajero, y dedica el espacio que merece a lo que no es opcional: sesgos, alucinaciones, privacidad de los datos que se envían al servicio y la revisión previa por los equipos legal y de compliance.
Curso de Azure
Módulo 1: Introducción a Azure
- ¿Qué es Azure?
- Modelos de servicio, regiones y zonas de disponibilidad
- Crear y configurar tu cuenta de Azure
- Recorrido por el portal de Azure
- Azure Resource Manager: suscripciones, grupos de recursos y etiquetas
- Azure CLI, PowerShell y Cloud Shell
Módulo 2: Servicios principales de Azure
- Máquinas virtuales de Azure
- Escalado y alta disponibilidad del cómputo
- Azure App Service
- Azure Storage: blobs, archivos, colas y tablas
- Redes en Azure: redes virtuales, subredes y NSG
- Conectividad híbrida y entrega global
Módulo 3: Bases de datos de Azure
- Elegir el servicio de datos adecuado
- Azure SQL Database
- Azure Cosmos DB
- Azure Database for MySQL
- Azure Database for PostgreSQL
- Analítica de datos: Data Lake, Data Factory y Synapse
Módulo 4: Seguridad en Azure
- Microsoft Entra ID y gestión de identidades
- RBAC e identidades administradas
- Azure Key Vault
- Protección DDoS y firewall de aplicaciones web
- Microsoft Defender for Cloud
- Gobernanza y cumplimiento con Azure Policy
Módulo 5: Azure DevOps
- Introducción a Azure DevOps
- Azure Repos
- Azure Pipelines: integración continua
- Despliegue continuo con entornos y aprobaciones
- Azure Artifacts
- Infraestructura como código con Bicep
Módulo 6: Servicios avanzados de Azure
- Contenedores en Azure: Container Registry y Container Apps
- Azure Kubernetes Service (AKS)
- Azure Functions
- Azure Logic Apps
- Mensajería y eventos: Service Bus, Event Grid y Event Hubs
- Servicios de IA de Azure
Módulo 7: Monitoreo y gestión
- Azure Monitor: métricas, alertas y paneles
- Log Analytics y consultas KQL
- Application Insights
- Azure Automation y runbooks
- Copias de seguridad y recuperación ante desastres
Módulo 8: Gestión y optimización de costos
- Calculadora de precios y estimación de costes
- Azure Cost Management: análisis, presupuestos y alertas
- Reservas, planes de ahorro y Azure Hybrid Benefit
- Azure Advisor
- Estrategias de optimización y cultura FinOps
