You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Django Rest Framework中处理并行请求避免重复数据?

解决DRF中同一IP并行请求导致的重复数据问题

针对你遇到的同一IP并行提交反馈导致重复数据的问题,之前用BooleanField的方案失效是因为并行请求存在竞态条件——多个请求同时读取到can_submit_feedback=True,然后都执行了保存操作。既然你倾向于在请求层面处理,下面提供几种可行的实现方式:

方法一:基于缓存的请求互斥锁

利用Django的缓存框架(生产环境推荐用Redis作为后端,性能更好),给每个请求的IP地址+API端点生成唯一锁键,请求到达时先尝试获取锁,成功则处理请求,失败则直接返回"请求处理中"的提示,避免并行执行。

实现步骤:

  1. 确保你的Django配置了分布式缓存(比如Redis),默认的内存缓存在多进程部署时会失效,无法实现跨进程的锁同步。
  2. 编写自定义装饰器,用于视图函数/类视图:
from django.core.cache import cache
from rest_framework.response import Response
from rest_framework import status
import hashlib

def prevent_parallel_requests(view_func):
    def _wrapped_view(request, *args, **kwargs):
        # 生成唯一锁键:IP地址 + 请求路径,确保同一IP同一端点的请求共享锁
        ip_address = request.META.get('REMOTE_ADDR', 'unknown')
        endpoint = request.path
        lock_key = hashlib.md5(f"{ip_address}:{endpoint}".encode()).hexdigest()
        
        # cache.add是原子操作:只有键不存在时才会添加成功,避免竞态
        if cache.add(lock_key, 'locked', timeout=5):
            try:
                # 执行原视图的业务逻辑
                return view_func(request, *args, **kwargs)
            finally:
                # 请求处理完成后主动释放锁
                cache.delete(lock_key)
        else:
            # 已有并行请求在处理,返回提示
            return Response(
                {"detail": "请求正在处理中,请稍后再试"},
                status=status.HTTP_429_TOO_MANY_REQUESTS
            )
    return _wrapped_view
  1. 在你的API视图上应用装饰器:
from rest_framework.views import APIView

class FeedbackSubmitView(APIView):
    @prevent_parallel_requests
    def post(self, request):
        # 你的反馈保存逻辑
        user_id = request.data.get('user_id')
        message = request.data.get('message')
        student = Student.objects.get(id=user_id)
        Feedback.objects.create(user=student, message=message)
        return Response({"detail": "反馈提交成功"}, status=status.HTTP_201_CREATED)

这种方法的核心是cache.add()的原子性——多个并行请求同时调用add时,只有第一个能成功添加锁键,其他都会失败,完美规避竞态条件。

方法二:自定义中间件全局处理

如果多个API端点都需要防并行处理,可以写一个中间件,对指定端点做统一拦截:

from django.core.cache import cache
from rest_framework.response import Response
from rest_framework import status
import hashlib

class ParallelRequestBlockerMiddleware:
    def __init__(self, get_response):
        self.get_response = get_response
        # 定义需要拦截的端点列表
        self.protected_endpoints = ['/api/submit-feedback/']

    def __call__(self, request):
        # 只处理POST请求和指定端点
        if request.method == 'POST' and request.path in self.protected_endpoints:
            ip_address = request.META.get('REMOTE_ADDR', 'unknown')
            lock_key = hashlib.md5(f"{ip_address}:{request.path}".encode()).hexdigest()
            
            if not cache.add(lock_key, 'locked', timeout=5):
                return Response(
                    {"detail": "请求正在处理中,请稍后再试"},
                    status=status.HTTP_429_TOO_MANY_REQUESTS
                )
            
            try:
                response = self.get_response(request)
            finally:
                cache.delete(lock_key)
            return response
        
        return self.get_response(request)

然后把这个中间件添加到settings.py的MIDDLEWARE列表中即可。

注意事项

  • 缓存后端选择:生产环境不要用默认的LocMemCache,多进程/多服务器部署时缓存不共享,锁会失效,推荐用Redis或Memcached。
  • 锁超时时间:要根据API的实际处理时间合理设置,太短可能导致正常请求未完成锁就释放,太长可能导致异常情况下锁长期存在。
  • IP局限性:如果多个用户共享同一IP(比如公司网络、公共WiFi),这种方法会误拦截不同用户的请求。若API支持用户认证,可以把用户ID+IP作为锁键,实现更精准的互斥。

内容的提问来源于stack exchange,提问作者SHIVAM JINDAL

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 06:52:56