Python多進程執行Redis優先級隊列任務

為什么需要消息隊列

系統中引入消息隊列機制是對系統一個非常大的改善。例如一個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

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • word直接復制來了,格式就不改了。至于這門課怎么復習,只要平時實驗都認真完成、報告認真寫,平時分都很高;考試的話...
    Jozhn閱讀 4,626評論 0 8
  • 又來到了一個老生常談的問題,應用層軟件開發的程序員要不要了解和深入學習操作系統呢? 今天就這個問題開始,來談談操...
    tangsl閱讀 4,165評論 0 23
  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,908評論 18 139
  • ——最美好的事情就是認識了你,并且想和你一直像這樣美好的走下去。 記得我們認識是在高一的時候,不知道那是...
    筱筱的星辰閱讀 576評論 0 2
  • 我喜歡拍照
    欍人_閱讀 263評論 0 0