chub-dev 0.1.0 → 0.1.2-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +55 -0
- package/bin/chub-mcp +2 -0
- package/dist/airtable/docs/database/javascript/DOC.md +1437 -0
- package/dist/airtable/docs/database/python/DOC.md +1735 -0
- package/dist/amplitude/docs/analytics/javascript/DOC.md +1282 -0
- package/dist/amplitude/docs/analytics/python/DOC.md +1199 -0
- package/dist/anthropic/docs/claude-api/javascript/DOC.md +503 -0
- package/dist/anthropic/docs/claude-api/python/DOC.md +389 -0
- package/dist/asana/docs/tasks/DOC.md +1396 -0
- package/dist/assemblyai/docs/transcription/DOC.md +1043 -0
- package/dist/atlassian/docs/confluence/javascript/DOC.md +1347 -0
- package/dist/atlassian/docs/confluence/python/DOC.md +1604 -0
- package/dist/auth0/docs/identity/javascript/DOC.md +968 -0
- package/dist/auth0/docs/identity/python/DOC.md +1199 -0
- package/dist/aws/docs/s3/javascript/DOC.md +1773 -0
- package/dist/aws/docs/s3/python/DOC.md +1807 -0
- package/dist/binance/docs/trading/javascript/DOC.md +1315 -0
- package/dist/binance/docs/trading/python/DOC.md +1454 -0
- package/dist/braintree/docs/gateway/javascript/DOC.md +1278 -0
- package/dist/braintree/docs/gateway/python/DOC.md +1179 -0
- package/dist/chromadb/docs/embeddings-db/javascript/DOC.md +1263 -0
- package/dist/chromadb/docs/embeddings-db/python/DOC.md +1707 -0
- package/dist/clerk/docs/auth/javascript/DOC.md +1220 -0
- package/dist/clerk/docs/auth/python/DOC.md +274 -0
- package/dist/cloudflare/docs/workers/javascript/DOC.md +918 -0
- package/dist/cloudflare/docs/workers/python/DOC.md +994 -0
- package/dist/cockroachdb/docs/distributed-db/DOC.md +1500 -0
- package/dist/cohere/docs/llm/DOC.md +1335 -0
- package/dist/datadog/docs/monitoring/javascript/DOC.md +1740 -0
- package/dist/datadog/docs/monitoring/python/DOC.md +1815 -0
- package/dist/deepgram/docs/speech/javascript/DOC.md +885 -0
- package/dist/deepgram/docs/speech/python/DOC.md +685 -0
- package/dist/deepl/docs/translation/javascript/DOC.md +887 -0
- package/dist/deepl/docs/translation/python/DOC.md +944 -0
- package/dist/deepseek/docs/llm/DOC.md +1220 -0
- package/dist/directus/docs/headless-cms/javascript/DOC.md +1128 -0
- package/dist/directus/docs/headless-cms/python/DOC.md +1276 -0
- package/dist/discord/docs/bot/javascript/DOC.md +1090 -0
- package/dist/discord/docs/bot/python/DOC.md +1130 -0
- package/dist/elasticsearch/docs/search/DOC.md +1634 -0
- package/dist/elevenlabs/docs/text-to-speech/javascript/DOC.md +336 -0
- package/dist/elevenlabs/docs/text-to-speech/python/DOC.md +552 -0
- package/dist/firebase/docs/auth/DOC.md +1015 -0
- package/dist/gemini/docs/genai/javascript/DOC.md +691 -0
- package/dist/gemini/docs/genai/python/DOC.md +555 -0
- package/dist/github/docs/octokit/DOC.md +1560 -0
- package/dist/google/docs/bigquery/javascript/DOC.md +1688 -0
- package/dist/google/docs/bigquery/python/DOC.md +1503 -0
- package/dist/hubspot/docs/crm/javascript/DOC.md +1805 -0
- package/dist/hubspot/docs/crm/python/DOC.md +2033 -0
- package/dist/huggingface/docs/transformers/DOC.md +948 -0
- package/dist/intercom/docs/messaging/javascript/DOC.md +1844 -0
- package/dist/intercom/docs/messaging/python/DOC.md +1797 -0
- package/dist/jira/docs/issues/javascript/DOC.md +1420 -0
- package/dist/jira/docs/issues/python/DOC.md +1492 -0
- package/dist/kafka/docs/streaming/javascript/DOC.md +1671 -0
- package/dist/kafka/docs/streaming/python/DOC.md +1464 -0
- package/dist/landingai-ade/docs/api/DOC.md +620 -0
- package/dist/landingai-ade/docs/sdk/python/DOC.md +489 -0
- package/dist/landingai-ade/docs/sdk/typescript/DOC.md +542 -0
- package/dist/landingai-ade/skills/SKILL.md +489 -0
- package/dist/launchdarkly/docs/feature-flags/javascript/DOC.md +1191 -0
- package/dist/launchdarkly/docs/feature-flags/python/DOC.md +1671 -0
- package/dist/linear/docs/tracker/DOC.md +1554 -0
- package/dist/livekit/docs/realtime/javascript/DOC.md +303 -0
- package/dist/livekit/docs/realtime/python/DOC.md +163 -0
- package/dist/mailchimp/docs/marketing/DOC.md +1420 -0
- package/dist/meilisearch/docs/search/DOC.md +1241 -0
- package/dist/microsoft/docs/onedrive/javascript/DOC.md +1421 -0
- package/dist/microsoft/docs/onedrive/python/DOC.md +1549 -0
- package/dist/mongodb/docs/atlas/DOC.md +2041 -0
- package/dist/notion/docs/workspace-api/javascript/DOC.md +1435 -0
- package/dist/notion/docs/workspace-api/python/DOC.md +1400 -0
- package/dist/okta/docs/identity/javascript/DOC.md +1171 -0
- package/dist/okta/docs/identity/python/DOC.md +1401 -0
- package/dist/openai/docs/chat/javascript/DOC.md +407 -0
- package/dist/openai/docs/chat/python/DOC.md +568 -0
- package/dist/paypal/docs/checkout/DOC.md +278 -0
- package/dist/pinecone/docs/sdk/javascript/DOC.md +984 -0
- package/dist/pinecone/docs/sdk/python/DOC.md +1395 -0
- package/dist/plaid/docs/banking/javascript/DOC.md +1163 -0
- package/dist/plaid/docs/banking/python/DOC.md +1203 -0
- package/dist/playwright-community/skills/login-flows/SKILL.md +108 -0
- package/dist/postmark/docs/transactional-email/DOC.md +1168 -0
- package/dist/prisma/docs/orm/javascript/DOC.md +1419 -0
- package/dist/prisma/docs/orm/python/DOC.md +1317 -0
- package/dist/qdrant/docs/vector-search/javascript/DOC.md +1221 -0
- package/dist/qdrant/docs/vector-search/python/DOC.md +1653 -0
- package/dist/rabbitmq/docs/message-queue/javascript/DOC.md +1193 -0
- package/dist/rabbitmq/docs/message-queue/python/DOC.md +1243 -0
- package/dist/razorpay/docs/payments/javascript/DOC.md +1219 -0
- package/dist/razorpay/docs/payments/python/DOC.md +1330 -0
- package/dist/redis/docs/key-value/javascript/DOC.md +1851 -0
- package/dist/redis/docs/key-value/python/DOC.md +2054 -0
- package/dist/registry.json +2817 -0
- package/dist/replicate/docs/model-hosting/DOC.md +1318 -0
- package/dist/resend/docs/email/DOC.md +1271 -0
- package/dist/salesforce/docs/crm/javascript/DOC.md +1241 -0
- package/dist/salesforce/docs/crm/python/DOC.md +1183 -0
- package/dist/search-index.json +1 -0
- package/dist/sendgrid/docs/email-api/javascript/DOC.md +371 -0
- package/dist/sendgrid/docs/email-api/python/DOC.md +656 -0
- package/dist/sentry/docs/error-tracking/javascript/DOC.md +1073 -0
- package/dist/sentry/docs/error-tracking/python/DOC.md +1309 -0
- package/dist/shopify/docs/storefront/DOC.md +457 -0
- package/dist/slack/docs/workspace/javascript/DOC.md +933 -0
- package/dist/slack/docs/workspace/python/DOC.md +271 -0
- package/dist/square/docs/payments/javascript/DOC.md +1855 -0
- package/dist/square/docs/payments/python/DOC.md +1728 -0
- package/dist/stripe/docs/api/DOC.md +1727 -0
- package/dist/stripe/docs/payments/DOC.md +1726 -0
- package/dist/stytch/docs/auth/javascript/DOC.md +1813 -0
- package/dist/stytch/docs/auth/python/DOC.md +1962 -0
- package/dist/supabase/docs/client/DOC.md +1606 -0
- package/dist/twilio/docs/messaging/python/DOC.md +469 -0
- package/dist/twilio/docs/messaging/typescript/DOC.md +946 -0
- package/dist/vercel/docs/platform/DOC.md +1940 -0
- package/dist/weaviate/docs/vector-db/javascript/DOC.md +1268 -0
- package/dist/weaviate/docs/vector-db/python/DOC.md +1388 -0
- package/dist/zendesk/docs/support/javascript/DOC.md +2150 -0
- package/dist/zendesk/docs/support/python/DOC.md +2297 -0
- package/package.json +22 -6
- package/skills/get-api-docs/SKILL.md +84 -0
- package/src/commands/annotate.js +83 -0
- package/src/commands/build.js +12 -1
- package/src/commands/feedback.js +150 -0
- package/src/commands/get.js +83 -42
- package/src/commands/search.js +7 -0
- package/src/index.js +43 -17
- package/src/lib/analytics.js +90 -0
- package/src/lib/annotations.js +57 -0
- package/src/lib/bm25.js +170 -0
- package/src/lib/cache.js +69 -6
- package/src/lib/config.js +8 -3
- package/src/lib/identity.js +99 -0
- package/src/lib/registry.js +103 -20
- package/src/lib/telemetry.js +86 -0
- package/src/mcp/server.js +177 -0
- package/src/mcp/tools.js +251 -0
|
@@ -0,0 +1,1243 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: message-queue
|
|
3
|
+
description: "RabbitMQ Pika coding guidelines for Python message broker interactions"
|
|
4
|
+
metadata:
|
|
5
|
+
languages: "python"
|
|
6
|
+
versions: "1.3.2"
|
|
7
|
+
updated-on: "2026-03-02"
|
|
8
|
+
source: maintainer
|
|
9
|
+
tags: "rabbitmq,queue,amqp,messaging,async"
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# RabbitMQ Pika Coding Guidelines (Python)
|
|
13
|
+
|
|
14
|
+
You are a RabbitMQ Pika coding expert. Help me with writing code using RabbitMQ message broker via the official Pika library.
|
|
15
|
+
|
|
16
|
+
## Golden Rule: Use the Correct and Current SDK
|
|
17
|
+
|
|
18
|
+
Always use the official Pika library for all RabbitMQ (AMQP 0-9-1) interactions in Python.
|
|
19
|
+
|
|
20
|
+
- **Library Name:** Pika
|
|
21
|
+
- **PyPI Package:** `pika`
|
|
22
|
+
- **Current Version:** 1.3.2 or higher
|
|
23
|
+
- **Python Support:** Python 3.9+
|
|
24
|
+
|
|
25
|
+
**Installation:**
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install pika
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
**IMPORTANT:** Pika is the official pure-Python RabbitMQ/AMQP 0-9-1 client library. Do not use deprecated alternatives.
|
|
32
|
+
|
|
33
|
+
**Import Pattern:**
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
import pika
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## Initialization and Connection
|
|
40
|
+
|
|
41
|
+
### Environment Variables
|
|
42
|
+
|
|
43
|
+
Configure RabbitMQ connection using environment variables:
|
|
44
|
+
|
|
45
|
+
```python
|
|
46
|
+
import os
|
|
47
|
+
from dotenv import load_dotenv
|
|
48
|
+
|
|
49
|
+
load_dotenv()
|
|
50
|
+
|
|
51
|
+
# .env file
|
|
52
|
+
RABBITMQ_HOST=localhost
|
|
53
|
+
RABBITMQ_PORT=5672
|
|
54
|
+
RABBITMQ_USER=guest
|
|
55
|
+
RABBITMQ_PASS=guest
|
|
56
|
+
RABBITMQ_VHOST=/
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
### Basic Connection (Blocking)
|
|
60
|
+
|
|
61
|
+
```python
|
|
62
|
+
import pika
|
|
63
|
+
|
|
64
|
+
# Simple connection to localhost
|
|
65
|
+
connection = pika.BlockingConnection(
|
|
66
|
+
pika.ConnectionParameters('localhost')
|
|
67
|
+
)
|
|
68
|
+
channel = connection.channel()
|
|
69
|
+
|
|
70
|
+
print("Connected to RabbitMQ")
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
### Connection with Parameters
|
|
74
|
+
|
|
75
|
+
```python
|
|
76
|
+
import pika
|
|
77
|
+
import os
|
|
78
|
+
|
|
79
|
+
credentials = pika.PlainCredentials(
|
|
80
|
+
username=os.getenv('RABBITMQ_USER', 'guest'),
|
|
81
|
+
password=os.getenv('RABBITMQ_PASS', 'guest')
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
parameters = pika.ConnectionParameters(
|
|
85
|
+
host=os.getenv('RABBITMQ_HOST', 'localhost'),
|
|
86
|
+
port=int(os.getenv('RABBITMQ_PORT', 5672)),
|
|
87
|
+
virtual_host=os.getenv('RABBITMQ_VHOST', '/'),
|
|
88
|
+
credentials=credentials,
|
|
89
|
+
heartbeat=30, # Heartbeat interval in seconds
|
|
90
|
+
blocked_connection_timeout=300
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
connection = pika.BlockingConnection(parameters)
|
|
94
|
+
channel = connection.channel()
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Connection with URL
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
import pika
|
|
101
|
+
import os
|
|
102
|
+
|
|
103
|
+
# Connection URL format: amqp://username:password@host:port/vhost
|
|
104
|
+
url = os.getenv('RABBITMQ_URL', 'amqp://guest:guest@localhost:5672/')
|
|
105
|
+
|
|
106
|
+
parameters = pika.URLParameters(url)
|
|
107
|
+
connection = pika.BlockingConnection(parameters)
|
|
108
|
+
channel = connection.channel()
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Connection Options
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
import pika
|
|
115
|
+
|
|
116
|
+
parameters = pika.ConnectionParameters(
|
|
117
|
+
host='localhost',
|
|
118
|
+
port=5672,
|
|
119
|
+
virtual_host='/',
|
|
120
|
+
credentials=pika.PlainCredentials('guest', 'guest'),
|
|
121
|
+
heartbeat=30, # Heartbeat interval in seconds
|
|
122
|
+
blocked_connection_timeout=300, # Timeout for blocked connection
|
|
123
|
+
connection_attempts=3, # Number of connection attempts
|
|
124
|
+
retry_delay=2, # Delay between retries in seconds
|
|
125
|
+
socket_timeout=10, # Socket timeout in seconds
|
|
126
|
+
stack_timeout=15, # Stack timeout in seconds
|
|
127
|
+
channel_max=2047, # Maximum number of channels
|
|
128
|
+
frame_max=131072, # Maximum frame size
|
|
129
|
+
locale='en_US' # Locale for error messages
|
|
130
|
+
)
|
|
131
|
+
|
|
132
|
+
connection = pika.BlockingConnection(parameters)
|
|
133
|
+
channel = connection.channel()
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
## Core Messaging Patterns
|
|
137
|
+
|
|
138
|
+
### 1. Simple Queue - Producer
|
|
139
|
+
|
|
140
|
+
```python
|
|
141
|
+
import pika
|
|
142
|
+
|
|
143
|
+
connection = pika.BlockingConnection(
|
|
144
|
+
pika.ConnectionParameters('localhost')
|
|
145
|
+
)
|
|
146
|
+
channel = connection.channel()
|
|
147
|
+
|
|
148
|
+
# Declare queue (create if it doesn't exist)
|
|
149
|
+
channel.queue_declare(queue='hello', durable=True)
|
|
150
|
+
|
|
151
|
+
# Send message
|
|
152
|
+
message = 'Hello World!'
|
|
153
|
+
channel.basic_publish(
|
|
154
|
+
exchange='',
|
|
155
|
+
routing_key='hello',
|
|
156
|
+
body=message,
|
|
157
|
+
properties=pika.BasicProperties(
|
|
158
|
+
delivery_mode=pika.DeliveryMode.Persistent # Make message persistent
|
|
159
|
+
)
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
print(f"Sent: {message}")
|
|
163
|
+
|
|
164
|
+
connection.close()
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### 2. Simple Queue - Consumer
|
|
168
|
+
|
|
169
|
+
```python
|
|
170
|
+
import pika
|
|
171
|
+
|
|
172
|
+
connection = pika.BlockingConnection(
|
|
173
|
+
pika.ConnectionParameters('localhost')
|
|
174
|
+
)
|
|
175
|
+
channel = connection.channel()
|
|
176
|
+
|
|
177
|
+
# Declare queue
|
|
178
|
+
channel.queue_declare(queue='hello', durable=True)
|
|
179
|
+
|
|
180
|
+
# Callback function for message processing
|
|
181
|
+
def callback(ch, method, properties, body):
|
|
182
|
+
message = body.decode('utf-8')
|
|
183
|
+
print(f"Received: {message}")
|
|
184
|
+
|
|
185
|
+
# Acknowledge message
|
|
186
|
+
ch.basic_ack(delivery_tag=method.delivery_tag)
|
|
187
|
+
|
|
188
|
+
# Consume messages
|
|
189
|
+
channel.basic_consume(
|
|
190
|
+
queue='hello',
|
|
191
|
+
on_message_callback=callback,
|
|
192
|
+
auto_ack=False # Manual acknowledgment
|
|
193
|
+
)
|
|
194
|
+
|
|
195
|
+
print('Waiting for messages. Press CTRL+C to exit.')
|
|
196
|
+
channel.start_consuming()
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### 3. Work Queue with Prefetch
|
|
200
|
+
|
|
201
|
+
```python
|
|
202
|
+
import pika
|
|
203
|
+
import time
|
|
204
|
+
|
|
205
|
+
connection = pika.BlockingConnection(
|
|
206
|
+
pika.ConnectionParameters('localhost')
|
|
207
|
+
)
|
|
208
|
+
channel = connection.channel()
|
|
209
|
+
|
|
210
|
+
channel.queue_declare(queue='task_queue', durable=True)
|
|
211
|
+
|
|
212
|
+
# Fair dispatch - only send one message at a time
|
|
213
|
+
channel.basic_qos(prefetch_count=1)
|
|
214
|
+
|
|
215
|
+
def callback(ch, method, properties, body):
|
|
216
|
+
message = body.decode('utf-8')
|
|
217
|
+
print(f"Processing: {message}")
|
|
218
|
+
|
|
219
|
+
# Simulate work (one dot = 1 second)
|
|
220
|
+
time.sleep(message.count('.'))
|
|
221
|
+
|
|
222
|
+
print(f"Done: {message}")
|
|
223
|
+
ch.basic_ack(delivery_tag=method.delivery_tag)
|
|
224
|
+
|
|
225
|
+
channel.basic_consume(
|
|
226
|
+
queue='task_queue',
|
|
227
|
+
on_message_callback=callback
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
print('Worker waiting for tasks. Press CTRL+C to exit.')
|
|
231
|
+
channel.start_consuming()
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### 4. Publish/Subscribe with Fanout Exchange
|
|
235
|
+
|
|
236
|
+
**Publisher:**
|
|
237
|
+
|
|
238
|
+
```python
|
|
239
|
+
import pika
|
|
240
|
+
import sys
|
|
241
|
+
|
|
242
|
+
connection = pika.BlockingConnection(
|
|
243
|
+
pika.ConnectionParameters('localhost')
|
|
244
|
+
)
|
|
245
|
+
channel = connection.channel()
|
|
246
|
+
|
|
247
|
+
# Declare fanout exchange
|
|
248
|
+
channel.exchange_declare(exchange='logs', exchange_type='fanout')
|
|
249
|
+
|
|
250
|
+
message = ' '.join(sys.argv[1:]) or "info: Hello World!"
|
|
251
|
+
|
|
252
|
+
# Publish to exchange
|
|
253
|
+
channel.basic_publish(
|
|
254
|
+
exchange='logs',
|
|
255
|
+
routing_key='', # Ignored for fanout exchange
|
|
256
|
+
body=message
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
print(f"Sent: {message}")
|
|
260
|
+
connection.close()
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
**Subscriber:**
|
|
264
|
+
|
|
265
|
+
```python
|
|
266
|
+
import pika
|
|
267
|
+
|
|
268
|
+
connection = pika.BlockingConnection(
|
|
269
|
+
pika.ConnectionParameters('localhost')
|
|
270
|
+
)
|
|
271
|
+
channel = connection.channel()
|
|
272
|
+
|
|
273
|
+
# Declare exchange
|
|
274
|
+
channel.exchange_declare(exchange='logs', exchange_type='fanout')
|
|
275
|
+
|
|
276
|
+
# Create exclusive queue (auto-delete when consumer disconnects)
|
|
277
|
+
result = channel.queue_declare(queue='', exclusive=True)
|
|
278
|
+
queue_name = result.method.queue
|
|
279
|
+
|
|
280
|
+
# Bind queue to exchange
|
|
281
|
+
channel.queue_bind(exchange='logs', queue=queue_name)
|
|
282
|
+
|
|
283
|
+
print(f'Waiting for logs. Queue: {queue_name}')
|
|
284
|
+
|
|
285
|
+
def callback(ch, method, properties, body):
|
|
286
|
+
print(f"Received: {body.decode('utf-8')}")
|
|
287
|
+
|
|
288
|
+
channel.basic_consume(
|
|
289
|
+
queue=queue_name,
|
|
290
|
+
on_message_callback=callback,
|
|
291
|
+
auto_ack=True
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
channel.start_consuming()
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### 5. Routing with Direct Exchange
|
|
298
|
+
|
|
299
|
+
**Emitter:**
|
|
300
|
+
|
|
301
|
+
```python
|
|
302
|
+
import pika
|
|
303
|
+
import sys
|
|
304
|
+
|
|
305
|
+
connection = pika.BlockingConnection(
|
|
306
|
+
pika.ConnectionParameters('localhost')
|
|
307
|
+
)
|
|
308
|
+
channel = connection.channel()
|
|
309
|
+
|
|
310
|
+
# Declare direct exchange
|
|
311
|
+
channel.exchange_declare(exchange='direct_logs', exchange_type='direct')
|
|
312
|
+
|
|
313
|
+
severity = sys.argv[1] if len(sys.argv) > 1 else 'info'
|
|
314
|
+
message = ' '.join(sys.argv[2:]) or 'Hello World!'
|
|
315
|
+
|
|
316
|
+
# Publish with routing key
|
|
317
|
+
channel.basic_publish(
|
|
318
|
+
exchange='direct_logs',
|
|
319
|
+
routing_key=severity,
|
|
320
|
+
body=message
|
|
321
|
+
)
|
|
322
|
+
|
|
323
|
+
print(f"Sent [{severity}]: {message}")
|
|
324
|
+
connection.close()
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
**Receiver:**
|
|
328
|
+
|
|
329
|
+
```python
|
|
330
|
+
import pika
|
|
331
|
+
import sys
|
|
332
|
+
|
|
333
|
+
connection = pika.BlockingConnection(
|
|
334
|
+
pika.ConnectionParameters('localhost')
|
|
335
|
+
)
|
|
336
|
+
channel = connection.channel()
|
|
337
|
+
|
|
338
|
+
channel.exchange_declare(exchange='direct_logs', exchange_type='direct')
|
|
339
|
+
|
|
340
|
+
result = channel.queue_declare(queue='', exclusive=True)
|
|
341
|
+
queue_name = result.method.queue
|
|
342
|
+
|
|
343
|
+
# Severities to listen for
|
|
344
|
+
severities = sys.argv[1:] if len(sys.argv) > 1 else ['error', 'warning']
|
|
345
|
+
|
|
346
|
+
# Bind queue for each severity
|
|
347
|
+
for severity in severities:
|
|
348
|
+
channel.queue_bind(
|
|
349
|
+
exchange='direct_logs',
|
|
350
|
+
queue=queue_name,
|
|
351
|
+
routing_key=severity
|
|
352
|
+
)
|
|
353
|
+
|
|
354
|
+
print(f'Waiting for logs with severities: {", ".join(severities)}')
|
|
355
|
+
|
|
356
|
+
def callback(ch, method, properties, body):
|
|
357
|
+
print(f"[{method.routing_key}]: {body.decode('utf-8')}")
|
|
358
|
+
|
|
359
|
+
channel.basic_consume(
|
|
360
|
+
queue=queue_name,
|
|
361
|
+
on_message_callback=callback,
|
|
362
|
+
auto_ack=True
|
|
363
|
+
)
|
|
364
|
+
|
|
365
|
+
channel.start_consuming()
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
### 6. Topics with Topic Exchange
|
|
369
|
+
|
|
370
|
+
**Emit Log Topic:**
|
|
371
|
+
|
|
372
|
+
```python
|
|
373
|
+
import pika
|
|
374
|
+
import sys
|
|
375
|
+
|
|
376
|
+
connection = pika.BlockingConnection(
|
|
377
|
+
pika.ConnectionParameters('localhost')
|
|
378
|
+
)
|
|
379
|
+
channel = connection.channel()
|
|
380
|
+
|
|
381
|
+
# Declare topic exchange
|
|
382
|
+
channel.exchange_declare(exchange='topic_logs', exchange_type='topic')
|
|
383
|
+
|
|
384
|
+
routing_key = sys.argv[1] if len(sys.argv) > 1 else 'anonymous.info'
|
|
385
|
+
message = ' '.join(sys.argv[2:]) or 'Hello World!'
|
|
386
|
+
|
|
387
|
+
channel.basic_publish(
|
|
388
|
+
exchange='topic_logs',
|
|
389
|
+
routing_key=routing_key,
|
|
390
|
+
body=message
|
|
391
|
+
)
|
|
392
|
+
|
|
393
|
+
print(f"Sent [{routing_key}]: {message}")
|
|
394
|
+
connection.close()
|
|
395
|
+
```
|
|
396
|
+
|
|
397
|
+
**Receive Log Topic:**
|
|
398
|
+
|
|
399
|
+
```python
|
|
400
|
+
import pika
|
|
401
|
+
import sys
|
|
402
|
+
|
|
403
|
+
connection = pika.BlockingConnection(
|
|
404
|
+
pika.ConnectionParameters('localhost')
|
|
405
|
+
)
|
|
406
|
+
channel = connection.channel()
|
|
407
|
+
|
|
408
|
+
channel.exchange_declare(exchange='topic_logs', exchange_type='topic')
|
|
409
|
+
|
|
410
|
+
result = channel.queue_declare(queue='', exclusive=True)
|
|
411
|
+
queue_name = result.method.queue
|
|
412
|
+
|
|
413
|
+
# Binding keys with pattern matching
|
|
414
|
+
# * (star) substitutes for exactly one word
|
|
415
|
+
# # (hash) substitutes for zero or more words
|
|
416
|
+
binding_keys = sys.argv[1:] if len(sys.argv) > 1 else ['#']
|
|
417
|
+
|
|
418
|
+
for binding_key in binding_keys:
|
|
419
|
+
channel.queue_bind(
|
|
420
|
+
exchange='topic_logs',
|
|
421
|
+
queue=queue_name,
|
|
422
|
+
routing_key=binding_key
|
|
423
|
+
)
|
|
424
|
+
|
|
425
|
+
print(f'Waiting for logs matching: {", ".join(binding_keys)}')
|
|
426
|
+
|
|
427
|
+
def callback(ch, method, properties, body):
|
|
428
|
+
print(f"[{method.routing_key}]: {body.decode('utf-8')}")
|
|
429
|
+
|
|
430
|
+
channel.basic_consume(
|
|
431
|
+
queue=queue_name,
|
|
432
|
+
on_message_callback=callback,
|
|
433
|
+
auto_ack=True
|
|
434
|
+
)
|
|
435
|
+
|
|
436
|
+
channel.start_consuming()
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
### 7. RPC (Remote Procedure Call) Pattern
|
|
440
|
+
|
|
441
|
+
**RPC Client:**
|
|
442
|
+
|
|
443
|
+
```python
|
|
444
|
+
import pika
|
|
445
|
+
import uuid
|
|
446
|
+
|
|
447
|
+
class FibonacciRpcClient:
|
|
448
|
+
def __init__(self):
|
|
449
|
+
self.connection = pika.BlockingConnection(
|
|
450
|
+
pika.ConnectionParameters('localhost')
|
|
451
|
+
)
|
|
452
|
+
self.channel = self.connection.channel()
|
|
453
|
+
|
|
454
|
+
# Declare callback queue
|
|
455
|
+
result = self.channel.queue_declare(queue='', exclusive=True)
|
|
456
|
+
self.callback_queue = result.method.queue
|
|
457
|
+
|
|
458
|
+
self.channel.basic_consume(
|
|
459
|
+
queue=self.callback_queue,
|
|
460
|
+
on_message_callback=self.on_response,
|
|
461
|
+
auto_ack=True
|
|
462
|
+
)
|
|
463
|
+
|
|
464
|
+
self.response = None
|
|
465
|
+
self.corr_id = None
|
|
466
|
+
|
|
467
|
+
def on_response(self, ch, method, props, body):
|
|
468
|
+
if self.corr_id == props.correlation_id:
|
|
469
|
+
self.response = body
|
|
470
|
+
|
|
471
|
+
def call(self, n):
|
|
472
|
+
self.response = None
|
|
473
|
+
self.corr_id = str(uuid.uuid4())
|
|
474
|
+
|
|
475
|
+
self.channel.basic_publish(
|
|
476
|
+
exchange='',
|
|
477
|
+
routing_key='rpc_queue',
|
|
478
|
+
properties=pika.BasicProperties(
|
|
479
|
+
reply_to=self.callback_queue,
|
|
480
|
+
correlation_id=self.corr_id
|
|
481
|
+
),
|
|
482
|
+
body=str(n)
|
|
483
|
+
)
|
|
484
|
+
|
|
485
|
+
# Wait for response
|
|
486
|
+
self.connection.process_data_events(time_limit=None)
|
|
487
|
+
while self.response is None:
|
|
488
|
+
self.connection.process_data_events(time_limit=None)
|
|
489
|
+
|
|
490
|
+
return int(self.response)
|
|
491
|
+
|
|
492
|
+
def close(self):
|
|
493
|
+
self.connection.close()
|
|
494
|
+
|
|
495
|
+
# Usage
|
|
496
|
+
fibonacci_rpc = FibonacciRpcClient()
|
|
497
|
+
|
|
498
|
+
print("Requesting fib(30)")
|
|
499
|
+
response = fibonacci_rpc.call(30)
|
|
500
|
+
print(f"Result: {response}")
|
|
501
|
+
|
|
502
|
+
fibonacci_rpc.close()
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
**RPC Server:**
|
|
506
|
+
|
|
507
|
+
```python
|
|
508
|
+
import pika
|
|
509
|
+
|
|
510
|
+
def fib(n):
|
|
511
|
+
if n == 0:
|
|
512
|
+
return 0
|
|
513
|
+
elif n == 1:
|
|
514
|
+
return 1
|
|
515
|
+
else:
|
|
516
|
+
return fib(n - 1) + fib(n - 2)
|
|
517
|
+
|
|
518
|
+
connection = pika.BlockingConnection(
|
|
519
|
+
pika.ConnectionParameters('localhost')
|
|
520
|
+
)
|
|
521
|
+
channel = connection.channel()
|
|
522
|
+
|
|
523
|
+
channel.queue_declare(queue='rpc_queue')
|
|
524
|
+
|
|
525
|
+
def on_request(ch, method, props, body):
|
|
526
|
+
n = int(body)
|
|
527
|
+
print(f"Computing fib({n})")
|
|
528
|
+
|
|
529
|
+
response = fib(n)
|
|
530
|
+
|
|
531
|
+
# Send response
|
|
532
|
+
ch.basic_publish(
|
|
533
|
+
exchange='',
|
|
534
|
+
routing_key=props.reply_to,
|
|
535
|
+
properties=pika.BasicProperties(
|
|
536
|
+
correlation_id=props.correlation_id
|
|
537
|
+
),
|
|
538
|
+
body=str(response)
|
|
539
|
+
)
|
|
540
|
+
|
|
541
|
+
ch.basic_ack(delivery_tag=method.delivery_tag)
|
|
542
|
+
|
|
543
|
+
channel.basic_qos(prefetch_count=1)
|
|
544
|
+
channel.basic_consume(queue='rpc_queue', on_message_callback=on_request)
|
|
545
|
+
|
|
546
|
+
print("Awaiting RPC requests")
|
|
547
|
+
channel.start_consuming()
|
|
548
|
+
```
|
|
549
|
+
|
|
550
|
+
## Advanced Configuration
|
|
551
|
+
|
|
552
|
+
### Message Properties
|
|
553
|
+
|
|
554
|
+
```python
|
|
555
|
+
import pika
|
|
556
|
+
import time
|
|
557
|
+
|
|
558
|
+
connection = pika.BlockingConnection(
|
|
559
|
+
pika.ConnectionParameters('localhost')
|
|
560
|
+
)
|
|
561
|
+
channel = connection.channel()
|
|
562
|
+
|
|
563
|
+
channel.queue_declare(queue='advanced_queue', durable=True)
|
|
564
|
+
|
|
565
|
+
message = 'Important message'
|
|
566
|
+
|
|
567
|
+
# Comprehensive message properties
|
|
568
|
+
properties = pika.BasicProperties(
|
|
569
|
+
delivery_mode=pika.DeliveryMode.Persistent, # Message persistence
|
|
570
|
+
priority=5, # Message priority (0-255)
|
|
571
|
+
content_type='text/plain', # MIME type
|
|
572
|
+
content_encoding='utf-8', # Encoding
|
|
573
|
+
expiration='60000', # TTL in milliseconds
|
|
574
|
+
message_id='msg-123', # Application message ID
|
|
575
|
+
timestamp=int(time.time()), # Timestamp
|
|
576
|
+
type='notification', # Message type
|
|
577
|
+
user_id='guest', # Creating user
|
|
578
|
+
app_id='my-app', # Application ID
|
|
579
|
+
headers={ # Custom headers
|
|
580
|
+
'x-custom-header': 'value',
|
|
581
|
+
'retry-count': 0
|
|
582
|
+
}
|
|
583
|
+
)
|
|
584
|
+
|
|
585
|
+
channel.basic_publish(
|
|
586
|
+
exchange='',
|
|
587
|
+
routing_key='advanced_queue',
|
|
588
|
+
body=message,
|
|
589
|
+
properties=properties
|
|
590
|
+
)
|
|
591
|
+
|
|
592
|
+
connection.close()
|
|
593
|
+
```
|
|
594
|
+
|
|
595
|
+
### Queue Options
|
|
596
|
+
|
|
597
|
+
```python
|
|
598
|
+
import pika
|
|
599
|
+
|
|
600
|
+
connection = pika.BlockingConnection(
|
|
601
|
+
pika.ConnectionParameters('localhost')
|
|
602
|
+
)
|
|
603
|
+
channel = connection.channel()
|
|
604
|
+
|
|
605
|
+
# Queue with advanced options
|
|
606
|
+
channel.queue_declare(
|
|
607
|
+
queue='advanced_queue',
|
|
608
|
+
durable=True, # Queue survives broker restart
|
|
609
|
+
exclusive=False, # Can be accessed by other connections
|
|
610
|
+
auto_delete=False, # Queue won't auto-delete
|
|
611
|
+
arguments={
|
|
612
|
+
'x-message-ttl': 60000, # Message TTL in milliseconds
|
|
613
|
+
'x-expires': 300000, # Queue expires after 5 minutes
|
|
614
|
+
'x-max-length': 1000, # Maximum queue length
|
|
615
|
+
'x-max-priority': 10, # Enable priority queue (0-10)
|
|
616
|
+
'x-dead-letter-exchange': 'dlx', # Dead letter exchange
|
|
617
|
+
'x-dead-letter-routing-key': 'dead.letter', # DLX routing key
|
|
618
|
+
'x-queue-mode': 'lazy' # Lazy queue mode
|
|
619
|
+
}
|
|
620
|
+
)
|
|
621
|
+
|
|
622
|
+
connection.close()
|
|
623
|
+
```
|
|
624
|
+
|
|
625
|
+
### Exchange Options
|
|
626
|
+
|
|
627
|
+
```python
|
|
628
|
+
import pika
|
|
629
|
+
|
|
630
|
+
connection = pika.BlockingConnection(
|
|
631
|
+
pika.ConnectionParameters('localhost')
|
|
632
|
+
)
|
|
633
|
+
channel = connection.channel()
|
|
634
|
+
|
|
635
|
+
# Exchange with advanced options
|
|
636
|
+
channel.exchange_declare(
|
|
637
|
+
exchange='advanced_exchange',
|
|
638
|
+
exchange_type='topic',
|
|
639
|
+
durable=True, # Exchange survives broker restart
|
|
640
|
+
auto_delete=False, # Exchange won't auto-delete
|
|
641
|
+
internal=False, # Can be published to by clients
|
|
642
|
+
arguments={
|
|
643
|
+
'alternate-exchange': 'alternate_exchange' # AE for unroutable messages
|
|
644
|
+
}
|
|
645
|
+
)
|
|
646
|
+
|
|
647
|
+
connection.close()
|
|
648
|
+
```
|
|
649
|
+
|
|
650
|
+
### Dead Letter Queue (DLQ)
|
|
651
|
+
|
|
652
|
+
```python
|
|
653
|
+
import pika
|
|
654
|
+
|
|
655
|
+
connection = pika.BlockingConnection(
|
|
656
|
+
pika.ConnectionParameters('localhost')
|
|
657
|
+
)
|
|
658
|
+
channel = connection.channel()
|
|
659
|
+
|
|
660
|
+
# Declare dead letter exchange
|
|
661
|
+
channel.exchange_declare(exchange='dlx', exchange_type='direct', durable=True)
|
|
662
|
+
|
|
663
|
+
# Declare dead letter queue
|
|
664
|
+
channel.queue_declare(queue='dead_letter_queue', durable=True)
|
|
665
|
+
|
|
666
|
+
# Bind DLQ to DLX
|
|
667
|
+
channel.queue_bind(
|
|
668
|
+
queue='dead_letter_queue',
|
|
669
|
+
exchange='dlx',
|
|
670
|
+
routing_key='dead.letter'
|
|
671
|
+
)
|
|
672
|
+
|
|
673
|
+
# Declare main queue with DLX
|
|
674
|
+
channel.queue_declare(
|
|
675
|
+
queue='main_queue',
|
|
676
|
+
durable=True,
|
|
677
|
+
arguments={
|
|
678
|
+
'x-dead-letter-exchange': 'dlx',
|
|
679
|
+
'x-dead-letter-routing-key': 'dead.letter',
|
|
680
|
+
'x-message-ttl': 10000 # Messages expire after 10 seconds
|
|
681
|
+
}
|
|
682
|
+
)
|
|
683
|
+
|
|
684
|
+
print("Dead letter queue setup complete")
|
|
685
|
+
connection.close()
|
|
686
|
+
```
|
|
687
|
+
|
|
688
|
+
### Consuming from Dead Letter Queue
|
|
689
|
+
|
|
690
|
+
```python
|
|
691
|
+
import pika
|
|
692
|
+
|
|
693
|
+
connection = pika.BlockingConnection(
|
|
694
|
+
pika.ConnectionParameters('localhost')
|
|
695
|
+
)
|
|
696
|
+
channel = connection.channel()
|
|
697
|
+
|
|
698
|
+
channel.queue_declare(queue='dead_letter_queue', durable=True)
|
|
699
|
+
|
|
700
|
+
def callback(ch, method, properties, body):
|
|
701
|
+
print(f"Dead letter received: {body.decode('utf-8')}")
|
|
702
|
+
print(f"Original routing key: {method.routing_key}")
|
|
703
|
+
|
|
704
|
+
# Check death reason
|
|
705
|
+
if properties.headers and 'x-death' in properties.headers:
|
|
706
|
+
print(f"Death reason: {properties.headers['x-death']}")
|
|
707
|
+
|
|
708
|
+
# Process or log dead letter
|
|
709
|
+
ch.basic_ack(delivery_tag=method.delivery_tag)
|
|
710
|
+
|
|
711
|
+
channel.basic_consume(
|
|
712
|
+
queue='dead_letter_queue',
|
|
713
|
+
on_message_callback=callback
|
|
714
|
+
)
|
|
715
|
+
|
|
716
|
+
print('Waiting for dead letter messages. Press CTRL+C to exit.')
|
|
717
|
+
channel.start_consuming()
|
|
718
|
+
```
|
|
719
|
+
|
|
720
|
+
### Message Rejection and Requeuing
|
|
721
|
+
|
|
722
|
+
```python
|
|
723
|
+
import pika
|
|
724
|
+
import random
|
|
725
|
+
|
|
726
|
+
connection = pika.BlockingConnection(
|
|
727
|
+
pika.ConnectionParameters('localhost')
|
|
728
|
+
)
|
|
729
|
+
channel = connection.channel()
|
|
730
|
+
|
|
731
|
+
channel.queue_declare(queue='task_queue', durable=True)
|
|
732
|
+
channel.basic_qos(prefetch_count=1)
|
|
733
|
+
|
|
734
|
+
def callback(ch, method, properties, body):
|
|
735
|
+
try:
|
|
736
|
+
message = body.decode('utf-8')
|
|
737
|
+
print(f"Processing: {message}")
|
|
738
|
+
|
|
739
|
+
# Simulate processing with potential failure
|
|
740
|
+
if random.random() < 0.3:
|
|
741
|
+
raise Exception('Random failure')
|
|
742
|
+
|
|
743
|
+
# Success - acknowledge
|
|
744
|
+
ch.basic_ack(delivery_tag=method.delivery_tag)
|
|
745
|
+
|
|
746
|
+
except Exception as e:
|
|
747
|
+
print(f"Processing failed: {e}")
|
|
748
|
+
|
|
749
|
+
# Check retry count
|
|
750
|
+
retry_count = 0
|
|
751
|
+
if properties.headers and 'x-retry-count' in properties.headers:
|
|
752
|
+
retry_count = properties.headers['x-retry-count']
|
|
753
|
+
|
|
754
|
+
if retry_count < 3:
|
|
755
|
+
# Reject and requeue for retry
|
|
756
|
+
ch.basic_nack(delivery_tag=method.delivery_tag, requeue=True)
|
|
757
|
+
else:
|
|
758
|
+
# Max retries reached - reject without requeue (goes to DLQ)
|
|
759
|
+
ch.basic_nack(delivery_tag=method.delivery_tag, requeue=False)
|
|
760
|
+
|
|
761
|
+
channel.basic_consume(
|
|
762
|
+
queue='task_queue',
|
|
763
|
+
on_message_callback=callback
|
|
764
|
+
)
|
|
765
|
+
|
|
766
|
+
print('Consumer with retry waiting for tasks. Press CTRL+C to exit.')
|
|
767
|
+
channel.start_consuming()
|
|
768
|
+
```
|
|
769
|
+
|
|
770
|
+
### Publisher Confirms
|
|
771
|
+
|
|
772
|
+
```python
|
|
773
|
+
import pika
|
|
774
|
+
|
|
775
|
+
connection = pika.BlockingConnection(
|
|
776
|
+
pika.ConnectionParameters('localhost')
|
|
777
|
+
)
|
|
778
|
+
channel = connection.channel()
|
|
779
|
+
|
|
780
|
+
# Enable publisher confirms
|
|
781
|
+
channel.confirm_delivery()
|
|
782
|
+
|
|
783
|
+
channel.queue_declare(queue='confirm_queue', durable=True)
|
|
784
|
+
|
|
785
|
+
message = 'Important message'
|
|
786
|
+
|
|
787
|
+
try:
|
|
788
|
+
channel.basic_publish(
|
|
789
|
+
exchange='',
|
|
790
|
+
routing_key='confirm_queue',
|
|
791
|
+
body=message,
|
|
792
|
+
properties=pika.BasicProperties(
|
|
793
|
+
delivery_mode=pika.DeliveryMode.Persistent
|
|
794
|
+
),
|
|
795
|
+
mandatory=True
|
|
796
|
+
)
|
|
797
|
+
print("Message confirmed by broker")
|
|
798
|
+
except pika.exceptions.UnroutableError:
|
|
799
|
+
print("Message was returned (unroutable)")
|
|
800
|
+
except pika.exceptions.NackError:
|
|
801
|
+
print("Message was nacked by broker")
|
|
802
|
+
|
|
803
|
+
connection.close()
|
|
804
|
+
```
|
|
805
|
+
|
|
806
|
+
### Batch Publishing with Confirms
|
|
807
|
+
|
|
808
|
+
```python
|
|
809
|
+
import pika
|
|
810
|
+
|
|
811
|
+
connection = pika.BlockingConnection(
|
|
812
|
+
pika.ConnectionParameters('localhost')
|
|
813
|
+
)
|
|
814
|
+
channel = connection.channel()
|
|
815
|
+
|
|
816
|
+
channel.confirm_delivery()
|
|
817
|
+
channel.queue_declare(queue='batch_queue', durable=True)
|
|
818
|
+
|
|
819
|
+
messages = [f'Message {i}' for i in range(100)]
|
|
820
|
+
|
|
821
|
+
confirmed = 0
|
|
822
|
+
nacked = 0
|
|
823
|
+
|
|
824
|
+
for message in messages:
|
|
825
|
+
try:
|
|
826
|
+
channel.basic_publish(
|
|
827
|
+
exchange='',
|
|
828
|
+
routing_key='batch_queue',
|
|
829
|
+
body=message,
|
|
830
|
+
properties=pika.BasicProperties(
|
|
831
|
+
delivery_mode=pika.DeliveryMode.Persistent
|
|
832
|
+
)
|
|
833
|
+
)
|
|
834
|
+
confirmed += 1
|
|
835
|
+
except (pika.exceptions.UnroutableError, pika.exceptions.NackError):
|
|
836
|
+
nacked += 1
|
|
837
|
+
|
|
838
|
+
print(f"Confirmed: {confirmed}, Nacked: {nacked}")
|
|
839
|
+
connection.close()
|
|
840
|
+
```
|
|
841
|
+
|
|
842
|
+
## Error Handling and Reconnection
|
|
843
|
+
|
|
844
|
+
### Connection Error Handling
|
|
845
|
+
|
|
846
|
+
```python
|
|
847
|
+
import pika
|
|
848
|
+
import time
|
|
849
|
+
|
|
850
|
+
def connect_rabbitmq():
|
|
851
|
+
while True:
|
|
852
|
+
try:
|
|
853
|
+
connection = pika.BlockingConnection(
|
|
854
|
+
pika.ConnectionParameters('localhost')
|
|
855
|
+
)
|
|
856
|
+
channel = connection.channel()
|
|
857
|
+
print("Connected to RabbitMQ")
|
|
858
|
+
return connection, channel
|
|
859
|
+
|
|
860
|
+
except pika.exceptions.AMQPConnectionError as e:
|
|
861
|
+
print(f"Connection failed: {e}")
|
|
862
|
+
print("Retrying in 5 seconds...")
|
|
863
|
+
time.sleep(5)
|
|
864
|
+
|
|
865
|
+
# Usage
|
|
866
|
+
connection, channel = connect_rabbitmq()
|
|
867
|
+
```
|
|
868
|
+
|
|
869
|
+
### Robust Connection Manager
|
|
870
|
+
|
|
871
|
+
```python
|
|
872
|
+
import pika
|
|
873
|
+
import time
|
|
874
|
+
import logging
|
|
875
|
+
|
|
876
|
+
logging.basicConfig(level=logging.INFO)
|
|
877
|
+
logger = logging.getLogger(__name__)
|
|
878
|
+
|
|
879
|
+
class RabbitMQConnection:
|
|
880
|
+
def __init__(self, host='localhost', max_retries=5, retry_delay=5):
|
|
881
|
+
self.host = host
|
|
882
|
+
self.max_retries = max_retries
|
|
883
|
+
self.retry_delay = retry_delay
|
|
884
|
+
self.connection = None
|
|
885
|
+
self.channel = None
|
|
886
|
+
|
|
887
|
+
def connect(self):
|
|
888
|
+
retries = 0
|
|
889
|
+
|
|
890
|
+
while retries < self.max_retries:
|
|
891
|
+
try:
|
|
892
|
+
self.connection = pika.BlockingConnection(
|
|
893
|
+
pika.ConnectionParameters(self.host)
|
|
894
|
+
)
|
|
895
|
+
self.channel = self.connection.channel()
|
|
896
|
+
logger.info("Connected to RabbitMQ")
|
|
897
|
+
return self.channel
|
|
898
|
+
|
|
899
|
+
except pika.exceptions.AMQPConnectionError as e:
|
|
900
|
+
retries += 1
|
|
901
|
+
logger.error(f"Connection failed (attempt {retries}/{self.max_retries}): {e}")
|
|
902
|
+
|
|
903
|
+
if retries < self.max_retries:
|
|
904
|
+
logger.info(f"Retrying in {self.retry_delay} seconds...")
|
|
905
|
+
time.sleep(self.retry_delay)
|
|
906
|
+
else:
|
|
907
|
+
logger.error("Max retries reached. Giving up.")
|
|
908
|
+
raise
|
|
909
|
+
|
|
910
|
+
def close(self):
|
|
911
|
+
if self.connection and not self.connection.is_closed:
|
|
912
|
+
self.connection.close()
|
|
913
|
+
logger.info("Connection closed")
|
|
914
|
+
|
|
915
|
+
# Usage
|
|
916
|
+
manager = RabbitMQConnection(host='localhost', max_retries=5)
|
|
917
|
+
channel = manager.connect()
|
|
918
|
+
|
|
919
|
+
# Use channel...
|
|
920
|
+
|
|
921
|
+
manager.close()
|
|
922
|
+
```
|
|
923
|
+
|
|
924
|
+
### Graceful Shutdown
|
|
925
|
+
|
|
926
|
+
```python
|
|
927
|
+
import pika
|
|
928
|
+
import signal
|
|
929
|
+
import sys
|
|
930
|
+
|
|
931
|
+
connection = None
|
|
932
|
+
channel = None
|
|
933
|
+
|
|
934
|
+
def setup():
|
|
935
|
+
global connection, channel
|
|
936
|
+
|
|
937
|
+
connection = pika.BlockingConnection(
|
|
938
|
+
pika.ConnectionParameters('localhost')
|
|
939
|
+
)
|
|
940
|
+
channel = connection.channel()
|
|
941
|
+
|
|
942
|
+
channel.queue_declare(queue='shutdown_queue', durable=True)
|
|
943
|
+
|
|
944
|
+
channel.basic_consume(
|
|
945
|
+
queue='shutdown_queue',
|
|
946
|
+
on_message_callback=callback
|
|
947
|
+
)
|
|
948
|
+
|
|
949
|
+
def callback(ch, method, properties, body):
|
|
950
|
+
print(f"Processing: {body.decode('utf-8')}")
|
|
951
|
+
ch.basic_ack(delivery_tag=method.delivery_tag)
|
|
952
|
+
|
|
953
|
+
def cleanup(sig, frame):
|
|
954
|
+
print("\nShutting down gracefully...")
|
|
955
|
+
|
|
956
|
+
if channel and channel.is_open:
|
|
957
|
+
channel.stop_consuming()
|
|
958
|
+
channel.close()
|
|
959
|
+
|
|
960
|
+
if connection and connection.is_open:
|
|
961
|
+
connection.close()
|
|
962
|
+
|
|
963
|
+
print("Cleanup complete")
|
|
964
|
+
sys.exit(0)
|
|
965
|
+
|
|
966
|
+
# Register signal handlers
|
|
967
|
+
signal.signal(signal.SIGINT, cleanup)
|
|
968
|
+
signal.signal(signal.SIGTERM, cleanup)
|
|
969
|
+
|
|
970
|
+
setup()
|
|
971
|
+
print('Waiting for messages. Press CTRL+C to exit.')
|
|
972
|
+
channel.start_consuming()
|
|
973
|
+
```
|
|
974
|
+
|
|
975
|
+
## Common Patterns
|
|
976
|
+
|
|
977
|
+
### Message Retry with Delay
|
|
978
|
+
|
|
979
|
+
```python
|
|
980
|
+
import pika
|
|
981
|
+
|
|
982
|
+
connection = pika.BlockingConnection(
|
|
983
|
+
pika.ConnectionParameters('localhost')
|
|
984
|
+
)
|
|
985
|
+
channel = connection.channel()
|
|
986
|
+
|
|
987
|
+
# Main queue
|
|
988
|
+
channel.queue_declare(
|
|
989
|
+
queue='main_queue',
|
|
990
|
+
durable=True,
|
|
991
|
+
arguments={
|
|
992
|
+
'x-dead-letter-exchange': 'retry',
|
|
993
|
+
'x-dead-letter-routing-key': 'retry'
|
|
994
|
+
}
|
|
995
|
+
)
|
|
996
|
+
|
|
997
|
+
# Retry exchange
|
|
998
|
+
channel.exchange_declare(exchange='retry', exchange_type='direct', durable=True)
|
|
999
|
+
|
|
1000
|
+
# Retry queue with delay
|
|
1001
|
+
channel.queue_declare(
|
|
1002
|
+
queue='retry_queue',
|
|
1003
|
+
durable=True,
|
|
1004
|
+
arguments={
|
|
1005
|
+
'x-message-ttl': 30000, # 30 second delay
|
|
1006
|
+
'x-dead-letter-exchange': '',
|
|
1007
|
+
'x-dead-letter-routing-key': 'main_queue'
|
|
1008
|
+
}
|
|
1009
|
+
)
|
|
1010
|
+
|
|
1011
|
+
channel.queue_bind(queue='retry_queue', exchange='retry', routing_key='retry')
|
|
1012
|
+
|
|
1013
|
+
print("Retry queue setup complete")
|
|
1014
|
+
connection.close()
|
|
1015
|
+
```
|
|
1016
|
+
|
|
1017
|
+
### Priority Queue
|
|
1018
|
+
|
|
1019
|
+
```python
|
|
1020
|
+
import pika
|
|
1021
|
+
|
|
1022
|
+
connection = pika.BlockingConnection(
|
|
1023
|
+
pika.ConnectionParameters('localhost')
|
|
1024
|
+
)
|
|
1025
|
+
channel = connection.channel()
|
|
1026
|
+
|
|
1027
|
+
# Queue with priority support
|
|
1028
|
+
channel.queue_declare(
|
|
1029
|
+
queue='priority_queue',
|
|
1030
|
+
durable=True,
|
|
1031
|
+
arguments={'x-max-priority': 10}
|
|
1032
|
+
)
|
|
1033
|
+
|
|
1034
|
+
# Send messages with different priorities
|
|
1035
|
+
messages = [
|
|
1036
|
+
('Low priority', 1),
|
|
1037
|
+
('High priority', 10),
|
|
1038
|
+
('Medium priority', 5)
|
|
1039
|
+
]
|
|
1040
|
+
|
|
1041
|
+
for message, priority in messages:
|
|
1042
|
+
channel.basic_publish(
|
|
1043
|
+
exchange='',
|
|
1044
|
+
routing_key='priority_queue',
|
|
1045
|
+
body=message,
|
|
1046
|
+
properties=pika.BasicProperties(priority=priority)
|
|
1047
|
+
)
|
|
1048
|
+
|
|
1049
|
+
print("Priority messages sent")
|
|
1050
|
+
connection.close()
|
|
1051
|
+
```
|
|
1052
|
+
|
|
1053
|
+
### Rate Limiting Consumer
|
|
1054
|
+
|
|
1055
|
+
```python
|
|
1056
|
+
import pika
|
|
1057
|
+
import time
|
|
1058
|
+
|
|
1059
|
+
connection = pika.BlockingConnection(
|
|
1060
|
+
pika.ConnectionParameters('localhost')
|
|
1061
|
+
)
|
|
1062
|
+
channel = connection.channel()
|
|
1063
|
+
|
|
1064
|
+
channel.queue_declare(queue='rate_limited_queue', durable=True)
|
|
1065
|
+
|
|
1066
|
+
# Process only 1 message at a time
|
|
1067
|
+
channel.basic_qos(prefetch_count=1)
|
|
1068
|
+
|
|
1069
|
+
def callback(ch, method, properties, body):
|
|
1070
|
+
print(f"Processing: {body.decode('utf-8')}")
|
|
1071
|
+
|
|
1072
|
+
# Simulate rate-limited processing (2 seconds delay)
|
|
1073
|
+
time.sleep(2)
|
|
1074
|
+
|
|
1075
|
+
ch.basic_ack(delivery_tag=method.delivery_tag)
|
|
1076
|
+
|
|
1077
|
+
channel.basic_consume(
|
|
1078
|
+
queue='rate_limited_queue',
|
|
1079
|
+
on_message_callback=callback
|
|
1080
|
+
)
|
|
1081
|
+
|
|
1082
|
+
print('Rate-limited consumer started. Press CTRL+C to exit.')
|
|
1083
|
+
channel.start_consuming()
|
|
1084
|
+
```
|
|
1085
|
+
|
|
1086
|
+
### JSON Message Handling
|
|
1087
|
+
|
|
1088
|
+
```python
|
|
1089
|
+
import pika
|
|
1090
|
+
import json
|
|
1091
|
+
|
|
1092
|
+
connection = pika.BlockingConnection(
|
|
1093
|
+
pika.ConnectionParameters('localhost')
|
|
1094
|
+
)
|
|
1095
|
+
channel = connection.channel()
|
|
1096
|
+
|
|
1097
|
+
channel.queue_declare(queue='json_queue', durable=True)
|
|
1098
|
+
|
|
1099
|
+
# Send JSON message
|
|
1100
|
+
data = {
|
|
1101
|
+
'user': 'john',
|
|
1102
|
+
'action': 'login',
|
|
1103
|
+
'timestamp': 1609459200
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
message = json.dumps(data)
|
|
1107
|
+
|
|
1108
|
+
channel.basic_publish(
|
|
1109
|
+
exchange='',
|
|
1110
|
+
routing_key='json_queue',
|
|
1111
|
+
body=message,
|
|
1112
|
+
properties=pika.BasicProperties(
|
|
1113
|
+
content_type='application/json',
|
|
1114
|
+
delivery_mode=pika.DeliveryMode.Persistent
|
|
1115
|
+
)
|
|
1116
|
+
)
|
|
1117
|
+
|
|
1118
|
+
print(f"Sent JSON: {data}")
|
|
1119
|
+
|
|
1120
|
+
# Receive JSON message
|
|
1121
|
+
def callback(ch, method, properties, body):
|
|
1122
|
+
try:
|
|
1123
|
+
data = json.loads(body)
|
|
1124
|
+
print(f"Received JSON: {data}")
|
|
1125
|
+
ch.basic_ack(delivery_tag=method.delivery_tag)
|
|
1126
|
+
except json.JSONDecodeError as e:
|
|
1127
|
+
print(f"Invalid JSON: {e}")
|
|
1128
|
+
ch.basic_nack(delivery_tag=method.delivery_tag, requeue=False)
|
|
1129
|
+
|
|
1130
|
+
channel.basic_consume(
|
|
1131
|
+
queue='json_queue',
|
|
1132
|
+
on_message_callback=callback
|
|
1133
|
+
)
|
|
1134
|
+
|
|
1135
|
+
# channel.start_consuming() # Uncomment to start consuming
|
|
1136
|
+
connection.close()
|
|
1137
|
+
```
|
|
1138
|
+
|
|
1139
|
+
## Async Usage with Select Connection
|
|
1140
|
+
|
|
1141
|
+
### Async Producer
|
|
1142
|
+
|
|
1143
|
+
```python
|
|
1144
|
+
import pika
|
|
1145
|
+
|
|
1146
|
+
def on_open(connection):
|
|
1147
|
+
connection.channel(on_open_callback=on_channel_open)
|
|
1148
|
+
|
|
1149
|
+
def on_channel_open(channel):
|
|
1150
|
+
channel.queue_declare(
|
|
1151
|
+
queue='async_queue',
|
|
1152
|
+
durable=True,
|
|
1153
|
+
callback=lambda method: on_queue_declared(channel)
|
|
1154
|
+
)
|
|
1155
|
+
|
|
1156
|
+
def on_queue_declared(channel):
|
|
1157
|
+
message = 'Hello Async World!'
|
|
1158
|
+
|
|
1159
|
+
channel.basic_publish(
|
|
1160
|
+
exchange='',
|
|
1161
|
+
routing_key='async_queue',
|
|
1162
|
+
body=message,
|
|
1163
|
+
properties=pika.BasicProperties(
|
|
1164
|
+
delivery_mode=pika.DeliveryMode.Persistent
|
|
1165
|
+
)
|
|
1166
|
+
)
|
|
1167
|
+
|
|
1168
|
+
print(f"Sent: {message}")
|
|
1169
|
+
channel.connection.close()
|
|
1170
|
+
|
|
1171
|
+
# Create async connection
|
|
1172
|
+
parameters = pika.ConnectionParameters('localhost')
|
|
1173
|
+
connection = pika.SelectConnection(parameters, on_open_callback=on_open)
|
|
1174
|
+
|
|
1175
|
+
try:
|
|
1176
|
+
connection.ioloop.start()
|
|
1177
|
+
except KeyboardInterrupt:
|
|
1178
|
+
connection.close()
|
|
1179
|
+
connection.ioloop.start()
|
|
1180
|
+
```
|
|
1181
|
+
|
|
1182
|
+
### Async Consumer
|
|
1183
|
+
|
|
1184
|
+
```python
|
|
1185
|
+
import pika
|
|
1186
|
+
|
|
1187
|
+
class AsyncConsumer:
|
|
1188
|
+
def __init__(self, queue_name):
|
|
1189
|
+
self.queue_name = queue_name
|
|
1190
|
+
self.connection = None
|
|
1191
|
+
self.channel = None
|
|
1192
|
+
|
|
1193
|
+
def connect(self):
|
|
1194
|
+
parameters = pika.ConnectionParameters('localhost')
|
|
1195
|
+
return pika.SelectConnection(
|
|
1196
|
+
parameters,
|
|
1197
|
+
on_open_callback=self.on_connection_open
|
|
1198
|
+
)
|
|
1199
|
+
|
|
1200
|
+
def on_connection_open(self, connection):
|
|
1201
|
+
self.connection = connection
|
|
1202
|
+
self.connection.channel(on_open_callback=self.on_channel_open)
|
|
1203
|
+
|
|
1204
|
+
def on_channel_open(self, channel):
|
|
1205
|
+
self.channel = channel
|
|
1206
|
+
self.channel.queue_declare(
|
|
1207
|
+
queue=self.queue_name,
|
|
1208
|
+
durable=True,
|
|
1209
|
+
callback=self.on_queue_declared
|
|
1210
|
+
)
|
|
1211
|
+
|
|
1212
|
+
def on_queue_declared(self, method):
|
|
1213
|
+
self.channel.basic_qos(prefetch_count=1)
|
|
1214
|
+
self.channel.basic_consume(
|
|
1215
|
+
queue=self.queue_name,
|
|
1216
|
+
on_message_callback=self.on_message
|
|
1217
|
+
)
|
|
1218
|
+
print(f'Waiting for messages in {self.queue_name}')
|
|
1219
|
+
|
|
1220
|
+
def on_message(self, ch, method, properties, body):
|
|
1221
|
+
print(f"Received: {body.decode('utf-8')}")
|
|
1222
|
+
ch.basic_ack(delivery_tag=method.delivery_tag)
|
|
1223
|
+
|
|
1224
|
+
def run(self):
|
|
1225
|
+
self.connection = self.connect()
|
|
1226
|
+
try:
|
|
1227
|
+
self.connection.ioloop.start()
|
|
1228
|
+
except KeyboardInterrupt:
|
|
1229
|
+
self.connection.close()
|
|
1230
|
+
self.connection.ioloop.start()
|
|
1231
|
+
|
|
1232
|
+
# Usage
|
|
1233
|
+
consumer = AsyncConsumer('async_queue')
|
|
1234
|
+
consumer.run()
|
|
1235
|
+
```
|
|
1236
|
+
|
|
1237
|
+
## Useful Links
|
|
1238
|
+
|
|
1239
|
+
- **Official Documentation:** https://www.rabbitmq.com/docs
|
|
1240
|
+
- **Pika Documentation:** https://pika.readthedocs.io/
|
|
1241
|
+
- **Pika GitHub:** https://github.com/pika/pika
|
|
1242
|
+
- **RabbitMQ Tutorials (Python):** https://www.rabbitmq.com/tutorials
|
|
1243
|
+
- **AMQP 0-9-1 Reference:** https://www.rabbitmq.com/amqp-0-9-1-reference.html
|