• python中利用redis构建任务队列(queue)


    Python中的使用标准queue模块就可以建立多进程使用的队列,但是使用redisredis-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返回值
    

    参考:

    1. https://realpython.com/blog/python/flask-by-example-implementing-a-redis-task-queue/
    2. http://peter-hoffmann.com/2012/python-simple-queue-redis-queue.html
    3. http://python-rq.org/docs/
    4. https://www.cnblogs.com/melonjiang/p/5342505.html
    5. http://xiaorui.cc/2015/11/11/解决python-rq获取返回结果和异常的问题/
  • 相关阅读:
    实验二 递归下降语法分析
    作业十一
    第七次作业逻辑回归实践
    机器学习第六次作业
    第五次作业
    第三次作业k均值算法
    第二次作业
    机器学习作业一
    14次作业
    12 实验二 递归下降语法分析
  • 原文地址:https://www.cnblogs.com/arkenstone/p/7813551.html
Copyright © 2020-2023  润新知