Python中的使用标准queue模块就可以建立多进程使用的队列,但是使用redis和redis-queue(rq)模块使这一操作更加简单。
Part 1.
比如首先我们使用队列来简单的储存数据:我们选用redis list
类型,其他类型的数据操作可以参考这个文章
在redis_queue.py
文件中:
import redis class RedisQueue(object): def __init__(self, name, namespace='queue', **redis_kwargs): # redis的默认参数为:host='localhost', port=6379, db=0, 其中db为定义redis database的数量 self.__db= redis.Redis(**redis_kwargs) self.key = '%s:%s' %(namespace, name) def qsize(self): return self.__db.llen(self.key) # 返回队列里面list内元素的数量 def put(self, item): self.__db.rpush(self.key, item) # 添加新元素到队列最右方 def get_wait(self, timeout=None): # 返回队列第一个元素,如果为空则等待至有元素被加入队列(超时时间阈值为timeout,如果为None则一直等待) item = self.__db.blpop(self.key, timeout=timeout) # if item: # item = item[1] # 返回值为一个tuple return item def get_nowait(self): # 直接返回队列第一个元素,如果队列为空返回的是None item = self.__db.lpop(self.key) return item
在input.py
文件中:
from redis_queue import RedisQueue import time q = RedisQueue('rq') # 新建队列名为rq for i in range(5): q.put(i) print "input.py: data {} enqueue {}".format(i, time.strftime("%c")) time.sleep(1)
在output.py
文件中:
from redis_queue import RedisQueue import time q = RedisQueue('rq') while 1: result = q.get_nowait() if not result: break print "output.py: data {} out of queue {}".format(result, time.strftime("%c")) time.sleep(2)
在test_run.sh
文件中:
python input.py &
python output.py &
在terminal中运行test.sh的结果如下:
input.py: data 0 enqueue Fri Nov 10 10:51:37 2017 output.py: data 0 out of queue Fri Nov 10 10:51:37 2017 input.py: data 1 enqueue Fri Nov 10 10:51:38 2017 output.py: data 1 out of queue Fri Nov 10 10:51:38 2017 input.py: data 2 enqueue Fri Nov 10 10:51:39 2017 output.py: data 2 out of queue Fri Nov 10 10:51:39 2017 input.py: data 3 enqueue Fri Nov 10 10:51:40 2017 output.py: data ('queue:rq', '3') out of queue Fri Nov 10 10:51:40 2017 input.py: data 4 enqueue Fri Nov 10 10:51:41 2017 output.py: data ('queue:rq', '4') out of queue 1510282301.69
其中lpop返回的是一个队列名+元素值的tuple,并且返回的数值默认为string
Part 2.
队列里面可以添加任意object,因此可以添加函数到多个队列来实现多线程并发的效果。
首先,建立一个rq work进程(写在worker.py
脚本中)来监听队列
import redis from rq import Worker, Queue, Connection listen = ['default'] redis_url = "redis://localhost:6379" # redis server 默认地址 conn = redis.from_url(redis_url) def square_function(x): return x*x if __name__ == '__main__': with Connection(conn): # 建立与redis server的连接 worker = Worker(list(map(Queue, listen))) # 建立worker监听给定的队列 worker.work()
然后python worker.py
启动redis server
在test.py
文件中:
from rq import Queue from rq.job import Job from worker import square_function, conn import time q = Queue(connection=conn) job = q.enqueue_call(square_function, args=(5, ), result_ttl=5000) # 保存结果5000s job_id = job.get_id() print job_id result1 = Job.fetch(job_id, connection=conn) print result1.is_finished time.sleep(1) # 等待队列里任务完成 result2 = Job.fetch(job_id, connection=conn) print result2.return_value
上面文件的输出结果为:
98dc6f58-9333-48f7-88c1-c4de1cc9e5cf # job id False # 任务尚未完成 25 # 任务完成,输出结果
注: 调用的square_function不允许和任务发起在同一个脚本,否则会报错ValueError: Functions from the __main__ module cannot be processed by workers
当与flask一起使用时:
在app.py
文件中:
from rq import Queue from rq.job import Job from worker import conn, square_function from flask import Flask, request app = Flask(__name__) q = Queue(connection=conn) # 建立与Redis server的连接并初始化一个队列 @app.route("/", methods=['POST']) def index(): x = request.get_data() # string 类型 job = q.enqueue_call(square_function, args=(int(x), ), result_ttl=5000) # 最后的参数为RQ保留结果的时间 return job.get_id() # 返回job的id @app.route('/result/<job_key>', methods=['GET']) def get_results(job_key): job = Job.fetch(job_key, connection=conn) # 获取根据job_id获取任务的返回值 if job.is_finished: # 检验是否完成 return str(job.result), 200 else: return "Wait!", 202 if __name__ == "__main__": app.run('0.0.0.0', port=5000)
python app.py
启动flask服务
在test.py
文件:
import requests import time post_url = "http://localhost:5000" post_result = requests.post(post_url, data={'x': 2}) job_id = post_result.content print job_id time.sleep(1) get_url = "http://localhost:5000/result/{}".format(job_id) get_result = requests.get(get_url) print get_result.content
获得的结果如下:
067306e9-f13b-4b6a-93dc-2f5b457a78b7 # job id 4 # job返回值