簡單模式
The Python code based on pika==1.0.0 version
producer:
channel.basic_publish(
exchange = ``,
routing_key = 'hello',
body = “世界你好!”
)
- 這種交換是特殊的‒它使我們可以準確地指定消息應進入的隊列。隊列名稱需要在routing_key參數中指定.
consumer:
channel.basic_consume(
queue='hello',
on_message_callback=callback,
auto_ack=True
)
Putting it all together
send.py
#!/usr/bin/env python import pika connection = pika.BlockingConnection( pika.ConnectionParameters(host='localhost')) channel = connection.channel() channel.queue_declare(queue='hello') # 隊列的聲明是冪等的 channel.basic_publish(exchange='', routing_key='hello', body='Hello World!') print(" [x] Sent 'Hello World!'") connection.close()
receive.py
#!/usr/bin/env python import pika, sys, os def main(): connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost')) channel = connection.channel() channel.queue_declare(queue='hello') def callback(ch, method, properties, body): print(" [x] Received %r" % body) channel.basic_consume(queue='hello', on_message_callback=callback, auto_ack=True) print(' [*] Waiting for messages. To exit press CTRL+C') channel.start_consuming() if __name__ == '__main__': try: main() except KeyboardInterrupt: print('Interrupted') try: sys.exit(0) except SystemExit: os._exit(0)
消息確認機制
鋪墊:同生產者消費者模型,消費者在處理消息後要向隊列發送消息回執,表明該消息已被消息掉。
官網解釋:bool auto_ack: if set to True, automatic acknowledgement mode will be used
該參數默認爲False,意味着RabbitMQ會消耗越來越多的內存,因爲它無法釋放那些未被確認的消息。
當設置爲True時: 如果一個消費者在處理消息過程中掛掉了,那麼這個消息就丟失了,並且還丟失了發送給此消費者所有尚未處理的消息。
這種情況肯定不是我們想要的,當一個消費者掛掉,如果能重新把消息發給其他消費者,這樣,我們的消息就不會丟失任何消息了。要如何做呢? (消息持久化+工作隊列+任務派遣)
消息持久化
當RabbitMQ服務掛掉時,隊列和消息都會丟失,要想不丟失,需要做持久化。
# 隊列持久化 channel.queue_declare(queue='hello', durable=True) # 消息持久化 發送端: channel.basic_publish( exchange='', routing_key='task_queue', body=message, properties=pika.BasicProperties( delivery_mode=2, # make message persistent )) 消費端: def callback(ch, method, properties, body): print(" [x] Received %r" % body.decode()) ch.basic_ack(delivery_tag=method.delivery_tag) channel.basic_consume(queue='task_queue', on_message_callback=callback, auto_ack=False)
工作隊列
工作隊列又叫任務隊列
使用場景:當一個消費者處理一條消息很耗時,我又不想等待它完成,想把消息分發給其他消費者時
準備:一個生產者、多個消費者
MQ將按順序地將每個消息發送給下一個消費者,平均而言,每個消費者都會收到相同數量的消息,這種分發方式稱爲循環。
以循環方式分發消息仍然不能解決上述場景的問題,理想情況是公平的分發消息(在消費者忙碌時就把消息發給不忙的消費者)
公平派遣/公平分發
爲了克服這個問題,我們可以將 Channel#basic_qos通道方法與 prefetch_count = 1設置一起使用。
使用basic.qos協議方法來告訴RabbitMQ一次不向消費者發送多條消息。換句話說,在處理並確認上一條消息之前,不要將新消息發送給消費者。而是將其分派給不忙的下一個消費者。
channel.basic_qos(prefetch_count = 1)
關於隊列大小的注意事項
如果所有工作人員都忙,您的隊列就滿了。您將需要注意這一點,並可能增加更多的工作人員,或使用消息TTL。
Putting it all together
send.py
#!/usr/bin/env python import pika import sys connection = pika.BlockingConnection( pika.ConnectionParameters(host='localhost')) channel = connection.channel() channel.queue_declare(queue='task_queue', durable=True) message = ' '.join(sys.argv[1:]) or "Hello World!" channel.basic_publish( exchange='', routing_key='task_queue', body=message, properties=pika.BasicProperties( delivery_mode=2, # make message persistent )) print(" [x] Sent %r" % message) connection.close()
receive.py
#!/usr/bin/env python import pika import time connection = pika.BlockingConnection( pika.ConnectionParameters(host='localhost')) channel = connection.channel() channel.queue_declare(queue='task_queue', durable=True) print(' [*] Waiting for messages. To exit press CTRL+C') def callback(ch, method, properties, body): print(" [x] Received %r" % body.decode()) time.sleep(body.count(b'.')) print(" [x] Done") ch.basic_ack(delivery_tag=method.delivery_tag) channel.basic_qos(prefetch_count=1) channel.basic_consume(queue='task_queue', on_message_callback=callback) channel.start_consuming()