DRF接口幂等操作

CACHES = {

'default': {

'BACKEND': 'django_redis.cache.RedisCache',

'LOCATION': 'redis://127.0.0.1:6379/1',

'OPTIONS': {'CLIENT_CLASS': 'django_redis.client.DefaultClient'}

}

}

幂等键保留时间(秒),建议比你业务最长处理时间久一点,比如7天

IDEMPOTENCY_TIMEOUT = 60 * 60 * 24 * 7

decorators.py

from django.core.cache import cachefrom django.http import JsonResponsefrom rest_framework.response import Responsefrom functools import wrapsimport hashlibimport jsondef idempotent(timeout=86400): """ DRF视图幂等装饰器 使用方式: @idempotent(timeout=3600) 放在 post/put 方法上 """ def decorator(view_func): @wraps(view_func) def wrapper(self, request, *args, **kwargs): # 1. 获取幂等键(优先Header,其次请求体) idem_key = request.headers.get('Idempotency-Key') or request.data.get('idempotency_key') if not idem_key: # 如果没有幂等键,直接执行(不阻塞,但非幂等) return view_func(self, request, *args, **kwargs) # 2. 生成唯一缓存Key(结合用户ID和路径,防止不同用户冲突) user_id = request.user.id if request.user.is_authenticated else 'anonymous' path = request.path cache_key = f"idem:{user_id}:{path}:{idem_key}" # 3. 尝试原子性占位(利用cache.add的原子性) # 设置占位值 "PROCESSING",防止并发请求同时执行业务 if not cache.add(cache_key, "PROCESSING", timeout=timeout): # 键已存在,说明请求正在处理或已处理完成 cached_result = cache.get(cache_key) if cached_result == "PROCESSING": # 并发冲突:另一个相同请求正在执行中 # 方案A:轮询等待(不推荐,占用连接) # 方案B:直接返回409,让客户端稍后重试(推荐) return Response( {"error": "Duplicate request is being processed, please retry later"}, status=409 # Conflict ) else: # 已处理完成,直接返回之前缓存的响应 return Response(cached_result.get('data'), status=cached_result.get('status')) # 4. 成功占用锁,执行业务逻辑 try: response = view_func(self, request, *args, **kwargs) # 判断响应类型,提取数据和状态码 if isinstance(response, Response): resp_data = response.data resp_status = response.status_code else: # 兼容普通的HttpResponse/JsonResponse resp_data = json.loads(response.content) if hasattr(response, 'content') else {} resp_status = response.status_code # 缓存成功结果(覆盖 "PROCESSING") cache.set(cache_key, {"data": resp_data, "status": resp_status}, timeout=timeout) return response except Exception as e: # 业务执行失败,删除缓存键,释放锁(允许客户端重试) cache.delete(cache_key) raise e # 继续抛出异常让DRF处理 return wrapper return decorator

views.py

from rest_framework.views import APIViewfrom .decorators import idempotentclass OrderAPIView(APIView): @idempotent(timeout=3600) # 该订单创建接口1小时内幂等 def post(self, request): # 假设这里会扣库存、创建订单 order = create_order(request.data) return Response({"order_id": order.id, "status": "created"}, status=201)

相关推荐
Είναι η κοπέλα11 分钟前
显存计算与模型选择:你的显卡能跑多大的模型
人工智能·pytorch·python·开源·conda
ROSF686816 分钟前
2026 仿石漆乳液供应商:市场布局与行业发展态势梳理
大数据·人工智能·python
青少儿编程课堂24 分钟前
AC自动机多模式串匹配精讲:校园关键词巡检实战解析
c++·python·算法·bfs·信息学竞赛
hhzz1 小时前
【YOLO 入门到精通 04】理解任务与模型家族:7 大 CV 任务与 YOLO26 选型指南
人工智能·python·深度学习·yolo·计算机视觉
Escalating_xu1 小时前
【Python】使用库:标准库、第三方库、pip、项目实战与程序打包
java·python·pip
水獭比特1 小时前
Agent 工具审批不止一个按钮:用 v0.22.1 复盘入口、恢复和副作用
人工智能·python
苦瓜打怪兽1 小时前
leetcode 热题100(11. 盛最多水的容器)
python·算法·leetcode
砚底藏山河1 小时前
量化实战:回测数据底座快照与版本管理进阶
java·大数据·python·金融·maven
“AI国潮设计-小江”1 小时前
《Python+SDXL实战:用ControlNet批量生成“英歌舞麻将糕”IP,附自动化脚本与商用思路》
开发语言·人工智能·python·prompt·aigc
逗脑IDE3 小时前
零基础学ESP32:RFID无线射频卡——让ESP32拥有“刷卡“能力!
开发语言·网络·人工智能·python·esp32·硬件开发