Frappe 内置了一套在后台运行任务的系统。它通过使用 schedule 包和一个简单的长时间运行的无限 while 循环来实现。
你可以使用 frappe.enqueue 方法将一个 Python 方法加入队列以在后台运行:
def long_running_job(param1, param2):
# expensive tasks
pass
# directly pass the function
frappe.enqueue(long_running_job, queue='short', param1='A', param2='B')
# or pass the full module path as string
frappe.enqueue('app.module.folder.long_running_job', queue='short', param1='A', param2='B')
以下是你传递给 enqueue 的所有可能参数:
frappe.enqueue(
method, # python function or a module path as string
queue="default", # one of short, default, long
timeout=None, # pass timeout manually
is_async=True, # if this is True, method is run in worker
now=False, # if this is True, method is run directly (not in a worker)
job_name=None, # specify a job name
enqueue_after_commit=False, # enqueue the job after the database commit is done at the end of the request
at_front=False, # put the job at the front of the queue
**kwargs, # kwargs are passed to the method as arguments
)
你也可以使用 frappe.enqueue_doc 将一个文档(Document)方法加入队列:
frappe.enqueue_doc(
doctype,
name,
"do_something", # name of the controller method
queue="long",
timeout=4000,
param="value"
)
队列
框架默认配置了 3 个队列:short、default 和 long。每个队列都有一个默认超时时间,如下所示:
- short:300 秒
- default:300 秒
- long:1500 秒
你也可以向 enqueue 方法传递自定义的超时时间。
自定义队列
你可以通过在 [common_site_config.json](https://frappeframework.com/docs/v14/user/en/basics/site_config#common-site-config) 中进行配置来添加自定义队列:
{
...
"workers": {
"myqueue": {
"timeout": 5000, # queue timeout
"background_workers": 4, # number of workers for this queue
}
}
}
工作进程
默认情况下,Frappe 会设置 3 种工作进程类型来消费各个队列中的任务。默认配置如下所示:
bench worker --queue short
bench worker --queue default
bench worker --queue long
在生产环境中,这 3 个工作进程会被复制到配置数量的后台工作进程中,以处理更高的工作负载。
注意:这种将工作进程映射到单个队列的方式只是一种约定,并非必须遵循。
多队列消费
你可以通过指定一个逗号分隔的队列名称字符串,来让工作进程消费多个队列。
示例:如果你想要合并 short 和 default 工作进程,并且只使用两种类型的工作进程而不是默认配置,你可以像这样修改你的工作进程配置:
bench worker --queue short,default
bench worker --queue long
注意:这里展示的示例是针对 Procfile 格式的,但它们也很容易应用于 supervisor 或 systemd 配置。
使用 --burst 的突发模式
bench worker --queue short --burst
该命令会生成一个临时工作进程,该进程将开始消费 short 队列,并在队列清空后退出。如果你定期需要更多的工作进程,你可以使用操作系统的 crontab 在特定时间设置突发工作进程。
调度器事件
你可以使用调度器事件,通过 scheduler_events 钩子在后台定期运行任务。
app/hooks.py
scheduler_events = {
"hourly": [
# will run hourly
"app.scheduled_tasks.update_database_usage"
],
}
app/scheduled_tasks.py
def update_database_usage():
pass
在 hooks.py 中更改任何计划事件后,你需要运行 bench migrate 才能使更改生效。
可用事件
hourly、daily、weekly和monthly
这些事件将分别每小时、每天、每周和每月触发一次。
hourly_long、daily_long、weekly_long、monthly_long
与上述相同,但这些任务在 long 工作进程中运行,适用于长时间运行的任务。
all
all 事件每 4 分钟触发一次。这可以通过 common_site_config.json 中的 scheduler_interval 键进行配置。
cron
一个有效的 cron 字符串,可以被 croniter 解析。
使用示例:
scheduler_events = {
"daily": [
"app.scheduled_tasks.manage_recurring_invoices"
],
"daily_long": [
"app.scheduled_tasks.take_backups_daily"
],
"cron": {
"15 18 * * *": [
"app.scheduled_tasks.delete_all_barcodes_for_users"
],
"*/6 * * * *": [
"app.scheduled_tasks.collect_error_snapshots"
],
"annual": [
"app.scheduled_tasks.collect_error_snapshots"
]
}
}
可配置的调度器事件
在需要用户可配置触发间隔的场景下,创建一个 Scheduler Event 记录,并针对该记录创建一个 Scheduled Job Type 条目。这不需要 scheduler_event 钩子。
示例:
# Create `Scheduler Event` record
sch_eve = frappe.new_doc("Scheduler Event")
sch_eve.scheduled_against = "Process Payment Reconciliation"
sch_eve.save()
# Create `Scheduled Job Type`
job = frappe.new_doc("Scheduled Job Type")
job.frequency = "Cron"
job.scheduler_event = sch_eve.name
job.cron_format = "0/5 * * * *" # runs every five minutes
job.save()
Scheduled Job Type 的触发间隔可以在之后修改,并且会在 bench 迁移时保持不变。
由调度器触发的任务由 管理员 用户运行。这也意味着,除非另有指定,否则你通过计划任务创建的任何文档都将归 管理员 用户所有。