Python操作RabbitMq詳解

一、簡介:

RabbitMq是實現了高級消息隊列協議(AMQP)的開源消息代理中間件。消息隊列是一種應用程序對應用程序的通行方式,應用程序通過寫消息,將消息傳遞與隊列,由另一應用程序讀取完成通信,而作爲中間件的RabbitMq無疑是目前最流行的消息隊列之一
在這裏插入圖片描述
在這裏插入圖片描述

二、VirtualHost

RabbitMq的VirtualHost(虛擬消息服務器),每個VirtualHost相當於一個相對獨立的RabbitMQ服務器;每個VirtualHost之間是相互隔離的,exchange、queue、message不能互通。

添加用戶

在這裏插入圖片描述
下面給大家介紹權限也就是方框中的內容,

  1. 超級管理員(admin):可登錄管理控制檯,可查看所有的信息,並且可以對用戶,策略(policy)進行操作
  2. 監控者(monitoring):可登陸管理控制檯,同時可以查看rabbitmq節點的相關信息(進程數,內存使用情況,磁盤使用情況等)
  3. 策略制定者(policymaker):可登陸管理控制檯
    拿數據庫(用MySQL)來類比:RabbitMq相當於MySQL,RabbitMq中的VirtualHost就相當於MySQL中的一個庫。
  4. 管理管理者(management):僅可登錄管理控制檯,無法看到節點信息,也無法對策略進行管理
  5. 其他:無法登錄管理控制檯,通常就是普通的生產者和消費者

三、RabbitMq的應用場景

  • 系統的高可用:日常生活當中各種商城秒殺,高流量、高併發的場景,當服務器接受到如此大量請求處理業務時,有宕機的風險,某些業務可能極其複雜,但這部分不是高時效性,不需要立即反饋給用戶,我們可以將這部分處理請求拋給隊列,讓程序後置去處理,減輕服務器在高併發場景下的壓力
  • 分佈式系統,集成系統,子系統之間的對接,以及架構設計中常常需要考慮消息隊列的應用

四、RabbitMq中的Connection和Channel

我們知道無論是生產者還是消費者,都需要RabbitMq Broker 建立連接,這個連接就是一條TCP連接,也就是Connection
一旦TCP連接建立起來,客戶端緊接着就可以創建一個AMQP信道(Channel),每個信道都會被指派一個唯一的ID
信道是建立在Connection之上的虛擬連接,RabbitMq處理的每條AMQP指令都是通過信道完成的。

後續。。。。

五、RabbitMq生產者消費者模型

生產者(producter) 隊列消息的產生者,複製生產消息,並將消息傳入隊列
生產者代碼:

import pika
import json

credentials = pika.PlainCredentials('lvhua','123456')#mq用戶名和密碼,用於認證
#虛擬隊列需要指定參數virtual_host,如果是默認的可以不填
connection = pika.BlockingConnection(pika.ConnectionParameters(host='localhost',port=5672,virtual_host='/',credentials=credentials))
channel = connection.channel()# 創建一個AMQP信道

#聲明隊列,並設置durable爲True,爲了避免rabbitMq-server掛掉數據丟失,將durable設爲True
channel.queue_declare(queue='1',durable=True)
for i in range(10):   # 創建10個q
    message = json.dumps({'OrderId':"1000%s"%i})
    # exchange表示交換器,可以精確的指定消息應該發到哪個隊列中,route_key設置隊列的名稱,body表示發送的內容
    channel.basic_publish(exchange='',routing_key='1',body=message)
    print(message)
connection.close()

消費者(consumer):隊列消息的接收者,扶着接收並處理消息隊列中的消息

import pika
credentials = pika.PlainCredentials('lvhua','123456')
connection = pika.BlockingConnection(pika.ConnectionParameters(
    host='localhost',
    port=5672,
    virtual_host='/',
    credentials=credentials
))
channel = connection.channel()
#聲明消息隊列,消息在這個隊列中傳遞,如果不存在,則創建隊列
channel.queue_declare(queue='1',durable=True)
# 定義一個回調函數來處理消息隊列中消息,這裏是打印出來
def callback(ch,method,properties,body):
    ch.basic_ack(delivery_tag=method.delivery_tag)
    print(body.decode())
