RabbitMQ + .NET 9 Pub/Sub (v7 async client)

Minimal publisher + consumer. Durable topic exchange, persistent messages, manual ACKs.

View on GitHub

RabbitMQ .NET 9 demo: Postman request → Producer publishes → Consumer receives and ACKs; RabbitMQ queue blips

Quickstart (3 commands + Postman)

  1. Start RabbitMQ:
    docker compose up -d

    Management UI: http://localhost:15672 (demo/demo)

  2. Run the Consumer:
    dotnet run --project src/ConsumerWorker/ConsumerWorker/ConsumerWorker.csproj
  3. Run the API (publisher):
    dotnet run --project src/ProducerApi/ProducerApi/ProducerApi.csproj
  4. In Postman, send a request:
    • Method: POST
    • URL: http://localhost:5187/orders
    • Headers: Content-Type: application/json
    • Body (raw → JSON):
    {
      "amount": 149.99,
      "customerId": "CUST-1001"
    }

    You should see 202 Accepted in Postman, a “Published …” log in the API console, and a “Received … ACK” log in the consumer console.

Prefer curl? (optional)
curl -X POST http://localhost:5187/orders ^
  -H "Content-Type: application/json" ^
  -d "{ \"amount\": 149.99, \"customerId\": \"CUST-1001\" }"

Topology

  • Exchange: demo.events (topic, durable)
  • Routing key: order.created.v1
  • Queue: demo.order.created.q (durable, prefetch=10, manual ACK)
  • Messages: persistent (DeliveryMode = 2)

Step-by-step clips

Clip 1 — Project overview: ProducerAPI + ConsumerWorker with v7 async API highlights
Clip 1 — Project overview (ProducerAPI + ConsumerWorker) ~1.9 MB
Clip 2 — Start RabbitMQ via Docker Compose; management UI reachable
Clip 2 — Start RabbitMQ via docker-compose ~470 KB
Clip 3 — Start the ConsumerWorker; waiting with durable queue and prefetch
Clip 3 — Start the ConsumerWorker (first) ~120 KB
Clip 4 — Start the ProducerAPI; API listening on fixed port
Clip 4 — Start the ProducerAPI ~109 KB
Clip 5 — Postman POST /orders → Producer publishes event → Consumer receives + ACKs; queue depth blips
Clip 5 — Send request in Postman (end-to-end) ~1.36 MB

All GIFs are kept short (≤40s) and ~960px wide for quick loading.

Key code (v7 async)

// Producer (excerpt)
await using var conn = await factory.CreateConnectionAsync();
await using var ch   = await conn.CreateChannelAsync();
await ch.ExchangeDeclareAsync("demo.events", ExchangeType.Topic, durable: true, autoDelete: false);
await ch.BasicPublishAsync(exchange, routingKey, mandatory: true, basicProperties: props, body: body);

// Consumer (excerpt)
await ch.BasicQosAsync(0, prefetchCount: 10, global: false);
var consumer = new AsyncEventingBasicConsumer(ch);
consumer.ReceivedAsync += async (_, ea) => {
  var json = Encoding.UTF8.GetString(ea.Body.ToArray());
  var msg  = JsonSerializer.Deserialize<OrderCreated>(json);
  await ch.BasicAckAsync(ea.DeliveryTag, false);
};
await ch.BasicConsumeAsync("demo.order.created.q", autoAck: false, consumer);