我尝试过各大网站转载的“放在 urls 文件里执行”的办法,但是很不幸,我发现会执行多次。
具体业务就是设计了一个 Redis Set 队列控制并发,任务正常运行的时候是可以加入、弹出的,但是如果任务在运行过程中发生死机,或者其它未知的 Django 整体崩溃,虽然概率很小,但是一旦发生这个队列就会产生脏数据,我现在是考虑启动 Django 的时候,自动清空这个队列,但是就要求这个函数仅运行一次,如果实现不了,我就只能考虑其他办法。
请问有什么其他方法能让我的业务代码在启动的时候就运行一次么?

通过各个方案对比最终结论如下:

在 apps 中定义 def ready() 因为多线程的问题,不能保证执行一次,且各个线程执行的时间十分不固定
在 urls 中定义与上述问题一致,且实现不优雅
在中间件中定义看起来就很复杂,而且我觉得会影响性能,也没必要
自定义启动命令,绝对保证只执行一次,可维护性较好(胜出)

操作如下:
1 . 新建一个文件,例如:yourapp/management/commands/startup.py
2 . 在文件中定义启动程序
from django.core.management.base import BaseCommand, CommandError

class Command(BaseCommand):

def handle(self, args, *kwargs):
try:
pass # your code
except Exception as e:
raise CommandError('Initialization failed: {}'.format(e))

3 . 在外部通过 python3 manage.py startup 执行
参见: pythonin1minute.com/where-to-put-django-startup-code/
希望能帮助到检索到这个帖子的伙伴。
关键词 Django 首次启动 运行 代码 一次

Django 启动执行任务吗?中间件了解一下

  1. 判断是否“产生脏数据”
  2. 再重置数据

逻辑应该是这样的,而不是次数问题

放在 url 里确实执行一次,但是是一个进程执行一次。用 uwsgi 或者 gunicorn 启动多个进程会发生这个问题。
从只执行一次的角度,需要别的进程来做,比如 celery 、job 平台等。
但我感觉你说的业务场景,可以适当修改下更好,比如执行队列数据时,发现历史脏数据就清除掉

manage.py 不是有 main 函数吗?

redis 本身不就可以做嘛
另外这个很奇怪啊,如果要清空队列那不是任务就没了,那我崩溃了几次就清除几次和一次启动清除了几次有啥区别,脏数据嘛用其他方式解决,保证重复任务也没问题这样子?

判断是否有脏数据就要查表,我可以接受启动的时候查询一次是否有脏数据,但是我不能隔段时间就轮询一次数据库,所以还是要解决如何让它只启动一次的问题。

崩溃其实是极端条件,我外面全部 try 包来了,可以保证任务是否成功都能安全退出队列,另外请问 redis 本身怎么做?我任务队列要持久化的,对应的 Key 肯定不能设置超时

我尝试过各大网站转载的“放在 urls 文件里执行”的办法,但是很不幸,我发现会执行多次。

。。。这种做法完全就是傻逼的做法,把业务代码扔到 router ,无论代码能不能跑都是不对的


django 是以 app 为基本单位划分业务逻辑,
你需要执行的代码,放在 apps.py 里面做执行
具体搜下 django app run code once 之类的关键词

找到的结果大概这样
pythonin1minute.com/where-to-put-django-startup-code/

感谢回复,你这个方法是最稳的,但是这块儿代码我想让它 15 秒轮询一次,因为除非是极端条件,否则根本不会出现这样的事情,所以想尽量不查表完成,当然也不是必须,如果我没找到好的方法就按常规的解决。

感谢回复,我找找

写个 django 从 command ,在 crontab 里定时调用。

重写 get_asig_application 或者 get_wsgi_application 方法,在 django.setup 后执行校验逻辑,另外可以看下 django 的 system check 系统,应该有相应的 hook 。

wsgi sever 通常都会提供 hook 功能
docs.gunicorn.org/en/stable/settings.html#server-hooks

面向可维护的开发吧,得考虑后来接手人的感受。

放在 wsgi 然后 gunicorn 启动的时候指定 --preload

通过 Django 的中间件,用 APScheduler 启动一个定时任务

gist.github.com/zengxs/f3d0d1a894587af929fed835d4d78bbd  显示 Gist 代码 

参考 docs.djangoproject.com/zh-hans/3.2/ref/applications/#django.apps.AppConfig.ready 这个

没有运维知识的纯程序员往往会把什么乱七八糟的需求都用项目的主编程语言写到主程序里面去。其实呢,你的程序在生产环境运行时,可以外面包一个 sh 脚本来启动,把清理数据的操作单独写一个几行的 py 在 django 之前运行不就可以了嘛。

def ready

感谢各位回复,我来想一个优雅的方法解决这个问题

ready

celery ?

可以参考下 migrate 的逻辑

方法挺多的, 简单点在 settings.py 中做, 标准点就是楼上说的 ready, 可以翻一下你自己项目下面的 wsgi.py ,django.setup(), apps.populate(settings.INSTALLED_APPS)的源码. 楼上说多进程启动的问题, 在做这部分操作的时候,用 redis 做个分布式锁就好了

写 command, 用 cli 调用

直接写在 wsgi.py 文件里就行啊
def xxx():
pass

xxx()