#告訴rabbitmq,用callback來接收消息
channel.basic_consume('1',callback)
#開始接收信息,並進入阻塞狀態,隊列裏有信息纔會調用callback進行處理
channel.start_consuming()

六、RabbitMq持久化

MQ默認建立的臨時的queue和exchange,如果不聲明持久化,一旦rabbitmq掛掉,queue,exchange將會全部丟失,所以我們一般在創建queue或者exchange的時候會聲明持久化
1.queue聲明持久化

# 聲明消息隊列,消息將在這個隊列傳遞,如不存在,則創建。durable = True 代表消息隊列持久化存儲,False 非持久化存儲
result = channel.queue_declare(queue = 'python-test',durable = True)
  1. exchange聲明持久化
# 聲明exchange,由exchange指定消息在哪個隊列傳遞,如不存在,則創建.durable = True 代表exchange持久化存儲,False 非持久化存儲
channel.exchange_declare(exchange = 'python-test', durable = True)

注意:如果已存在一個非持久化的queue或exchange,執行上述代碼會報錯,因爲當前狀態不能更該queue 或 exchange存儲屬性,需要刪除重建,如果queue和exchange中一個聲明瞭持久化,另一個沒有聲明持久化,則不允許綁定

  1. 消息持久化

雖然exchange和queue都聲明瞭持久化,但如果消息只存在內存裏,rabbitmq重啓後,內存裏的東西還是會丟失,所以必須聲明消息也是持久化,從內存轉存到到硬盤

# 向隊列插入數值 routing_key是隊列名。delivery_mode = 2 聲明消息在隊列中持久化,delivery_mod = 1 消息非持久化
    channel.basic_publish(exchange = '',routing_key = 'python-test',body = message,                       properties=pika.BasicProperties(delivery_mode = 2))
  1. acknowledgement消息不丟失
    消費者(consume)調用callback函數時,會存在處理消息失敗的風險,如果處理失敗,則消息會丟失,但是也可以選擇消費者處理失敗時,將消息回退給rabbitmq,重新再被消費者消費,這個時候需要設置確認標識。
channel.basic_consume(callback,queue = 'python-test',
# no_ack 設置成 False,在調用callback函數時,未收到確認標識,消息會重回隊列。True,無論調用callback成功與否,消息都被消費掉
                      no_ack = False)

七、RabbitMq發佈與訂閱

在上一章中,我們創建了一個工作隊列,工作隊列模式的設想是每一條消息只會被轉發給一個消費者。本章將會講解完全不一樣的場景: 我們會把一個消息轉發給多個消費者,這種模式稱之爲發佈-訂閱模式。
RabbitMq消息模式的核心思想是:一個生產者並不會直接往一個隊列中發送消息,事實上,生產者根本不知道它發送的消息將被轉發到哪些隊列。
實際上,生產者只能把消息發送給一個exchange,exchange只做一件簡單的事情:一方面它們接收從生產者發送過來的消息,另一方面,它們把接收到的消息推送給隊列。一個exchage必須清楚地知道如何處理一條消息.  
rabbitmq的發佈與訂閱要藉助交換機(Exchange)的原理實現:
在這裏插入圖片描述
Exchange 一共有三種工作模式:fanout, direct, topicd

模式一:fanout

這種模式下,傳遞到exchange的消息將會==轉發到所有於其綁定的queue上

  1. 不需要指定routing_key,即使指定了也是無效的。
  2. 需要提前將exchange和queue綁定,一個exchange可以綁定多個queue,一個queue可以綁定多個exchange。
  3. 需要先啓動訂閱者,此模式下的隊列是consume隨機生成的,發佈者僅僅發佈消息到exchange,由exchange轉消息至queue。

exchange交換器

首先我們創建一個fanout類型的交換器,我們稱之爲:python-test:

