翻译 - Python RQ Job

一个任务(job)就是一个Python对象,具体表现为在一个工作(后台)进程中异步调用一个函数。任何Python函数都可以异步调用,简单的将函数与参数追加到队列中,这叫做入队(enqueueing)。

入队

为了向队列中加入任务,我们需要先定义一个函数:

require requests

def count_words_at_url(url):
    resp = requests.get(url)
    return len(resp.text.split())

有那些需要注意的吗?这里没有对函数做任何限定,任意函数都可以加入队列。

为了将根据url计算其返回内容单词数量这个昂贵的操作放在后台运行,需要这样做:

from rq import Connection, Queue
from redis import Redis
from somewhere import count_words_at_url

redis_conn = Redis()
#告诉RQ使用Redis连接
q = Queue(connection = redis_conn)

#延迟执行函数 count_words_at_url("http://gudaojuanma.com")
job = q.enqueue(count_words_at_url, "http://gudaojuanma.com")
print job.result # => null

#等待一会,直到工作进程处理完
time.sleep(2)
print job.result # => 1000

如果想要把任务加到一个指定的队列中,可以这样指定队列的名字:

q = Queue("low", connection = Redis())
q.enqueue(count_words_at_url, "http://gudaojuanma.com")

注意到上面例子中的Queue("low")了吗?你可以指定任意的名字,这样就可以根据你的需要灵活地归类你的任务了。一般会根据优先级给队列命名(如:high, medium, low)。

对于想要给enqueue传递参数的情况(而不是任务函数),可以使用enqueue_call方法。在要传递超时参数的情况下:

q = Queue("low", connection=Redis())
q.enqueue_call(func=count_words_at_url, 
    args= ("http://gudaojuanma.com",), 
    timeout = 30)

当工作进程没有进入代码源里面执行函数时(如在X中调用基于Y的函数),可以使用函数字符串引用。(译者:大致明白,但语言理不顺)

q = Queue("low", count_words_at_url)
q.enqueue("my_package.my_module.my_func", 2, 3)

在设计方面

使用RQ,不用预先设置队列,也不用指定任何通道,转换,路由通道等其他乱七八糟的东西。直接把你想要的任务加入队列就好了。如果你加入任务的队列不存在,则队列立马就会被创建。

RQ没有使用高级的中间件实现消息路由,这是好是坏,取决于你要解决的问题。

最后,它没有描述一个轻便的协议,自从使用pickle序列化任务。所以他是一个只支持Python的系统。

延迟结果

当任务加入队列,queue.enqueue()方法返回一个job实例。没有什么比一个代理对象来检测结果来得更实际了。出于这个目的,它有一个可访问的result属性。当任务没有执行的时候返回None,否则返回非空值(当然,你的任务确实有内容返回才行)。

@job装饰器

如果你用惯了Gelery,也许使用过@task装饰器。自从RQ版本0.3开始,就有一个类似的装饰器:

from rq.decorators import job

@job("low", connection=my_redis_conn, timeout=5)
def add(x, y)
    return x + y

job = add.delay(3, 4)
time.sleep(1)
print job.result

绕过工作进程

出于测试的考虑,你可以在入队的时候不委托工作者来实际执行任务(从0.3.1版本适用)。你可以在Queue构造方法中传递参数async=False类达到此目的:

>>> q = Queue("low", async=False)
>>> job = q.enqueue(fib, 8)
>>> job.result
21

没有在工作进程中运行代码,而是在同一进程中同步执行任务。你可能在Gelery中的AlWAYS_EAGER遇见过相似的功能。

任务依赖

从版本0.4.0起支持任务的联动执行。使用参数depends_on参数,使一个任务的执行依赖于另一个任务。

q = Queue("low", async=False)
report_job = q.enqueue(generate_report)
q.enqueue(send_report, depends_on=report_job)

这种任务之间依赖执行的能力,可以让你把大的任务切分为多个小的任务来执行。一个依赖于另一个任务的任务,只有当他依赖的任务执行成功后,才会被加入队列。

工作进程

细节请查看workers的文档。

关于任务的注意事项

技术上来说,你可以放置任何Python可执行方法到队列中去,但是并不总是明智的。下面是一些入队时要考虑的事项:

  • 确定任务函数的包被工作进程包含了。这意味着你不能将__main__模块中的函数入队。
  • 确定工作进程与其生成器共享同一套源代码
  • 确定任务函数的执行不依赖于上下文。全局变量就不太好(一直是这样的),任务函数依赖的任务状态在工作进程执行时候都是不存在的。如果确定要使用一些当前状态等,你应该将他们封装成一个实例,然后将其引用作为参数传递(入队的时候)。

局限性

RQ工作进程只能在实现了fork()调用的系统上运行。很明显,在Windows上是运行不了的。

posted on 2014-07-15 14:33  古道倦马  阅读(2012)  评论(0编辑  收藏  举报

导航