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)

相关推荐
WiKiLeaks_successor1 小时前
Scipy库里的众数函数不严谨,我把它重构了。
python·scipy
傻啦嘿哟1 小时前
房产数据对比爬虫:同时爬取链家+贝壳+安居客,做房价横向对比
python
小静AI工程实验室1 小时前
JS 逆向接口 ID 变了?Python 与 Node.js 复现 JSON 大整数精度丢失
javascript·python·node.js
李航19831 小时前
用 DeepDraw 几何引擎开发建筑设计软件(五):创建选择工具
python·3d
Metaphor6921 小时前
使用 Python 设置 Excel 行列自适应 【代码示例】
python·excel
花酒锄作田10 小时前
FastAPI 使用 session 认证
python·fastapi
lsswear10 小时前
Python 并发 线程
开发语言·python
Ivanqhz11 小时前
MLIR OpBuilder
开发语言·python·mlir
威联通安全存储12 小时前
TS-h2287XU-RP 在家电制造总装与质检数据场景的部署
python·制造
泡泡鱼(敲代码中)13 小时前
Python 字符串 str 完整学习笔记
python·学习