channel.exchange_declare(exchange = 'python-test',durable = True, exchange_type='fanout')

  廣播模式交換器很簡單,從字面意思也能理解,它其實就是把接收到的消息推送給所有它知道的隊列。
  想查看當前系統中有多少個exchange,可以從控制檯查看在這裏插入圖片描述
  可以看到有很多以amq.*開頭的交換器,以及(AMQP default)默認交換器,這些是默認創建的交換器。
  在前面,我們並不知道交換器的存在,但是依然可以將消息發送到隊列中,那其實並不是因爲我們可以不使用交換器,實際上是我們使用了默認的交換器(我們通過指定交換器爲字字符串:""),回顧一下我們之前是如何發送消息的:

channel.basic_publish(exchange='',routing_key='1',body=message)

  第一個參數是交換器的名字,空字符串表示它是一個默認或無命名的交換器,消息將會由指定的路由鍵(第二個參數,routingKey,後面會講)轉發到隊列。
你可能會有疑問:既然exchange可以指定爲空字符串(""),那麼可否指定爲null?
    答案是:不能!

  通過跟蹤發佈消息的代碼,在AMQImpl類中的Publish()方面中,可以看到,不光是exchange不能爲null,同時routingKey路由鍵也不能爲null,否則會拋出異常:

臨時隊列

在前面的例子中,我們使用的隊列都是有具體的隊列名,創建命名隊列是很必要的,因爲我們需要將消費者指向同一名字的隊列。因此,要想在生產者和消費者中間共享隊列就必須要使用命名隊列。

發佈者:

import pika
import json

credentials = pika.PlainCredentials('lvhua', '123456')  # mq用戶名和密碼
# 虛擬隊列需要指定參數 virtual_host,如果是默認的可以不填。
connection = pika.BlockingConnection(pika.ConnectionParameters(host = 'localhost',port = 5672,virtual_host = '/',credentials = credentials))
channel=connection.channel()
# 聲明exchange,由exchange指定消息在哪個隊列傳遞,如不存在,則創建。durable = True 代表exchange持久化存儲,False 非持久化存儲
channel.exchange_declare(exchange = 'python-test',durable = True, exchange_type='fanout')
for i in range(10):
    message=json.dumps({'OrderId':"1000%s"%i})
# 向隊列插入數值 routing_key是隊列名。delivery_mode = 2 聲明消息在隊列中持久化,delivery_mod = 1 消息非持久化。routing_key 不需要配置
    channel.basic_publish(exchange = 'python-test',routing_key = '',body = message,
                          properties=pika.BasicProperties(delivery_mode = 2))
    print(message)
connection.close()

訂閱者1

import pika

credentials = pika.PlainCredentials('lvhua', '123456')
connection = pika.BlockingConnection(pika.ConnectionParameters(host = 'localhost',port = 5672,virtual_host = '/',credentials = credentials))
channel = connection.channel()
# 創建臨時隊列,隊列名傳空字符,consumer關閉後,隊列自動刪除
result = channel.queue_declare('4')
# 聲明exchange,由exchange指定消息在哪個隊列傳遞,如不存在,則創建。durable = True 代表exchange持久化存儲,False 非持久化存儲
channel.exchange_declare(exchange = 'python-test',durable = True, exchange_type='fanout')
# 綁定exchange和隊列  exchange 使我們能夠確切地指定消息應該到哪個隊列去
channel.queue_bind(exchange = 'python-test',queue = "4")
# 定義一個回調函數來處理消息隊列中的消息,這裏是打印出來
def callback(ch, method, properties, body):
    ch.basic_ack(delivery_tag = method.delivery_tag)
    print(body.decode())

channel.basic_consume(result.method.queue,callback,# 設置成 False,在調用callback函數時,未收到確認標識,消息會重回隊列。True,無論調用callback成功與否,消息都被消費掉
                      auto_ack = False)
channel.start_consuming()

訂閱者2

import pika

credentials = pika.PlainCredentials('lvhua', '123456')
connection = pika.BlockingConnection(pika.ConnectionParameters(host = 'localhost',port = 5672,virtual_host = '/',credentials = credentials))
channel = connection.channel()
# 創建臨時隊列,隊列名傳空字符,consumer關閉後,隊列自動刪除
result = channel.queue_declare('2')
# 聲明exchange,由exchange指定消息在哪個隊列傳遞,如不存在,則創建。durable = True 代表exchange持久化存儲,False 非持久化存儲
channel.exchange_declare(exchange = 'python-test',durable = True, exchange_type='fanout')
# 綁定exchange和隊列  exchange 使我們能夠確切地指定消息應該到哪個隊列去
channel.queue_bind(exchange = 'python-test',queue = "2")
# 定義一個回調函數來處理消息隊列中的消息,這裏是打印出來
def callback(ch, method, properties, body):
    ch.basic_ack(delivery_tag = method.delivery_tag)
    print(body.decode())

