Python中線程的MQ訊息佇列實現以及訊息佇列的優點解析,pythonmq
“訊息佇列”是在訊息的傳輸過程中儲存訊息的容器。訊息佇列管理器在將訊息從它的源中繼到它的目標時充當中間人。隊列的主要目的是提供路由並保證訊息的傳遞;如果發送訊息時接收者不可用,訊息佇列會保留訊息,直到可以成功地傳遞它。相信對任何架構或應用來說,訊息佇列都是一個至關重要的組件,下面是十個理由:
Python的訊息佇列樣本:
1.threading+Queue實現線程隊列
#!/usr/bin/env python import Queueimport threadingimport time queue = Queue.Queue() class ThreadNum(threading.Thread): """沒列印一個數字等待1秒,並發列印10個數字需要多少秒?""" def __init__(self, queue): threading.Thread.__init__(self) self.queue = queue def run(self): whileTrue: #消費者端,從隊列中擷取num num = self.queue.get() print"i'm num %s"%(num) time.sleep(1) #在完成這項工作之後,使用 queue.task_done() 函數向任務已經完成的隊列發送一個訊號 self.queue.task_done() start = time.time()def main(): #產生一個 threads pool, 並把訊息傳遞給thread函數進行處理,這裡開啟10個並發 for i in range(10): t = ThreadNum(queue) t.setDaemon(True) t.start() #往隊列中填錯資料 for num in range(10): queue.put(num) #wait on the queue until everything has been processed queue.join() main()print"Elapsed Time: %s" % (time.time() - start)
運行結果:
i'm num 0i'm num 1i'm num 2i'm num 3i'm num 4i'm num 5i'm num 6i'm num 7i'm num 8i'm num 9Elapsed Time: 1.01399993896
解讀:
具體工作步驟描述如下:
1,建立一個 Queue.Queue() 的執行個體,然後使用資料對它進行填充。
2,將經過填充資料的執行個體傳遞給線程類,後者是通過繼承 threading.Thread 的方式建立的。
3,產生守護線程池。
4,每次從隊列中取出一個項目,並使用該線程中的資料和 run 方法以執行相應的工作。
5,在完成這項工作之後,使用 queue.task_done() 函數向任務已經完成的隊列發送一個訊號。
6,對隊列執行 join 操作,實際上意味著等到隊列為空白,再退出主程式。
在使用這個模式時需要注意一點:通過將守護線程設定為 true,程式運行完自動結束。好處是在退出之前,可以對隊列執行 join 操作、或者等到隊列為空白。
2.多個隊列
所謂多個隊列,一個隊列的輸出可以作為另一個隊列的輸入!
#!/usr/bin/env pythonimport Queueimport threadingimport time queue = Queue.Queue()out_queue = Queue.Queue() class ThreadNum(threading.Thread): """bkeep""" def __init__(self, queue, out_queue): threading.Thread.__init__(self) self.queue = queue self.out_queue = out_queue def run(self): whileTrue: #從隊列中取訊息 num = self.queue.get() bkeep = num #將bkeep放入隊列中 self.out_queue.put(bkeep) #signals to queue job is done self.queue.task_done() class PrintLove(threading.Thread): """Threaded Url Grab""" def __init__(self, out_queue): threading.Thread.__init__(self) self.out_queue = out_queue def run(self): whileTrue: #從隊列中擷取訊息並賦值給bkeep bkeep = self.out_queue.get() keke = "I love " + str(bkeep) print keke, print self.getName() time.sleep(1) #signals to queue job is done self.out_queue.task_done() start = time.time()def main(): #populate queue with data for num in range(10): queue.put(num) #spawn a pool of threads, and pass them queue instance for i in range(5): t = ThreadNum(queue, out_queue) t.setDaemon(True) t.start() for i in range(5): pl = PrintLove(out_queue) pl.setDaemon(True) pl.start() #wait on the queue until everything has been processed queue.join() out_queue.join() main()print"Elapsed Time: %s" % (time.time() - start)
運行結果:
I love 0 Thread-6I love 1 Thread-7I love 2 Thread-8I love 3 Thread-9I love 4 Thread-10I love 5 Thread-7I love 6 Thread-6I love 7 Thread-9I love 8 Thread-8I love 9 Thread-10Elapsed Time: 2.00300002098
解讀:
ThreadNum 類工作流程
定義隊列--->繼承threading---->初始化queue---->定義run函數--->get queue中的資料---->處理資料---->put資料到另外一個queue-->發訊號告訴queue該條處理完畢
main函數工作流程:
--->往自訂queue中扔資料
--->for迴圈確定啟動的線程數---->執行個體化ThreadNum類---->啟動線程並設定守護
--->for迴圈確定啟動的線程數---->執行個體化PrintLove類--->啟動線程並設定為守護
--->等待queue中的訊息處理完畢後執行join。即退出主程式。
瞭解了MQ的大概實現以後,我們來總結一下訊息佇列的優點:
1. 解耦
在項目啟動之初來預測將來項目會碰到什麼需求,是極其困難的。訊息佇列在處理過程中間插入了一個隱含的、基於資料的介面層,兩邊的處理過程都要實現這一介面。這允許你獨立的擴充或修改兩邊的處理過程,只要確保它們遵守同樣的介面約束。
2. 冗餘
有時在處理資料的時候處理過程會失敗。除非資料被持久化,否則將永遠丟失。訊息佇列把資料進行持久化直到它們已經被完全處理,通過這一方式規避了資料丟失風險。在被許多訊息佇列所採用的"插入-擷取-刪除"範式中,在把一個訊息從隊列中刪除之前,需要你的處理過程明確的指出該訊息已經被處理完畢,確保你的資料被安全的儲存直到你使用完畢。
3. 擴充性
因為訊息佇列解耦了你的處理過程,所以增大訊息入隊和處理的頻率是很容易的;只要另外增加處理過程即可。不需要改變代碼、不需要調節參數。擴充就像調大電力按鈕一樣簡單。
4. 靈活性 & 峰值處理能力
當你的應用上了Hacker News的首頁,你將發現訪問流量攀升到一個不同尋常的水平。在訪問量劇增的情況下,你的應用仍然需要繼續發揮作用,但是這樣的突發流量並不常見;如果為 以能處理這類峰值訪問為標準來投入資源隨時待命無疑是巨大的浪費。使用訊息佇列能夠使關鍵組件頂住增長的訪問壓力,而不是因為超出負荷的請求而完全崩潰。 請查看我們關於峰值處理能力的部落格文章瞭解更多此方面的資訊。
5. 可恢複性
當體系的一部分組件失效,不會影響到整個系統。訊息佇列降低了進程間的耦合度,所以即使一個處理訊息的進程掛掉,排入佇列中的訊息仍然可以在系統復原後被處理。而這種允許重試或者延後處理請求的能力通常是造就一個略感不便的使用者和一個沮喪透頂的使用者之間的區別。
6. 送達保證
訊息佇列提供的冗餘機制保證了訊息能被實際的處理,只要一個進程讀取了該隊列即可。在此基礎上,IronMQ提供了一個"只送達一次"保證。無論有多少進 程在從隊列中領取資料,每一個訊息只能被處理一次。這之所以成為可能,是因為擷取一個訊息只是"預定"了這個訊息,暫時把它移出了隊列。除非用戶端明確的 表示已經處理完了這個訊息,否則這個訊息會被放回隊列中去,在一段可配置的時間之後可再次被處理。
7.排序保證
在許多情況下,資料處理的順序都很重要。訊息佇列本來就是排序的,並且能保證資料會按照特定的順序來處理。IronMO保證訊息漿糊通過FIFO(先進先出)的順序來處理,因此訊息在隊列中的位置就是從隊列中檢索他們的位置。
8.緩衝
在任何重要的系統中,都會有需要不同的處理時間的元素。例如,載入一張圖片比應用過濾器花費更少的時間。訊息佇列通過一個緩衝層來協助任務最高效率的執行--寫入隊列的處理會儘可能的快速,而不受從隊列讀的預備處理的約束。該緩衝有助於控制和最佳化資料流經過系統的速度。
9. 理解資料流
在一個分布式系統裡,要得到一個關於使用者操作會用多長時間及其原因的總體印象,是個巨大的挑戰。訊息系列通過訊息被處理的頻率,來方便的輔助確定那些表現不佳的處理過程或領域,這些地方的資料流都不夠最佳化。
10. 非同步通訊
很多時候,你不想也不需要立即處理訊息。訊息佇列提供了非同步處理機制,允許你把一個訊息放入隊列,但並不立即處理它。你想向隊列中放入多少訊息就放多少,然後在你樂意的時候再去處理它們。