Interact with LavinMQ and explore AMQP concepts.
This will create a user and vhost in the broker.
When you click the button below, it will populate queues named "<city>" (for
the cities Berlin and Munich) with
some available taxis.
| Taxi | Price | To | From | Group Name |
|---|
TaxiHub is a fictional ride-hailing startup operating in Berlin and Munich. You're the new backend engineer, and the product team needs a booking pipeline and a driver-comms system glued together using LavinMQ. Each task adds one small service.
First, head to the Register tab and pick a name — this creates your user and vhost on the broker. Use that same name in the AMQP URL below (user, password, and vhost are all the same value).
AMQP URL
amqps://<name>:<name>@wearedevelopers.lmq.cloudamqp.com/<name>
Management UI: wearedevelopers.lmq.cloudamqp.com — use this throughout to inspect queues, exchanges, bindings, and messages.
Client libraries: any AMQP 0.9.1 client works —
amqp-client.rb (Ruby),
pika (Python),
amqp-client.js (Node.js),
amqp-client.cr (Crystal),
amqp091-go (Go),
rabbitmq-java-client (Java),
rabbitmq-dotnet-client (.NET), and so on.
Task 1 — a 3-step booking pipeline: receive a request → match a taxi
→ price the ride.
Task 2 — a driver-comms broadcaster: route fleet notifications differently
depending on how urgent and how targeted they are (everyone / one city / jump the queue).
Goal. Receive a booking request, compute the route distance, forward it on.
You'll learn. Connecting to LavinMQ, declaring a queue, subscribing, publishing to the default exchange by queue name, and acknowledging messages.
Steps.
booking_requests queue.from (only berlin and
munich are supported) and to.
bookings queue.Try it. Use the Book Taxi button in the Tools tab to send an input message.
Input (booking_requests queue):
{"from": "berlin", "to": "stockholm"}
Output (bookings queue):
{"from": "berlin", "to": "stockholm", "distance": "820"}
Done when. Every Book Taxi click
produces a new message on bookings in the management UI.
Goal. For each booking, claim the next available taxi from that city.
You'll learn. Pulling a single message with basic_get (vs.
subscribing), and handling "no work available" gracefully.
Steps.
bookings queue.from city and pull one message from the queue named after it
— each city has its own queue, named simply berlin or
munich.
matched_taxis.
Setup. Use the Populate Taxis button in the Tools tab to fill each city queue with available taxis.
Heads up. What should happen if the city queue is empty? Two reasonable answers: drop the booking with a log, or requeue it and retry later. Either is fine — just be explicit about the choice.
Input (bookings queue):
{"from": "berlin", "to": "stockholm", "distance": "820"}
Input (city queue berlin or munich, populated by the Populate Taxis button):
{"taxi": "B-KF822"}
Output (matched_taxis queue):
{"from": "berlin", "to": "stockholm", "distance": "820", "taxi": "B-KF822"}
Done when. Each booking consumes exactly one taxi message and produces one
matched_taxis message.
Goal. Attach a price and finalise the booking.
You'll learn. Chaining services through queues — the value of small, single-purpose services.
Steps.
matched_taxis.rand, or
base + distance * rate).
confirmed_bookings.matched_taxis queue):
{"from": "berlin", "to": "stockholm", "distance": "820", "taxi": "B-KF822"}
Output (confirmed_bookings queue):
{"from": "berlin", "to": "stockholm", "distance": "820", "taxi": "B-KF822", "price": "1337"}
Done when. Confirmed bookings appear in the Confirmed Taxis table in the Tools tab.
You've built TaxiHub's full booking pipeline — request, match, price. Verify the end-to-end flow in the Confirmed Taxis section of the Tools tab.
TaxiHub now needs a way for HQ to talk to drivers. Different messages need different reach: some go to everyone, some go to one city, and some need to jump the queue.
Use the Send Notifications form in the Tools tab to generate input messages for these tasks.
Goal. A "free fika at HQ" message should reach every taxi, regardless of city.
You'll learn. Fanout exchanges (route to all bound queues, ignore routing key), durable exchanges, and bulk-binding queues at startup.
Steps.
<city>_<plate> (e.g. berlin_B-KF822).
news and bind every taxi queue to it.
notifications; when status == "news",
publish {"info": <message>} to the news exchange.
Reflection. Should the news exchange be durable? What breaks if
it isn't and the broker restarts?
notifications queue):
{"city": "berlin", "message": "Free fika at HQ", "status": "news"}
Output (fanout exchange news):
{"info": "Free fika at HQ"}
Done when. The same message appears in every taxi queue in the management UI.
Goal. "More customers needed downtown" should reach only that city's drivers.
You'll learn. Topic exchanges, routing keys, and binding queues to specific keys.
Steps.
alerts.<city>.<type>
(e.g. berlin.alert). Bind each taxi queue with the wildcard
<city>.* (so all berlin_* queues bind
berlin.* and receive every Berlin message, and likewise for
munich).
notifications; when status == "alert",
publish {"info": <message>} to alerts using
<city>.<status> (e.g. berlin.alert) as the
routing key.
Heads up. If your 2a service is still running, both will pull from
notifications and silently drop each other's messages (competing
consumers). Stop 2a first, or merge both handlers into one service.
notifications queue):
{"city": "berlin", "message": "More customers needed downtown", "status": "alert"}
Output (topic exchange alerts, routing key
<city>.<status>):
{"info": "More customers needed downtown"}
Done when. Only that city's taxi queues receive the message — the other city's queues stay empty.
Goal. "Road blocked, reroute now" should land at the front of each driver's queue, ahead of normal alerts already waiting.
You'll learn. Queue arguments (x-max-priority), the publish-time
priority property, and why queue arguments are immutable after creation.
Steps.
x-max-priority: 255 and observe what happens — you'll get
PRECONDITION_FAILED (406). Queue arguments can't be changed after a
queue is created.
<city>_<plate> queue and recreate it with
x-max-priority: 255. Valid range is 0–255; higher numbers mean
sooner delivery.
alerts topic exchange with the same
<city>.* wildcard as in 2b.
notifications; when status == "crisis",
publish {"info": <message>} to alerts with routing
key <city>.<status> (e.g. berlin.crisis) and
message property priority: 255.
Heads up. The 406 closes the channel it was sent on. Use a throwaway channel for the delete-and-recreate dance so your main consumer channel survives.
Input (notifications queue):
{"city": "berlin", "message": "Crash blocking Unter den Linden", "status": "crisis"}
Output (topic exchange alerts, routing key
<city>.<status>, priority 255):
{"info": "Crash blocking Unter den Linden"}
Done when. Send a normal alert and then a crisis to the same city — the crisis message sits ahead of the alert in the queue (visible via the management UI's Get messages action on a taxi queue).
A reroute for a jam that's already cleared is worse than no message at all. Make stale notifications disappear on their own — first per message, then across the whole fleet at once.
Goal. A "reroute now" that nobody consumes within a short window should vanish, not sit around waiting to misdirect a driver later.
You'll learn. Per-message TTL via the publish-time expiration
property (milliseconds, as a string), and how the broker drops expired messages.
Steps.
expiration of e.g.
"30000" (30 seconds) when publishing to alerts.
Reflection. Per-message TTL vs a per-queue x-message-ttl —
when would you reach for each?
notifications queue):
{"city": "berlin", "message": "Crash blocking Unter den Linden", "status": "crisis"}
Output (topic exchange alerts, routing key
<city>.<status>, priority 255, expiration
30000):
{"info": "Crash blocking Unter den Linden"}
Done when. An unconsumed crisis message disappears from the taxi queue once the TTL passes — the message count drops back to zero on its own.
Goal. Give every taxi queue a message TTL so old notifications age out, without redeclaring dozens of queues one by one.
You'll learn. Policies: apply arguments (like message-ttl) to many
existing queues by name pattern, changeable at any time, unlike the queue
arguments in 2c, which are fixed at creation.
Steps.
^(berlin|munich)_) with definition
{"message-ttl": 60000} and apply-to queues.
PUT /api/policies/<vhost>/<name>).
Reflection. In 2c you had to delete and recreate a queue to change
x-max-priority. Why can a policy add or change an argument on a live queue
when a redeclare can't?
Done when. Every taxi queue shows the policy applied in the management UI, and messages age out after the TTL. You set it across the whole fleet in one call, without redeclaring anything.