為什么需要消息隊列
系統中引入消息隊列機制是對系統一個非常大的改善。例如一個web系統中,用戶做了某項操作后需要發送郵件通知到用戶郵箱中。你可以使用同步方式讓用戶等待郵件發送完成后反饋給用戶,但是這樣可能會因為網絡的不確定性造成用戶長時間的等待從而影響用戶體驗。
有些場景下是不可能使用同步方式等待完成的,那些需要后臺花費大量時間的操作。例如極端例子,一個在線編譯系統任務,后臺編譯完成需要30分鐘。這種場景的設計不可能同步等待后在回饋,必須是先反饋用戶隨后異步處理完成,再等待處理完成后根據情況再此反饋用戶與否。
另外適用消息隊列的情況是那些系統處理能力有限的情況下,先使用隊列機制把任務暫時存放起來,系統再一個個輪流處理掉排隊的任務。這樣在系統吞吐量不足的情況下也能穩定的處理掉高并發的任務。
消息隊列可以用來做排隊機制,只要系統需要用到排隊機制的地方就可以使用消息隊列來作。
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from multiprocessing import Pool
import time
import random
import os
import redis
import logging
redis_client = redis.StrictRedis(host='localhost', port=6379, db=0)
WORKER_QUEUE = 'worker_queue'
def do_task(queue_key, x):
x = int(x)
delay = random.randint(3, 7)
print '開始打包序號: %s 任務優先級別:%s' % (x, queue_key)
time.sleep(delay)
print '打包結束,耗時 %s 秒 結果是:%s 優先級別 %s' % (delay, x*x, queue_key)
redis_client.lpush(WORKER_QUEUE, 1)
return x*x
if __name__ == '__main__':
pros = 2
pool = Pool(processes=pros)
HIGH_QUEUE_KEY = 'high_task_queue'
NOMAL_QUEUE_KEY = 'task_queue'
LOW_QUEUE_KEY = 'low_task_queue'
"""
再創建一個隊列,用來檢測是否還有空余進程, 初始化隊列是pros個值
取一個,子進程工作一個任務
配合這個 redis_client.blpop(WORKER_QUEUE)
一個子進程工作完就, 就lpush到這個WORKER_QUEUE隊列
所有子進程都在工作,意味這個隊列是空的,那么主進程不循環。等待空閑子進程
"""
redis_client.delete(WORKER_QUEUE)
for i in range(0, pros):
redis_client.lpush(WORKER_QUEUE, 1)
"""
父進程就是執行的這個python
子進程就是spawn出來由4個進程組成的進程池
下面的pool.apply_async 只是交給進程池里面的進程處理
父進程不會執行任務,他只是作為一個無限循環
"""
while 1:
redis_client.blpop(WORKER_QUEUE)
queue_key, params = redis_client.blpop([HIGH_QUEUE_KEY, NOMAL_QUEUE_KEY, LOW_QUEUE_KEY])
print queue_key, params
"""
pool.apply()執行任務是同步執行,意思是執行完一個任務,才到下一個任務,
而每次使用的進程都是RR輪詢依次使用進程
所以所有任務都是 9 8 7 6 5 4 3 依次使用不用進程池里的進程執行
"""
# res = pool.apply(do_task, (params, ))
"""
pool.apply_async() 任務時異步執行, 執行完apply_async(), 無論任務執行多久
都不會阻塞,立馬返回一個對象, 然后就交給這個進程處理
主進程繼續往下循環
但是這樣異步一個任務后, 主進程繼續循環,繼續塞任務到進程池,所以某個子進程
可能再未處理完上一個任務情況下,繼續塞了下個任務進來。這樣就只能排隊執行了
"""
res = pool.apply_async(do_task, (queue_key, params))
思考
問:
進程一只阻塞再redis_client.blpop(key) 這里,如果隊列一致沒有任務,進程一直卡在這里,對CPU 內存的影響是怎樣的?
答:
計算機硬件上使用DMA來訪問磁盤等IO,也就是請求發出后,CPU就不再管了,直到DMA處理器完成任務,再通過中斷告訴CPU完成了。所以,單獨的一個IO時間,對CPU的占用是很少的,阻塞了就更不會占用CPU了,因為程序都不繼續運行了,CPU時間交給其它線程和進程了。雖然IO不會占用大量的CPU時間,但是非常頻繁的IO還是會非常浪費CPU時間的,所以面對大量IO的任務,有時候是需要算法來合并IO,或者通過cache來緩解IO壓力的。
blpop本質上還是recv_from 那一套, 等待IO時交出CPU
參考
http://www.cnblogs.com/laozhbook/p/redis_queue.html
https://www.zhihu.com/question/27734728