python異步隊列queue驗證demo

由於在做websocket接收行情的事情,估計事情做多了,python在短時間內反應不過來,考慮將數據交到別處處理,搜索後發現python有一個queue.Queue()是一個不錯的工具。
主要用到三個功能,

1 queue.Queue()

可以設置一個默認大小,超過後會阻塞

2 put()

向隊列中放入數據,若超過隊列大小後會一直阻塞,當然,還有一個超時功能,暫時不需要。

3 get()

從隊列中取數據,取到最後取空了,同樣會阻塞。

然後借用一個異步任務
from apscheduler.schedulers.background import BackgroundScheduler
來進行不同任務的模擬
首先我們定義一個生產消費類:

class ProducerConsumer:
    def __init__(self, word):
        self.my_queue = queue.Queue(3)
        self.word = word

規定隊列長度爲3

生產數據比較簡單:

    def produce(self, count):
        input_data = "{}-{}".format(self.word, count)
        self.my_queue.put(input_data)
        logger.info("put data: {}".format(input_data))

消費數據也比較簡單:

    def consume(self):
        text = self.my_queue.get()
        logger.info("get data: {}".format(text))

然後就是連續的生產和消費任務:

    def batch_produce(self):
        count = 0
        while True:
            count += 1
            self.produce(count)

    def batch_consume(self):
        while True:
            self.consume()
            time.sleep(1)

生產任務不停生產數據,直到隊列滿了,然後被阻在那裏,消費任務不停消費,爲了控制節奏,每消費一個休息一秒,主要是用來觀察消費不完後,生產者是否真被塞在那兒了。
然後將這兩個批量任務放置到異步任務中,讓他們各自運行:

    @staticmethod
    def job(obj):
        sched = BackgroundScheduler()
        sched.add_job(obj.batch_consume)
        sched.add_job(obj.batch_produce)
        sched.start()

爲了使程序有趣,我們同時跑兩個消費者和兩個生產者,確認各自的隊列不受干擾:

def do_work():
    ProducerConsumer.job(ProducerConsumer("aaaa"))
    ProducerConsumer.job(ProducerConsumer("bbbb"))
    while True:
        time.sleep(1)

運行後的數據大致如下:

[2020-04-09 15:55:58.741999][INFO][queue_demo.py][produce][23] put data: aaaa-1
[2020-04-09 15:55:58.741999][INFO][queue_demo.py][produce][23] put data: bbbb-1
[2020-04-09 15:55:58.741999][INFO][queue_demo.py][consume][38] get data: aaaa-1
[2020-04-09 15:55:58.741999][INFO][queue_demo.py][consume][38] get data: bbbb-1
[2020-04-09 15:55:58.741999][INFO][queue_demo.py][produce][23] put data: aaaa-2
[2020-04-09 15:55:58.741999][INFO][queue_demo.py][produce][23] put data: bbbb-2
[2020-04-09 15:55:58.743002][INFO][queue_demo.py][produce][23] put data: aaaa-3
[2020-04-09 15:55:58.743002][INFO][queue_demo.py][produce][23] put data: bbbb-3
[2020-04-09 15:55:58.743002][INFO][queue_demo.py][produce][23] put data: aaaa-4
[2020-04-09 15:55:58.743002][INFO][queue_demo.py][produce][23] put data: bbbb-4
[2020-04-09 15:55:59.743940][INFO][queue_demo.py][consume][38] get data: aaaa-2
[2020-04-09 15:55:59.743940][INFO][queue_demo.py][consume][38] get data: bbbb-2
[2020-04-09 15:55:59.743940][INFO][queue_demo.py][produce][23] put data: aaaa-5
[2020-04-09 15:55:59.743940][INFO][queue_demo.py][produce][23] put data: bbbb-5
[2020-04-09 15:56:00.745094][INFO][queue_demo.py][consume][38] get data: aaaa-3
[2020-04-09 15:56:00.745094][INFO][queue_demo.py][consume][38] get data: bbbb-3
[2020-04-09 15:56:00.745094][INFO][queue_demo.py][produce][23] put data: aaaa-6
[2020-04-09 15:56:00.745094][INFO][queue_demo.py][produce][23] put data: bbbb-6

很明顯,由於生產中間沒有停頓,所以總是儘可能生產滿了,然後等消費取數據,當發現又可以生產時,立即生產,使得隊列在運行時儘可能是滿的狀態。

最後附所有代碼:

# !/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
一個多任務下,異步queue的測試
測試生產者和消費者模型,爲後面程序解耦作準備
"""
from apscheduler.schedulers.background import BackgroundScheduler
import queue
import time
import sys
sys.path.append("../../")
from utils.user_logbook import user_log as logger


class ProducerConsumer:
    def __init__(self, word):
        self.my_queue = queue.Queue(3)
        self.word = word

    def produce(self, count):
        input_data = "{}-{}".format(self.word, count)
        self.my_queue.put(input_data)
        logger.info("put data: {}".format(input_data))

    def batch_produce(self):
        count = 0
        while True:
            count += 1
            self.produce(count)

    def batch_consume(self):
        while True:
            self.consume()
            time.sleep(1)

    def consume(self):
        text = self.my_queue.get()
        logger.info("get data: {}".format(text))

    @staticmethod
    def job(obj):
        sched = BackgroundScheduler()
        sched.add_job(obj.batch_consume)
        sched.add_job(obj.batch_produce)
        sched.start()


def do_work():
    ProducerConsumer.job(ProducerConsumer("aaaa"))
    ProducerConsumer.job(ProducerConsumer("bbbb"))
    while True:
        time.sleep(1)


if __name__ == "__main__":
    do_work()

日誌採用logbook的日誌封裝,在此一併附上:

# !/usr/bin/env python3
# -*- coding: utf-8 -*-
# filename:user_logbook.py
import os
import logbook
from logbook import Logger, TimedRotatingFileHandler
from logbook.more import ColorizedStderrHandler
DATETIME_FORMAT = "%Y-%m-%d %H:%M:%S.00"


def user_handler_log_formatter(record, handler):
    log = "[{dt}][{level}][{filename}][{func_name}][{lineno}] {msg}".format(
        dt=record.time,
        level=record.level_name,                        # 日誌等級
        filename = os.path.split(record.filename)[-1],  # 文件名
        func_name = record.func_name,                   # 函數名
        lineno = record.lineno,                         # 行號
        msg=record.message,                             # 日誌內容
    )
    return log


# 打印到屏幕句柄
user_std_handler = ColorizedStderrHandler(bubble=True)
user_std_handler.formatter = user_handler_log_formatter
# 日誌路徑,在主工程下生成log目錄
LOG_DIR = os.path.join('../log')
if not os.path.exists(LOG_DIR):  
    os.makedirs(LOG_DIR)
    
# 打印到文件句柄
user_file_handler = TimedRotatingFileHandler(
    os.path.join(LOG_DIR , '%s.log' % 'server'), date_format='%Y%m%d', bubble=True)
user_file_handler.formatter = user_handler_log_formatter


# 用戶代碼logger日誌
user_log = Logger("user_log")


def init_logger():
    logbook.set_datetime_format("local")
    user_log.handlers = []
    user_log.handlers.append(user_std_handler)
    user_log.handlers.append(user_file_handler)    


# 初始化日誌系統(被默認調用)
init_logger()
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章