channel.basic_consume(result.method.queue,callback,# 設置成 False,在調用callback函數時,未收到確認標識,消息會重回隊列。True,無論調用callback成功與否,消息都被消費掉
                      auto_ack = False)
channel.start_consuming()

模式二:direct

這種工作模式的原理是消息發送至exchange,exchange根據**路由鍵(routing_key)**轉發到相對應的queue上。

  • 可以使用默認exchange=’ ',也可以自定義exchange
  • 這種模式下不需要將exchange和任何進行綁定,當然綁定也是可以的,可以將exchange和queue,routing_key和queue進行綁定
  • 傳遞或接收消息時,需要指定routing_key
  • 需要先啓動訂閱者,此模式下隊列是consumer隨機生成的,發佈者僅僅發佈消息到exchange,由exchange轉發消息至queue。

發佈者:

import pika
import json

credentials = pika.PlainCredentials('shampoo', '123456')  # mq用戶名和密碼
# 虛擬隊列需要指定參數 virtual_host,如果是默認的可以不填。
connection = pika.BlockingConnection(pika.ConnectionParameters(host = '10.1.62.170',port = 5672,virtual_host = '/',credentials = credentials))
channel=connection.channel()
# 聲明exchange,由exchange指定消息在哪個隊列傳遞,如不存在,則創建。durable = True 代表exchange持久化存儲,False 非持久化存儲
channel.exchange_declare(exchange = 'python-test',durable = True, exchange_type='direct')

for i in range(10):
    message=json.dumps({'OrderId':"1000%s"%i})
# 指定 routing_key。delivery_mode = 2 聲明消息在隊列中持久化,delivery_mod = 1 消息非持久化
    channel.basic_publish(exchange = 'python-test',routing_key = 'OrderId',body = message,
                          properties=pika.BasicProperties(delivery_mode = 2))
    print(message)
connection.close()

訂閱者:

import pika

credentials = pika.PlainCredentials('shampoo', '123456')
connection = pika.BlockingConnection(pika.ConnectionParameters(host = '10.1.62.170',port = 5672,virtual_host = '/',credentials = credentials))
channel = connection.channel()
# 創建臨時隊列,隊列名傳空字符,consumer關閉後,隊列自動刪除
result = channel.queue_declare('',exclusive=True)
# 聲明exchange,由exchange指定消息在哪個隊列傳遞,如不存在,則創建。durable = True 代表exchange持久化存儲,False 非持久化存儲
channel.exchange_declare(exchange = 'python-test',durable = True, exchange_type='direct')
# 綁定exchange和隊列  exchange 使我們能夠確切地指定消息應該到哪個隊列去
channel.queue_bind(exchange = 'python-test',queue = result.method.queue,routing_key='OrderId')
# 定義一個回調函數來處理消息隊列中的消息,這裏是打印出來
def callback(ch, method, properties, body):
    ch.basic_ack(delivery_tag = method.delivery_tag)
    print(body.decode())


#channel.basic_qos(prefetch_count=1)
# 告訴rabbitmq,用callback來接受消息
channel.basic_consume(result.method.queue,callback,
# 設置成 False,在調用callback函數時,未收到確認標識,消息會重回隊列。True,無論調用callback成功與否,消息都被消費掉
                      auto_ack = False)
channel.start_consuming()

模式三:topicd

  這種模式和第二種差不多,exchange也是通過路由鍵routing_key來轉發消息到指定的queue。不同之處在於:
**routing_key使用正則表達式支持模糊匹配,**但匹配規則又與常規正則表達式不同,比如"#"是匹配全部,“*”是匹配一個詞。
舉例:routing_key =“#orderid#”,意思是將消息轉發至所有 routing_key 包含 “orderid” 字符的隊列中。代碼和模式二 類似,

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章