用 before_app_first_request 的钩子,希望 app 首次启动时,开始运行一个每隔 1 小时定时查询最新数据的任务。结果发现这个任务每隔 1 小时去查询最新的数据,通过 flask_sqlalchemy 初始化之后的工厂模式,查询到的数据都是一样的。如下:
def async_cron_task(app):
with app.app_context():
while True:
new_user = User.query.order_by(User.reg_time.desc()).first()
print('New User: ', new_user.id)
time.sleep(3600)
def cron_task():
app = current_app._get_current_object()
thr = Thread(target=async_cron_task, args=[app])
thr.start()
return thr
但是,如果把数据库写成硬连接,就可以查到最新数据:
db = MySQLdb.connect(host='localhost', port=3306, user='xxx', passwd='xxx', db='xxx', charset='utf8')
cursor = db.cursor()
由于原生 SQL 查询语句太复杂了,希望用 SQLAlchemy 的方式连接数据库,要怎么样才能定时查到新数据呢?
1
cz5424 2021-07-11 11:36:46 +08:00 1
Flask 不适合做定时,定时任务可以选择系统 crontab 执行脚本或者 celery
|
2
wellsc 2021-07-11 11:36:55 +08:00 via iPhone
我没看懂,写个异步任务脚本?
|
3
BingoXuan 2021-07-11 11:37:48 +08:00 via Android
轻量就用 huey 做异步队列
|
4
miniyao OP |
5
miniyao OP @BingoXuan 就是不想用队列工具,增加一层不确定性。毕竟 app 级 while 很靠谱呀
|
6
cz5424 2021-07-11 11:49:53 +08:00 1
celery 并不是玩具,只是你不熟悉,实际上给的这些看不出你查询每次都一样的问题在哪里,可以尝试一下调换这两个的顺序
```python with app.app_context(): while True: ``` |
7
miniyao OP @cz5424 每次查询的数据都一样的意思是,就是无论系统有没有新赠的 new_user.id ,这里面 print 的 id 都不会变。感谢!上下调换了一下顺序,数据就可以更新了,应该是上下文没有切换。
|
8
MintZX 2021-07-11 14:04:34 +08:00
对啊,你这个明显是在同样的 context 里面空转啊。。。可不是每次获得的数据一样
|
9
knightdf 2021-07-11 15:23:27 +08:00
@miniyao celery 用过,过了几个星期自动假死,不敢用了,没有 app 级的 while 可靠。
自己写的有问题吧?我们线上产品都用了 celery,毛事没有 |
10
liuxingdeyu 2021-07-11 15:41:12 +08:00
我的思路是,定时任务、api 、各种 handler 自己干自己的,然后从启动命令里区分出来。如果需要通信就搞个消息通道。之前我们的项目就是一大坨面条似的东西,又弱又乱,后来我拆分出来 dao 层,把各种工具类还有枚举统一放,把 api 和定时任务拆开,各干各的。这样扩容也方便,自己查自己改也方便,容器化之后还能搞一堆镜像各用各的。flask 做定时任务除了 celery 我记得还有个 apscheduler,不过这东西贼坑,只能用线程( mokey_pach 后会切换失败)
|
11
ytmsdy 2021-07-11 15:43:16 +08:00
用 crontab,或者 celery 吧。
自己写太费劲了 |
12
yingxiangyu 2021-07-11 15:43:45 +08:00
celery+apscheduler
|
13
yingxiangyu 2021-07-11 15:44:53 +08:00
celery 定时任务不太好使,定时可以用 apscheduler 实现,触发任务 celery 异步执行
|
14
ila 2021-07-11 18:50:30 +08:00 via Android
用 mysql 的 event schedule
|
15
Hardrain 2021-07-11 20:47:13 +08:00 via Android
要定时执行的单独一个文件,建一个 oneshot 的 systemd service 配合 timer 就好了
cron 过时了 |
17
hunk 2021-07-11 22:21:43 +08:00
Flask+ apscheduler
|
18
ch2 2021-07-11 23:15:59 +08:00
apscheduler 插件
|
19
triptipstop 2021-07-11 23:55:42 +08:00
crontab 怎么会多写代码?
http 接口写好,定时 get 一下就行。 |
20
kkx 2021-07-12 05:24:03 +08:00
crontab 需要多写很多代码说明你代码写的耦合度高。。。
|
21
fhy1994 2021-07-12 08:14:52 +08:00
apscheduler
|
22
fansfans 2021-07-12 08:57:45 +08:00
我前几天刚好也遇到了类似的问题在接口中提交数据但是在线程池中的任务没有获取到对应的数据 应该是 sqlalchemy 的缓存问题 后面直接用 celery 了 调式控制都方便很多。
|
23
frostming 2021-07-12 09:01:53 +08:00
|
24
wobuhuicode 2021-07-12 09:17:43 +08:00
定时任务用 crontab ~
|
25
RRRoger 2021-07-12 09:29:18 +08:00
可以试试改成自己调自己的接口
|
26
amoyiki 2021-07-12 09:39:52 +08:00
目前项目使用很多年 celery, 出现过屈指可数的程序问题。一般就是 redis 获取连接池失败。
不使用 celery 的话可以自己创建一个接口,外部 1h 调用一次。外部直接用 crontab 写定时 |
27
l4ever 2021-07-12 09:40:23 +08:00
用 flask-apscheduler
|
28
www5070504 2021-07-12 09:52:45 +08:00
丢到 apscheduler 里
|
29
wzwwzw 2021-07-12 09:54:29 +08:00
apscheduler flask 有插件
|
30
Vegetable 2021-07-12 10:33:49 +08:00
对自己好一点,apscheduler 吧
|
31
676529483 2021-07-12 10:36:04 +08:00
1. 你这个就是数据库连接处理有问题额,问题在 with app.app_context():这行,你应该想要使用 sqlalchemy 的连接初始化,要手动调用获取数据库连接
2. 我以前也这样自己处理,发现各种调度时间问题。现在更倾向于另启动一个 cron 服务,定时去调用 api 接口 |
32
nonduality 2021-07-12 10:40:17 +08:00
针对你的需求,要获取最新数据,用相关事件信号触发动作不是更直截了当还稳定可靠?
|
33
cz5424 2021-07-12 11:20:51 +08:00
看了一下的评论,猜测是事务的问题,同一个上下文里面会一直处于同一个事务,可重复读会一直读到相同数据,另一个方法是可以修改 sqlalchemy 的事务隔离级别来避免,不需要调换上下文和 while (代价是其他读写提交也会生效,慎重修改)
|
34
HUNYXV 2021-07-12 12:38:11 +08:00
```
pip install apscheduler ``` |
35
jianhua 2021-07-12 14:47:14 +08:00
搜索:flask + celery
|
38
myCupOfTea 2021-07-13 08:37:07 +08:00
apscheduler 有的时候会忽略任务,比如当前系统比较繁忙正好到了定时任务的时候
|
40
yagamil 2021-07-15 08:35:20 +08:00
看着像脏读的问题. 每次用 sql 连接语句重连就不出现这个问题
|
41
fansfans 2021-07-15 09:19:05 +08:00
其实这个和 celery 、apscheduler 好像没什么关联、应该是 session 的上下文使用的是同一个、导致的脏读、但是在接口中进行了 commit 不应该出现这种情况才对、也许可以通过自己创建 session 解决 db_session = sessionmaker(bind=db.engine)()
|
42
nicolaz 2021-07-15 17:38:02 +08:00
```python
def set_interval(interval): def decorator(function): def wrapper(*args, **kwargs): stopped = threading.Event() def loop(): # executed in another thread while not stopped.wait(interval): # until stopped function(*args, **kwargs) t = threading.Thread(target=loop) t.daemon = True # stop if the program exits t.start() return stopped return wrapper return decorator @set_interval(60*60) def do_something(): pass ``` |
43
seven123 2021-07-29 09:35:44 +08:00
celery 的确有时候会出现一些诡异问题,直接用 apscheduler 把
|