由於在做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()