利用Python學習RabbitMQ訊息佇列

來源:互聯網
上載者:User
RabbitMQ可以當做一個訊息代理,它的核心原理非常簡單:即接收和發送訊息,可以把它想象成一個郵局:我們把信件放入郵箱,郵遞員就會把信件投遞到你的收件者處,RabbitMQ就是一個郵箱、郵局、投遞員功能綜合體,整個過程就是:郵箱接收信件,郵局轉寄信件,投遞員投遞信件到達收件者處。

RabbitMQ和郵局的主要區別就是RabbitMQ接收、儲存和發送的是位元據----訊息。

rabbitmq基本管理命令:

一步啟動Erlang node和Rabbit應用:sudo rabbitmq-server

在後台啟動Rabbit node:sudo rabbitmq-server -detached

關閉整個節點(包括應用):sudo rabbitmqctl stop

add_user  delete_user change_password  list_usersadd_vhost delete_vhost list_vhostsset_permissions [-p ]    clear_permissions [-p ] list_permissions [-p ]list_user_permissions list_queues [-p ] [ ...]list_exchanges [-p ] [ ...]list_bindings [-p ]list_connections [ ...]

Demo:

producer.py

 #!/usr/bin/env python # -*- coding: utf_ -*- # Date: 年月日 # Author:蔚藍行 # 部落格 http://www.cnblogs.com/duanv/ import pika import sys #建立串連connection到localhost con = pika.BlockingConnection(pika.ConnectionParameters('localhost')) #建立虛擬串連channel cha = con.channel() #建立隊列anheng,durable參數為真時,隊列將持久化;exclusive為真時,建立暫存佇列 result=cha.queue_declare(queue='anheng',durable=True,exclusive=False) #建立名為yanfa,類型為fanout的exchange,其他類型還有direct和topic,如果指定durable為真,exchange將持久化 cha.exchange_declare(durable=False,           exchange='yanfa',           type='direct',) #綁定exchange和queue,result.method.queue擷取的是隊列名稱 cha.queue_bind(exchange='yanfa',         queue=result.method.queue,        routing_key='',)  #公平分發,使每個consumer在同一時間最多處理一個message,收到ack前,不會分配新的message cha.basic_qos(prefetch_count=) #發送資訊到隊列‘anheng' message = ' '.join(sys.argv[:]) #訊息持久化指定delivery_mode=; cha.basic_publish(exchange='',          routing_key='anheng',          body=message,          properties=pika.BasicProperties(           delivery_mode = ,         )) print '[x] Sent %r' % (message,) #關閉串連 con.close()

consumer.py

 #!/usr/bin/env python # -*- coding: utf_ -*- # Date: 年月日 # Author:蔚藍行 # 部落格 http://www.cnblogs.com/duanv/ import pika #建立串連connection到localhost con = pika.BlockingConnection(pika.ConnectionParameters('localhost')) #建立虛擬串連channel cha = con.channel() #建立隊列anheng result=cha.queue_declare(queue='anheng',durable=True) #建立名為yanfa,類型為fanout的交換器,其他類型還有direct和topic cha.exchange_declare(durable=False,           exchange='yanfa',            type='direct',) #綁定exchange和queue,result.method.queue擷取的是隊列名稱 cha.queue_bind(exchange='yanfa',        queue=result.method.queue,        routing_key='',) #公平分發,使每個consumer在同一時間最多處理一個message,收到ack前,不會分配新的message cha.basic_qos(prefetch_count=) print ' [*] Waiting for messages. To exit press CTRL+C' #定義回呼函數 def callback(ch, method, properties, body):   print " [x] Received %r" % (body,)   ch.basic_ack(delivery_tag = method.delivery_tag) cha.basic_consume(callback,          queue='anheng',          no_ack=False,) cha.start_consuming()

一、概念:

Connection: 一個TCP的串連。Producer和Consumer都是通過TCP串連到RabbitMQ Server的。程式的起始處就是建立這個TCP串連。

Channels: 虛擬串連。建立在上述的TCP串連中。資料流動都是在Channel中進行的。一般情況是程式起始建立TCP串連,第二步就是建立這個Channel。

二、隊列:

首先建立一個Connection,然後建立Channels,在channel上建立隊列

建立時指定durable參數為真,隊列將持久化;指定exclusive為真,隊列為暫存佇列,關閉consumer後該隊列將不再存在,一般情況下建立暫存佇列並不指定隊列名稱,rabbitmq將隨機起名,通過result.method.queue來擷取隊列名:

result = channel.queue_declare(exclusive=True)

result.method.queue

區別:durable是隊列持久化與否,如果為真,隊列將在rabbitmq服務重啟後仍存在,如果為假,rabbitmq服務重啟前不會消失,與consumer關閉與否無關;

而exclusive是建立暫存佇列,當consumer關閉後,該隊列就會被刪除

三、exchange和bind

Exchange中durable參數指定exchange是否持久化,exchange參數指定exchange名稱,type指定exchange類型。Exchange類型有direct,fanout和topic。

Bind是將exchange與queue進行關聯,exchange參數和queue參數分別指定要進行bind的exchange和queue,routing_key為選擇性參數。

Exchange的三種模式:

Direct:

任何發送到Direct Exchange的訊息都會被轉寄到routing_key中指定的Queue

1.一般情況可以使用rabbitMQ內建的Exchange:””(該Exchange的名字為空白字串);

2.這種模式下不需要將Exchange進行任何綁定(bind)操作;

3.訊息傳遞時需要一個“routing_key”,可以簡單的理解為要發送到的隊列名字;

4.如果vhost中不存在routing_key中指定的隊列名,則該訊息會被拋棄。

