问题概述

DRF服务器向Django应用服务器发送Webhook POST请求,
在Django中,在创建Post模型实例的过程中出现了以下问题。

  1. Webhook响应超时
  2. 在创建Post对象后,处理ManyToMany字段categoriestags时耗时较长
  3. 因此Webhook响应延迟,DRF将其视为失败

  4. 数据一致性未得到保证

  5. 在后续处理时,如果立即调用Celery任务,tags.add(...)categories.add(...)的工作在未完成时被调用
  6. 因此,Celery将处理不完整的数据,产生了问题

  7. 即使在on_commit()之后,Celery仍然接收到空数据

  8. 在使用on_commit()确保数据一致性之后,虽然安排了Celery任务,但在任务内部tags.all()的查询结果仍然为空列表
  9. 这是因为Celery将读取操作发送到副本数据库,而主节点和副本节点之间的同步存在延迟

解决策略

  1. 立即返回响应,紧接Post创建
  2. 仅创建Post实例,并立即向DRF返回202 Accepted响应,从而避免超时问题

  3. 在独立线程中处理后续工作

  4. 利用threading.Thread()将关系处理和Celery调用与主流程分离
  5. 设计以避免Webhook响应变慢

  6. 在post_process内部保证事务

  7. 使用transaction.atomic()ManyToMany字段处理的整个过程封装在一个事务中
  8. 在工作完成后使用transaction.on_commit()安排Celery任务
  9. 由此确保在关系处理完成后Celery能够执行

  10. 在Celery内部直接指定主数据库进行查询

  11. 为了防止因副本延迟造成的一致性问题,在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请求总是路由到主数据库进行查询


最终结构总结

  1. DRF服务器 → Django Webhook请求
  2. Django:
    • 在创建Post对象后立即返回响应
    • 后续工作在独立线程中执行
  3. 线程内部:
    • 使用atomic()进行关系整理
    • on_commit()安排Celery任务
  4. Celery:
    • 在主数据库(default)中查询数据

结论

所有组件在个别情况下都是正常的,但是,
为了应对分散架构环境中出现的延迟和数据一致性问题,
需要考虑不仅仅是代码结构,

还需要考虑数据流动与时机、数据库副本延迟的系统设计