计划任务
计划任务
介绍
应用中经常要在特定时间执行一些周期性的任务,例如每个星期,每分钟或者每几月。这些任务可能是:
- 从目录中读取XML文件并导入数据库
- 检查是否在Stripe是否还有支付需要更新到数据库
- 清除数据库中不需要的数据
- 发送某些API来触发事件
或者一些其他的工作。有很多用例需要在一天某些时刻区执行,甚至是在员工下班后的离线
工作。
入门
首先我们需要安装调度器功能。我们仅需使用pip来完成安装:
$ pip install masonite-scheduler
然后加入Service Provider 到config/providers.py的PROVIDERS
列表中:
config/providers.py
...
from masonite.scheduler.providers import ScheduleProvider
PROVIDERS = [
AppProvider,
...
...
ScheduleProvider, # 这里
]
该provider会为Masonite增加几个新功能。首先是增添了两个新命令。
第一个命令是craft schedule:run
命令,用来执行所有需要执行的计划任务(待会我们会创建)。
第二个命令是craft task
,用来在app/tasks
目录下新增命令。
创建一个命令
现在我们已经增加好了Service Provider,我们现在可以开始创建任务了,首先我们来创建一个超级简单的任务,打印一个”Hi”。先创建任务:
$ craft task SayHi
新建的文件位于app/tasks/SayHi.py
from scheduler.Task import Task
class SayHi(Task):
def __init__(self):
pass
def handle(self):
pass
这就是我们任务的脚手架。
所有的任务都需要继承scheduler.task.Task
类。这样为任务添加所需的方法,也可以为实现容器收集。
确保学习收集对象,查看Service Container里的收集文档进行了解。
容器自动加载
为了使任务可以在在container内部可以自动发现。在容器收集后,会查看是否需要运行。
有两种方法从容器中获取到类。第一种使用手动创建Service Provider来绑定到容器。如果不知道如何实现,你可以通过Service Providers文档来进行创建。
另外一种方式是使用 自动加载。Masonite从2.0开始,你可以使用容器加载一个目录的所有类。可以通过配置文件config/application.py
里的AUTOLOAD配置项目来实现:
config/application.py
:
...
AUTOLOAD = [
'app',
'app/tasks'
]
...
这里将查找所有app/tasks
目录下的任务类,加载到容器中,使用类名作为键进行绑定。
手动加载任务
你不必非得使用自动加载,你可以手动来调度任务。你需要使用Service Provider来绑定。
如果你要手动来加载任务的话你需要继承CanSchedule
类。它提供self.call()
方法来让你更容易实现任务和命令。
你可以在Service Provider中的register方法来调度:
from masonite.scheduler import CanSchedule
from app.jobs.YourJob import YourJob
class YourServiceProvider(ServiceProvider, CanSchedule):
def register(self):
self.schedule(YourJob()).every('3 days')
你可使用如下的一些方法进行调度: every('5 minutes')
, every_minute()
, every_15_minutes()
, every_30_minutes()
, every_45_minutes()
, daily()
, hourly()
, weekly()
, monthly()
.
命令调度
你可以用相同的方法来实现命令调度。
如果是手动加载任务的话同样继承CanSchedule
类。
from masonite.scheduler import CanSchedule
from app.jobs.YourJob import YourJob
class YourServiceProvider(Serv):
def register(self):
self.call('your:command --flag').daily().at('9:00')
实现Task类
我们的任务已经可以自动加载到容器中,现在让我们来实现该类:
构造器
首先,构造器中的依赖可使用容器来完成。你可以从容器中获取中获取到任意不需要WSGI服务器执行的对象(大部分都不需要)。例如Upload, Mail, Broadcast和Request都可以获取,如下:
from scheduler.Task import Task
from masonite.request import Request
class SayHi(Task):
def __init__(self, request: Request):
self.request = request
def handle(self):
pass
Handle 方法
handle方法就是任务所要实现的逻辑。
例如我们开源请求API:
from scheduler.Task import Task
import requests
class SayHi(Task):
def __init__(self):
pass
def handle(self):
requests.post('http://url.com/api/store')
何时运行
任务神奇的地方在于我们开源定义执行的时间。我们通过Task几个选项来实现:
一个完成的任务代码类似如下:
from scheduler.Task import Task
import requests
class SayHi(Task):
run_every = '3 days'
run_at = '17:00'
def __init__(self):
pass
def handle(self):
requests.post('http://url.com/api/store')
该任务只会在每3天下午5点执行。
所有选项默认值为False
。存在的选项有:
属性 | 值 | 例子 |
---|---|---|
run_every | 接受单复数的时间单位:minutes , hours , days , months |
run_every = ‘1 day’ |
run_at | 24时间制时间 (“17:00” for 5pm) | run_at = ‘17:00’ |
run_every_hour | 布尔值,指示是否每小时执行:True , False |
run_every_hour = True |
run_every_minute | 布尔值,指示是否每分钟执行: True , False |
run_every_minute = True |
twice_daily | 一个元组值,里面元素也是24小时制各式的小时数,用来指定一天两个运行时间。 (1, 13) 将在上午1时和下午1时执行。 |
twice_daily = (1, 13) |
如果时间里的单位是days
或者months
,同样也可以使用run_at
来指定确切的执行时间。默认,设置了days
时会在午夜(半夜12点)执行,months
时就会在当月第一个天的午夜执行。我们也可以通过在设置了run_every
属性时,同时设定run_at
来控制确切的执行时间点。该选项设定的话,如果run_every
设置的值为minutes
或者hours
时会忽律。
时区
你也可以在单个任务里执行timezone
时区属性:
from scheduler.Task import Task
import requests
class SayHi(Task):
run_every = '3 days'
run_at = '17:00'
timezone = 'America/New_York'
def __init__(self):
pass
def handle(self):
requests.post('http://url.com/api/store')
警告
此功能被设计无需单独的服务器命令来执行,所以需要一个注意事项,请务必阅读本节来全面了解其工作原理。
何时执行
由于调度器并不知道服务器是何时启动,它不知道从哪一天开始计数。为了解决这个问题,Masonite使用一个模数运算符来计算当前日期,通过查看任务时间和当前时间的模数是否为0。
例如,如果上面的任务执行时间为(every 3 days),那么将在5月3日,5月6日,5月9日,5月12日的午夜执行,以此类推。所以,需要注意是如果任务创建在5月11日,并且设定为每3天执行,那么12日就会被执行。
执行任务
在我们加入AUTOLOAD
列表后,我们就可以通过schedule:run
命令来找到要执行的命令了。
$ craft schedule:run
Masonite将会从容器里拿到所有继承scheduler.tasks.Task
子类的任务,检查它们是否到了执行时间,然后执行。
即使我们运行任务,也应该看不到任何输出.我们修改一下任务添加 printing “Hi” 和其设置为每分钟运行:
from scheduler.Task import Task
class SayHi(Task):
run_every = '1 minute'
def __init__(self):
pass
def handle(self):
print('Hi!')
现在,让我们再次运行命令:
$ craft schedule:run
现在我们应该看到”hi!” 输出在终端窗口.
执行一个具体的任务
您也可以通过运行带有–task标志的schedule:run命令来运行特定任务。标志值是容器绑定(通常是任务类名称):
craft schedule:run --task SayHi
或者,您可以为任务明确命名:
from scheduler.Task import Task
class SayHi(Task):
run_every = '1 minute'
name = 'hey'
def __init__(self):
pass
def handle(self):
print('Hi!')
然后按名称运行命令
craft schedule:run --task hey
Cron Jobs
设置定时任务仅适用于基于UNIX的机器,例如Mac和Linux. Windows 有一个类似schedule 叫 Task Scheduler 该方式与此类似 但是需要不同的方式来设置它.
尽管上面的方式很有用, 但在生产环境中不是很实用. 在生产中, 我们应该设置一个每分钟执行的定时任务 以便Masonite可以决定需要执行哪些任务.
我们将向您展示一个示例 定时任务,然后介绍如何逐步构建它.
PATH=/Users/Masonite/Programming/project_name/venv/bin:/Library/Frameworks/Python.framework/Versions/3.6/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/Frameworks/Python.framework/Versions/3.6/bin
* * * * * cd /Users/Masonite/Programming/project_name && source venv/bin/activate && craft schedule:run
获取路径
当cron job运行时,它将使用/bin/sh命令来执行,而不是/bin/bash。因此,有可能不能正常在机器上找到craft命令,所以我们需要告诉cron job放置其路径到PATH环境中。我们可以简单通过如下命令来获取PATH变量:
$ env
这将打印类似如下信息:
...
__CF_USER_TEXT_ENCODING=0x1F5:0x0:0x0
PATH=/Library/Frameworks/Python.framework/Versions/3.6/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/Frameworks/Python.framework/Versions/3.6/bin
PWD=/Users/Masonite/Programming/masonite
...
如果你使用的是针对开发目的的虚拟环境,那么你需要在虚拟环境下执行env
。
我们现在可以拷贝PATH环境变量放置到cron job。
要进入cron,只需执行:
$ env EDITOR=nano crontab -e
然后粘贴拷贝的PATH
内容。一旦我们操作完内容类似如下:
PATH=/Users/Masonite/Programming/masonitetesting/venv/bin:/Library/Frameworks/Python.framework/Versions/3.6/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/Library/Frameworks/Python.framework/Versions/3.6/bin
退出nano。现在我们只需要设置真正的cron job:
配置 Cron 任务
现在我们需要设置cron任务了。同样使用nano拷贝粘贴方式来完成。不过稍微有些许变化。
首先是* * * * *
部分是必须的,它意味这“每分钟执行一次”。下一部分是Masonite应用程序的位置。
接下来部分取决于你应用程序安装方式。如果是虚拟环境的话还需要追加&& source venv/bin/activate
到cron任务中来激活环境。如果没有使用虚拟环境的话你直接可以忽略它。
最后我们需要通过&& craft schedule:run
执行调度器命令。
真棒!现在我们已经有了cron job来每分钟执行craft命令。Masonite会觉得哪些类需要被执行。
本译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。