q,ex,binding scaffolded
This commit is contained in:
@@ -0,0 +1 @@
|
|||||||
|
__pycache__
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
3.13
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
Links\
|
||||||
|
This page is very good and very structured
|
||||||
|
https://www.rabbitmq.com/tutorials
|
||||||
|
|
||||||
|
What is it?\
|
||||||
|
It's a queue - message broker is the technical term.
|
||||||
|
|
||||||
|
Once you up, dashboard is on :15672, username:pass = guest:guest
|
||||||
|
|
||||||
|
You never push directly to the q, it's always via exchanges.
|
||||||
@@ -24,66 +24,68 @@ By the end, you will have a real event flow running through RabbitMQ.
|
|||||||
## Lab Stack
|
## Lab Stack
|
||||||
|
|
||||||
- Language: **Python**
|
- Language: **Python**
|
||||||
- Broker: RabbitMQ (Docker)
|
- Tooling: `uv`
|
||||||
|
- Broker: RabbitMQ **4+** (Docker)
|
||||||
- Library: `pika`
|
- Library: `pika`
|
||||||
|
|
||||||
## Step 1: Project Setup (10 min)
|
## Step 1: Project Setup (10 min)
|
||||||
|
|
||||||
- [ ] Create files:
|
- [x] Create files:
|
||||||
- [ ] `docker-compose.yml`
|
- [x] `docker-compose.yml`
|
||||||
- [ ] `requirements.txt`
|
- [x] `pyproject.toml` (created by `uv init`)
|
||||||
- [ ] `producer.py`
|
- [x] `producer.py`
|
||||||
- [ ] `worker.py`
|
- [x] `worker.py`
|
||||||
- [ ] `notifier.py`
|
- [x] `notifier.py`
|
||||||
|
|
||||||
- [ ] Put this in `requirements.txt`:
|
- [x] Initialize project and dependency with `uv`:
|
||||||
|
|
||||||
```txt
|
```bash
|
||||||
pika==1.3.2
|
uv init --name rabbitmq-lab
|
||||||
|
uv add pika==1.3.2
|
||||||
```
|
```
|
||||||
|
|
||||||
- [ ] Put this in `docker-compose.yml`:
|
- [x] Put this in `docker-compose.yml`:
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
version: "3.9"
|
version: "3.9"
|
||||||
services:
|
services:
|
||||||
rabbitmq:
|
rabbitmq:
|
||||||
image: rabbitmq:3-management
|
image: rabbitmq:4-management
|
||||||
ports:
|
ports:
|
||||||
- "5672:5672"
|
- "5672:5672"
|
||||||
- "15672:15672"
|
- "15672:15672"
|
||||||
```
|
```
|
||||||
|
|
||||||
- [ ] Start RabbitMQ:
|
- [x] Start RabbitMQ:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker compose up -d
|
docker compose up -d
|
||||||
```
|
```
|
||||||
|
|
||||||
- [ ] Open UI: `http://localhost:15672` (`guest` / `guest`).
|
- [x] Open UI: `http://localhost:15672` (`guest` / `guest`).
|
||||||
|
|
||||||
## Step 2: RabbitMQ Topology (15 min)
|
## Step 2: RabbitMQ Topology (15 min)
|
||||||
|
|
||||||
Create these resources from code (preferred) or UI:
|
Create these resources from code (preferred) or UI:
|
||||||
|
|
||||||
- [ ] Exchange `orders.x` (direct)
|
- [x] Exchange `orders.x` (direct)
|
||||||
- [ ] Exchange `orders.dlx` (direct)
|
- [x] Exchange `orders.dlx` (direct)
|
||||||
- [ ] Queue `orders.q`
|
- [x] Queue `orders.q`
|
||||||
- [ ] Queue `orders.processed.q`
|
- [x] Queue `orders.processed.q`
|
||||||
- [ ] Queue `orders.failed.q`
|
- [x] Queue `orders.failed.q`
|
||||||
- [ ] Queue `orders.dlq`
|
- [x] Queue `orders.dlq`
|
||||||
|
|
||||||
Bindings:
|
Bindings:
|
||||||
|
|
||||||
- [ ] `orders.q` <- `orders.x` with `order.created`
|
- [x] `orders.q` <- `orders.x` with `order.created`
|
||||||
- [ ] `orders.processed.q` <- `orders.x` with `order.processed`
|
- [x] `orders.processed.q` <- `orders.x` with `order.processed`
|
||||||
- [ ] `orders.failed.q` <- `orders.x` with `order.failed`
|
- [x] `orders.failed.q` <- `orders.x` with `order.failed`
|
||||||
- [ ] `orders.dlq` <- `orders.dlx` with `order.dead`
|
- [x] `orders.dlq` <- `orders.dlx` with `order.dead`
|
||||||
|
|
||||||
Queue args for `orders.q`:
|
Queue args for `orders.q`:
|
||||||
|
|
||||||
- [ ] `x-dead-letter-exchange=orders.dlx`
|
- [x] `x-dead-letter-exchange=orders.dlx`
|
||||||
- [ ] `x-dead-letter-routing-key=order.dead`
|
- [x] `x-dead-letter-routing-key=order.dead`
|
||||||
|
|
||||||
## Step 3: Producer (20 min)
|
## Step 3: Producer (20 min)
|
||||||
|
|
||||||
@@ -98,7 +100,7 @@ Queue args for `orders.q`:
|
|||||||
|
|
||||||
Success check:
|
Success check:
|
||||||
|
|
||||||
- [ ] Running `python producer.py` prints “Published order …” 5 times.
|
- [ ] Running `uv run producer.py` prints “Published order …” 5 times.
|
||||||
|
|
||||||
## Step 4: Worker (35 min)
|
## Step 4: Worker (35 min)
|
||||||
|
|
||||||
@@ -135,9 +137,9 @@ Success check:
|
|||||||
|
|
||||||
Run in 3 terminals:
|
Run in 3 terminals:
|
||||||
|
|
||||||
- [ ] Terminal A: `python worker.py`
|
- [ ] Terminal A: `uv run worker.py`
|
||||||
- [ ] Terminal B: `python notifier.py`
|
- [ ] Terminal B: `uv run notifier.py`
|
||||||
- [ ] Terminal C: `python producer.py`
|
- [ ] Terminal C: `uv run producer.py`
|
||||||
|
|
||||||
Expected behavior:
|
Expected behavior:
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
services:
|
||||||
|
rabbitmq:
|
||||||
|
# management basically has nicer clis and a dashboard for seeing the q
|
||||||
|
image: rabbitmq:4.2.5-management
|
||||||
|
ports:
|
||||||
|
- 5672:5672 # actual container
|
||||||
|
- 15672:15672 # web dashboard
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
import pika
|
||||||
|
|
||||||
|
# this is the physical conn
|
||||||
|
connection = pika.BlockingConnection(pika.ConnectionParameters("localhost"))
|
||||||
|
# a channel is a lightweight multiplexer over it
|
||||||
|
channel = connection.channel()
|
||||||
|
|
||||||
|
# first the exchanges
|
||||||
|
|
||||||
|
# this is A DECLARE, basically so can run multuple times
|
||||||
|
# .x = exchange
|
||||||
|
channel.exchange_declare(
|
||||||
|
exchange="orders.x", exchange_type="direct", durable=True)
|
||||||
|
# .dlx = dead letter exchange for failed messages
|
||||||
|
channel.exchange_declare(exchange="orders.dlx",
|
||||||
|
exchange_type="direct", durable=True)
|
||||||
|
# the exchange_types are
|
||||||
|
# - direct: exact key match for routing
|
||||||
|
# - topic: pattern based
|
||||||
|
# - fanout: all
|
||||||
|
# - headers: defined in headers
|
||||||
|
|
||||||
|
# then the queues
|
||||||
|
# when changing settings, you might need to delete q from adming
|
||||||
|
# as it'll aready exist and this declare does not delete
|
||||||
|
channel.queue_declare(
|
||||||
|
queue="orders.q",
|
||||||
|
durable=True,
|
||||||
|
arguments={
|
||||||
|
# what to do with "dead letters"
|
||||||
|
# which can be when consumder signals issue
|
||||||
|
# or q overflow
|
||||||
|
# or ttl stuff
|
||||||
|
"x-dead-letter-exchange": "orders.dlx",
|
||||||
|
"x-dead-letter-routing-key": "order.dead",
|
||||||
|
},
|
||||||
|
)
|
||||||
|
channel.queue_declare(queue="orders.processed.q", durable=True)
|
||||||
|
channel.queue_declare(queue="orders.failed.q", durable=True)
|
||||||
|
channel.queue_declare(queue="orders.dlq", durable=True)
|
||||||
|
|
||||||
|
# then the bindings
|
||||||
|
# these are rules on how exchange will route what message
|
||||||
|
# read as, on order.x, if key is order.created, push to orders.q
|
||||||
|
channel.queue_bind(exchange="orders.x", queue="orders.q",
|
||||||
|
routing_key="order.created")
|
||||||
|
channel.queue_bind(exchange="orders.x", queue="orders.processed.q",
|
||||||
|
routing_key="order.processed")
|
||||||
|
channel.queue_bind(exchange="orders.x", queue="orders.failed.q",
|
||||||
|
routing_key="order.failed")
|
||||||
|
channel.queue_bind(exchange="orders.dlx", queue="orders.dlq",
|
||||||
|
routing_key="order.dead")
|
||||||
|
|
||||||
|
|
||||||
|
connection.close()
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
[project]
|
||||||
|
name = "learning-rabbitmq"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "Add your description here"
|
||||||
|
readme = "README.md"
|
||||||
|
requires-python = ">=3.13"
|
||||||
|
dependencies = [
|
||||||
|
"pika>=1.3.2",
|
||||||
|
]
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
version = 1
|
||||||
|
revision = 3
|
||||||
|
requires-python = ">=3.13"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "learning-rabbitmq"
|
||||||
|
version = "0.1.0"
|
||||||
|
source = { virtual = "." }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "pika" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[package.metadata]
|
||||||
|
requires-dist = [{ name = "pika", specifier = ">=1.3.2" }]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pika"
|
||||||
|
version = "1.3.2"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/db/db/d4102f356af18f316c67f2cead8ece307f731dd63140e2c71f170ddacf9b/pika-1.3.2.tar.gz", hash = "sha256:b2a327ddddf8570b4965b3576ac77091b850262d34ce8c1d8cb4e4146aa4145f", size = 145029, upload-time = "2023-05-05T14:25:43.368Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f9/f3/f412836ec714d36f0f4ab581b84c491e3f42c6b5b97a6c6ed1817f3c16d0/pika-1.3.2-py3-none-any.whl", hash = "sha256:0779a7c1fafd805672796085560d290213a465e4f6f76a6fb19e378d8041a14f", size = 155415, upload-time = "2023-05-05T14:25:41.484Z" },
|
||||||
|
]
|
||||||
Reference in New Issue
Block a user