由于业务需要,后台要有一个定时任务的功能,起初考虑单独出来使用Linux系统的corn来实现。但是考虑到这样会很不方便。于是便寻找定时任务的模块,就找到了APScheduler,考虑到要在Django中使用,后来就采用了django-apscheduler来作为定时任务的模块,但是这个模块本身有bug。当你使用uwsgi部署并开启多进程的时候,该模块的内置使用get方法来获取任务列表,然后就会报错。因为同一时间有了多个任务,get方法获取到多个任务的时候就会抛出异常。
Django定时任务不要使用django-apscheduler模块,直接使用APScheduler模块即可。
使用APScheduler
现在,我们避免了django-apscheduler模块抛出异常问题,但是我们还有一个问题等待解决,那就是uWsgi使用多进程模式启动Django项目,因此我们会有多个进程去执行这个定时任务,导致定时任务被重复执行。解决这个问题的方法,我们直接就会想到采用加锁的方式。第一个拿到锁的进程,执行定时任务,其余的进程由于拿不到锁,因此也就不会执行定时任务。下面给出两种加锁方案,分别适用于不同的场合。
Redis分布式锁
redis中放置锁,是可以解决分布式下的问题。当然,如果你没有使用分布式,也是可以使用redis锁的。
def testaps():
cache=get_redis_connection("default")
key = f"APS_Lock"
lock = cache.set(key, value=1, nx=True, ex=180)
if lock:
scheduler = BackgroundScheduler()
def my_job():
print (time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())))
scheduler.add_job(my_job, 'interval', seconds=10)
scheduler.start()
else:
...
testaps()
当多个进程都执行testaps方法的时候,只有第一个拿到redis锁(这是个原子操作)的进程才能开启定时任务,其他的进程都无法开启定时任务,这样就可以保证定时任务只被执行一次。
文件锁
在不是分布式的场景下(或者没有redis这种工具的场景下),使用文件锁也能达到相同的效果。
import atexit
import fcntl
def init():
f = open("scheduler.lock", "wb")
try:
fcntl.flock(f, fcntl.LOCK_EX | fcntl.LOCK_NB)
scheduler = BackgroundScheduler()
def my_job():
print (time.strftime('%Y-%m-%d %H:%M:%S', time.localtime(time.time())))
scheduler.add_job(my_job, 'interval', seconds=10)
scheduler.start()
except:
pass
def unlock():
fcntl.flock(f, fcntl.LOCK_UN)
f.close()
atexit.register(unlock)
init()
socket锁
参考资料
Original: https://blog.csdn.net/zy010101/article/details/120860642
Author: zy010101
Title: Django使用django-apscheduler的问题
原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/736185/
转载文章受原作者版权保护。转载请注明原作者出处!