Demo中雖然聲明了一個exchange='yanfa'和queue='anheng'的bind,但是在後面發送訊息時並沒有使用該exchange和bind,而是採用了direct的模式,沒有指定exchange,而是指定了routing_key的名稱為隊列名,訊息將發送到指定隊列。

如果一個exchange 聲明為direct,並且bind中指定了routing_key,那麼發送訊息時需要同時指明該exchange和routing_key.

Fanout:

任何發送到Fanout Exchange的訊息都會被轉寄到與該Exchange綁定(Binding)的所有Queue上

1.可以理解為路由表的模式

2.這種模式不需要routing_key

3.這種模式需要提前將Exchange與Queue進行綁定,一個Exchange可以綁定多個Queue,一個Queue可以同多個Exchange進行綁定。

4.如果接受到訊息的Exchange沒有與任何Queue綁定,則訊息會被拋棄。

Demo中建立了一個將一個exchange和一個queue進行fanout類型的bind.但是發送資訊時沒有用到它,如果要用到它,只要在發送訊息時指定該exchange的名稱即可,該exchange就會將訊息發送到所有和它bind的隊列中。在fanout模式下,指定的routing_key是無效的 。

Topic:

任何發送到Topic Exchange的訊息都會被轉寄到所有關心routing_key中指定話題的Queue上

1.這種模式較為複雜,簡單來說,就是每個隊列都有其關心的主題,所有的訊息都帶有一個“標題”(routing_key),Exchange會將訊息轉寄到所有關注主題能與routing_key模糊比對的隊列。

2.這種模式需要routing_key,也許要提前綁定Exchange與Queue。

3.在進行綁定時,要提供一個該隊列關心的主題,如“#.log.#”表示該隊列關心所有涉及log的訊息(一個routing_key為”MQ.log.error”的訊息會被轉寄到該隊列)。

4.“#”表示0個或若干個關鍵字,“*”表示一個關鍵字。如“log.*”能與“log.warn”匹配,無法與“log.warn.timeout”匹配;但是“log.#”能與上述兩者匹配。

5.同樣,如果Exchange沒有發現能夠與routing_key匹配的Queue,則會拋棄此訊息。

四、任務分發

1.Rabbitmq的任務是迴圈分發的,如果開啟兩個consumer,producer發送的資訊是輪流發送到兩個consume的。

2.在producer端使用cha.basic_publish()來發送訊息,其中body參數就是要發送的訊息,properties=pika.BasicProperties(delivery_mode = 2,)啟用訊息持久化,可以防止RabbitMQ Server 重啟或者crash引起的資料丟失。

3.在接收端使用cha.basic_consume()無限迴圈監聽,如果設定no-ack參數為真,每次Consumer接到資料後,而不管是否處理完成,RabbitMQ Server會立即把這個Message標記為完成,然後從queue中刪除了。為了保證資料不被丟失,RabbitMQ支援訊息確認機制,即acknowledgments。為了保證資料能被正確處理而不僅僅是被Consumer收到,那麼我們不能採用no-ack。而應該是在處理完資料後發送ack。

在處理資料後發送的ack,就是告訴RabbitMQ資料已經被接收,處理完成,RabbitMQ可以去安全的刪除它了。如果Consumer退出了但是沒有發送ack,那麼RabbitMQ就會把這個Message發送到下一個Consumer。這樣就保證了在Consumer異常退出的情況下資料也不會丟失。

這裡並沒有用到逾時機制。RabbitMQ僅僅通過Consumer的串連中斷來確認該Message並沒有被正確處理。也就是說,RabbitMQ給了Consumer足夠長的時間來做資料處理。

Demo的callback方法中ch.basic_ack(delivery_tag = method.delivery_tag)告訴rabbitmq訊息已經正確處理。如果沒有這條代碼,Consumer退出時,Message會重新分發。然後RabbitMQ會佔用越來越多的記憶體,由於RabbitMQ會長時間運行,因此這個“記憶體流失”是致命的。去調試這種錯誤,可以通過一下命令列印un-acked Messages:

sudo rabbitmqctl list_queues name messages_ready messages_unacknowledged

4.公平分發:設定cha.basic_qos(prefetch_count=1),這樣RabbitMQ就會使得每個Consumer在同一個時間點最多處理一個Message。換句話說,在接收到該Consumer的ack前,他它不會將新的Message分發給它。

五、注意:

生產者和消費者都應該聲明建立隊列,網上教程上說第二次建立如果參數和第一次不一樣,那麼該操作雖然成功,但是queue的屬性並不會被修改。

可能因為版本問題,在我的測試中如果第二次聲明建立的隊列屬性和第一次不完全相同,將報類似這種錯406, "PRECONDITION_FAILED - parameters for queue 'anheng' in vhost '/' not equivalent"

如果是exchange第二次建立屬性不同,將報這種錯406, "PRECONDITION_FAILED - cannot redeclare exchange 'yanfa' in vhost '/' with different type, durable, internal or autodelete value"

如果第一次聲明建立隊列也出現這個錯誤,說明之前存在名字相同的隊列且本次聲明的某些屬性和之前聲明不同,可通過命令sudo rabbitmqctl list_queues查看當前有哪些隊列。解決方案是聲明建立另一名稱的隊列或刪除原有隊列,如果原有隊列是非持久化的,可通過重啟rabbitmq服務刪除原有隊列,如果原有隊列是持久化的,只能刪除它所在的vhost,然後再重建vhost,再設定vhost的許可權(先確認該vhost中沒有其他有用隊列)。

sudo rabbitmqctl delete_vhost /sudo rabbitmqctl add_vhost /sudo rabbitmqctl set_permissions -p / username '.*' '.*' '.*'

以上內容是小編給大家介紹的利用Python學習RabbitMQ訊息佇列,希望大家喜歡。

  • 聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.