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