问题概述
DRF服务器向Django应用服务器发送Webhook POST请求,
在Django中,在创建Post模型实例的过程中出现了以下问题。
- Webhook响应超时
- 在创建Post对象后,处理
ManyToMany
字段categories
和tags
时耗时较长 -
因此Webhook响应延迟,DRF将其视为失败
-
数据一致性未得到保证
- 在后续处理时,如果立即调用Celery任务,
tags.add(...)
和categories.add(...)
的工作在未完成时被调用 -
因此,Celery将处理不完整的数据,产生了问题
-
即使在on_commit()之后,Celery仍然接收到空数据
- 在使用
on_commit()
确保数据一致性之后,虽然安排了Celery任务,但在任务内部tags.all()
的查询结果仍然为空列表 - 这是因为Celery将读取操作发送到副本数据库,而主节点和副本节点之间的同步存在延迟
解决策略
- 立即返回响应,紧接Post创建
-
仅创建Post实例,并立即向DRF返回
202 Accepted
响应,从而避免超时问题 -
在独立线程中处理后续工作
- 利用
threading.Thread()
将关系处理和Celery调用与主流程分离 -
设计以避免Webhook响应变慢
-
在post_process内部保证事务
- 使用
transaction.atomic()
将ManyToMany
字段处理的整个过程封装在一个事务中 - 在工作完成后使用
transaction.on_commit()
安排Celery任务 -
由此确保在关系处理完成后Celery能够执行
-
在Celery内部直接指定主数据库进行查询
- 为了防止因副本延迟造成的一致性问题,在Celery任务中明确使用
using('default')
post = Post.objects.using('default').get(id=post_id)
tags = post.tags.using('default').all()
categories = post.categories.using('default').all()
或者利用数据库路由器,将Celery请求总是路由到主数据库进行查询
最终结构总结
- DRF服务器 → Django Webhook请求
- Django:
- 在创建Post对象后立即返回响应
- 后续工作在独立线程中执行
- 线程内部:
- 使用
atomic()
进行关系整理 - 用
on_commit()
安排Celery任务
- 使用
- Celery:
- 在主数据库(
default
)中查询数据
- 在主数据库(
结论
所有组件在个别情况下都是正常的,但是,
为了应对分散架构环境中出现的延迟和数据一致性问题,
需要考虑不仅仅是代码结构,
댓글이 없습니다.