翻译 - 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上是运行不了的。