我尝试过各大网站转载的“放在 urls 文件里执行”的办法,但是很不幸,我发现会执行多次。
具体业务就是设计了一个 Redis Set 队列控制并发,任务正常运行的时候是可以加入、弹出的,但是如果任务在运行过程中发生死机,或者其它未知的 Django 整体崩溃,虽然概率很小,但是一旦发生这个队列就会产生脏数据,我现在是考虑启动 Django 的时候,自动清空这个队列,但是就要求这个函数仅运行一次,如果实现不了,我就只能考虑其他办法。
请问有什么其他方法能让我的业务代码在启动的时候就运行一次么?
通过各个方案对比最终结论如下:
操作如下:
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 执行
参见: https://pythonin1minute.com/where-to-put-django-startup-code/
希望能帮助到检索到这个帖子的伙伴。
关键词 Django 首次启动 运行 代码 一次
1
rationa1cuzz 2021-11-10 17:48:54 +08:00 1
Django 启动执行任务吗?中间件了解一下
|
2
SmiteChow 2021-11-10 17:52:54 +08:00 1
1. 判断是否“产生脏数据”
2. 再重置数据 逻辑应该是这样的,而不是次数问题 |
3
676529483 2021-11-10 17:54:47 +08:00 1
放在 url 里确实执行一次,但是是一个进程执行一次。用 uwsgi 或者 gunicorn 启动多个进程会发生这个问题。
从只执行一次的角度,需要别的进程来做,比如 celery 、job 平台等。 但我感觉你说的业务场景,可以适当修改下更好,比如执行队列数据时,发现历史脏数据就清除掉 |
5
huazhaozhe 2021-11-10 18:02:19 +08:00 via Android 1
redis 本身不就可以做嘛
另外这个很奇怪啊,如果要清空队列那不是任务就没了,那我崩溃了几次就清除几次和一次启动清除了几次有啥区别,脏数据嘛用其他方式解决,保证重复任务也没问题这样子? |
6
Phishion OP @SmiteChow 判断是否有脏数据就要查表,我可以接受启动的时候查询一次是否有脏数据,但是我不能隔段时间就轮询一次数据库,所以还是要解决如何让它只启动一次的问题。
|
7
Phishion OP @huazhaozhe 崩溃其实是极端条件,我外面全部 try 包来了,可以保证任务是否成功都能安全退出队列,另外请问 redis 本身怎么做?我任务队列要持久化的,对应的 Key 肯定不能设置超时
|
8
freakxx 2021-11-10 18:08:09 +08:00 1
我尝试过各大网站转载的“放在 urls 文件里执行”的办法,但是很不幸,我发现会执行多次。
。。。这种做法完全就是傻逼的做法,把业务代码扔到 router ,无论代码能不能跑都是不对的 ----- django 是以 app 为基本单位划分业务逻辑, 你需要执行的代码,放在 apps.py 里面做执行 具体搜下 django app run code once 之类的关键词 找到的结果大概这样 https://pythonin1minute.com/where-to-put-django-startup-code/ |
9
Phishion OP @676529483 感谢回复,你这个方法是最稳的,但是这块儿代码我想让它 15 秒轮询一次,因为除非是极端条件,否则根本不会出现这样的事情,所以想尽量不查表完成,当然也不是必须,如果我没找到好的方法就按常规的解决。
|
11
vicalloy 2021-11-10 18:20:44 +08:00 1
写个 django 从 command ,在 crontab 里定时调用。
|
12
chuanqirenwu 2021-11-10 18:50:24 +08:00 1
重写 get_asig_application 或者 get_wsgi_application 方法,在 django.setup 后执行校验逻辑,另外可以看下 django 的 system check 系统,应该有相应的 hook 。
|
13
tomczhen 2021-11-10 19:40:28 +08:00 via Android 1
wsgi sever 通常都会提供 hook 功能
https://docs.gunicorn.org/en/stable/settings.html#server-hooks |
14
kidblg 2021-11-10 19:41:36 +08:00 1
面向可维护的开发吧,得考虑后来接手人的感受。
|
15
wuwukai007 2021-11-10 19:56:13 +08:00 1
放在 wsgi 然后 gunicorn 启动的时候指定 --preload
|
16
zengxs 2021-11-10 20:31:11 +08:00 1
通过 Django 的中间件,用 APScheduler 启动一个定时任务
https://gist.github.com/zengxs/f3d0d1a894587af929fed835d4d78bbd |
17
GTim 2021-11-10 21:10:31 +08:00 1
|
18
adoal 2021-11-10 21:18:13 +08:00 via iPhone 1
没有运维知识的纯程序员往往会把什么乱七八糟的需求都用项目的主编程语言写到主程序里面去。其实呢,你的程序在生产环境运行时,可以外面包一个 sh 脚本来启动,把清理数据的操作单独写一个几行的 py 在 django 之前运行不就可以了嘛。
|
19
naijoag 2021-11-10 21:44:26 +08:00 1
def ready
|
20
Phishion OP 感谢各位回复,我来想一个优雅的方法解决这个问题
|
21
ericls 2021-11-11 08:08:08 +08:00 via iPhone
ready
|
22
littlezzll 2021-11-11 09:10:24 +08:00 via Android
celery ?
|
23
asmoker 2021-11-11 09:24:49 +08:00 via Android
可以参考下 migrate 的逻辑
|
24
yufpga 2021-11-11 09:43:42 +08:00
方法挺多的, 简单点在 settings.py 中做, 标准点就是楼上说的 ready, 可以翻一下你自己项目下面的 wsgi.py ,django.setup(), apps.populate(settings.INSTALLED_APPS)的源码. 楼上说多进程启动的问题, 在做这部分操作的时候,用 redis 做个分布式锁就好了
|
25
Rebely 2021-11-11 10:51:52 +08:00
写 command, 用 cli 调用
|
26
steptodream 2021-11-11 12:47:57 +08:00
|
27
gaogang 2022-05-29 22:03:55 +08:00
@steptodream
这种方式下,如果用的 gunicorn 多进程模式启动 而且没有开启 preload_app 的话 还是会执行多次的 |