RO EN

Azure Service Bus (1) — de la HTTP sincron la mesaje

Azure Service Bus (1) — de la HTTP sincron la mesaje ✨ Imagine generată cu AI
Doru Bulubașa
21 iulie 2026
56 vizualizări

Acesta e primul din patru articole despre Azure Service Bus. Aici acoperim fundamentele: când treci de la HTTP sincron la mesaje și cum alegi între Queue și Topic. În părțile următoare: producători și consumatori în .NET, dead-letter queue cu retry policies și Outbox pattern.


Problema cu apelurile HTTP sincrone

Scenariul clasic: un client plasează o comandă. Serviciul de comenzi apelează serviciul de plăți, apoi pe cel de stocuri, apoi pe cel de notificări — toate prin HTTP, toate sincron, toate în același request.

// Anti-pattern: lant de apeluri sincrone in acelasi request
public async Task<IActionResult> PlaceOrder(OrderRequest request)
{
    var order = await _orderRepository.CreateAsync(request);

    await _paymentClient.ChargeAsync(order);        // 1. daca pica?
    await _inventoryClient.ReserveAsync(order);     // 2. daca pica dupa plata?
    await _notificationClient.SendEmailAsync(order); // 3. chiar trebuie sa astepte user-ul?

    return Ok(order);
}

Ce e în neregulă aici:

  • Cuplare temporală — toate cele trei servicii trebuie să fie disponibile în același moment. Serviciul de notificări e picat 2 minute? Nicio comandă nu se poate plasa.
  • Latența se adună — user-ul așteaptă suma latențelor tuturor apelurilor, inclusiv pentru operații care nu-l privesc (email-ul poate pleca și peste 5 secunde).
  • Eșecuri parțiale greu de gestionat — plata a reușit, dar rezervarea de stoc a eșuat. Acum ce faci? Refund? Retry? În ce ordine?
  • Scalare cuplată — un vârf de comenzi lovește simultan toate serviciile din lanț, chiar dacă doar unul e bottleneck-ul.

Ce schimbă messaging-ul asincron

Cu mesaje, serviciul de comenzi publică un eveniment (OrderPlaced) și răspunde imediat. Celelalte servicii consumă evenimentul în ritmul lor:

// Cu messaging: publici evenimentul si raspunzi imediat
public async Task<IActionResult> PlaceOrder(OrderRequest request)
{
    var order = await _orderRepository.CreateAsync(request);

    await _messagePublisher.PublishAsync(new OrderPlacedEvent(order));

    return Accepted(order); // 202 -- procesarea continua asincron
}

Ce câștigi:

  • Decuplare temporală — serviciul de notificări e picat? Mesajele îl așteaptă în coadă. Când revine, le procesează. Nicio comandă pierdută.
  • Latență minimă pentru user — răspunzi după ce comanda e salvată, restul se întâmplă în fundal.
  • Load leveling — un vârf de 1000 de comenzi devine o coadă pe care consumatorii o procesează în ritmul lor, nu un tsunami simultan pe toate serviciile.
  • Retry natural — un mesaj eșuat se reprocesează automat, cu politici de retry configurabile (partea 3 din serie).

Când NU folosești mesaje

Echilibrul contează. Rămâi pe HTTP sincron când:

  • Ai nevoie de răspuns imediat — o validare de card, o verificare de stoc în timp real pentru afișare. Request/response e modelul corect.
  • Operația e o interogare — citirile sunt prin definiție sincrone; mesajele sunt pentru comenzi și evenimente, nu pentru query-uri.
  • Fluxul e simplu și intern — două servicii care comunică rar nu justifică infrastructura de messaging.

Azure Service Bus — Queue vs. Topic/Subscription

Service Bus oferă două modele de livrare, iar alegerea între ele e prima decizie de design.

Queue — un producător, un consumator (competing consumers)

Mesajul intră în coadă și e procesat de exact un consumator. Dacă rulezi 5 instanțe ale consumatorului, ele concurează pentru mesaje — fiecare mesaj ajunge la o singură instanță. Modelul natural pentru distribuirea de lucru:

Producator --> [ Queue: orders ] --> Consumator (instanta 1, 2 sau 3)

Use case tipic: procesare comenzi, generare rapoarte, orice „work item” care trebuie executat o singură dată.

Topic/Subscription — un producător, mai mulți consumatori (pub/sub)

Mesajul e publicat pe un topic, iar fiecare subscription primește propria copie. Trei servicii interesate de OrderPlaced? Trei subscriptions, fiecare cu copia lui, procesate independent:

                              +--> [ Sub: payments ]      --> Serviciu plati
Producator --> [ Topic ] -----+--> [ Sub: inventory ]     --> Serviciu stocuri
                              +--> [ Sub: notifications ] --> Serviciu notificari

Fiecare subscription se comportă ca o coadă proprie: dacă serviciul de stocuri e picat, mesajele lui se acumulează în subscription-ul lui, fără să afecteze plățile sau notificările.

Filtre pe subscription

Un subscription poate primi doar un subset al mesajelor, prin filtre SQL pe proprietăți:

# Subscription care primeste doar comenzile mari
az servicebus topic subscription rule create \
  --resource-group my-rg \
  --namespace-name my-servicebus \
  --topic-name orders \
  --subscription-name high-value-orders \
  --name HighValueFilter \
  --filter-sql-expression "TotalAmount > 1000"

Cum alegi

Criteriu Queue Topic/Subscription
Câți consumă un mesaj Exact unul Fiecare subscription primește o copie
Model mental Distribuire de lucru Difuzare de evenimente
Adăugarea unui nou consumator Concurează cu ceilalți Subscription nou, independent
Exemplu „Procesează această comandă” „S-a plasat o comandă” (cine e interesat reacționează)

Regula practică: dacă mesajul e o comandă („fă X”), folosește Queue. Dacă e un eveniment („s-a întâmplat Y”), folosește Topic — nu știi azi câte servicii vor fi interesate mâine.


Crearea resurselor

# Namespace (Standard tier -- necesar pentru topics)
az servicebus namespace create \
  --name my-servicebus \
  --resource-group my-rg \
  --location westeurope \
  --sku Standard

# Queue
az servicebus queue create \
  --name order-processing \
  --namespace-name my-servicebus \
  --resource-group my-rg

# Topic + subscriptions
az servicebus topic create \
  --name order-events \
  --namespace-name my-servicebus \
  --resource-group my-rg

az servicebus topic subscription create \
  --name inventory \
  --topic-name order-events \
  --namespace-name my-servicebus \
  --resource-group my-rg

Notă de tier: Basic suportă doar queues. Standard adaugă topics. Premium aduce izolare de resurse, latență predictibilă și mesaje până la 100 MB — pentru producție cu volum serios, Premium merită evaluat.


Ce urmează

În partea a doua scriem cod: producători și consumatori în .NET cu Azure.Messaging.ServiceBus, autentificare prin Managed Identity, ServiceBusProcessor pentru consum robust și setările care contează (prefetch, concurrency, lock duration).

Întrebări? Scrie-mi la contact@ludoprogramming.com.