如何异步运行Django API?解决单用户请求阻塞问题
解决Django长耗时API阻塞同一用户其他请求的方案
问题根源
这种情况的核心原因是Django默认的数据库会话存储机制:当处理用户请求时,Django会对该用户的会话数据库行加排他锁,直到请求处理完成才释放。同一用户的后续请求会因为等待这个锁而被阻塞,不同用户的会话行互不影响,所以其他用户可以正常请求。
以下是无需Celery/Redis异步任务框架的解决办法:
1. 更换会话存储后端
放弃数据库会话,改用缓存类会话存储,这类存储不会对用户会话加排他锁,能支持同一用户的并发请求。
配置示例(使用Memcached缓存)
在settings.py中修改:
# 配置缓存后端 CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.memcached.PyMemcacheCache', 'LOCATION': '127.0.0.1:11211', } } # 切换会话引擎到缓存 SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
如果用Redis作为缓存(仅做会话存储,不用于Celery任务),可以用django-redis包:
# settings.py CACHES = { "default": { "BACKEND": "django_redis.cache.RedisCache", "LOCATION": "redis://127.0.0.1:6379/1", "OPTIONS": { "CLIENT_CLASS": "django_redis.client.DefaultClient", } } } SESSION_ENGINE = "django.contrib.sessions.backends.cache" SESSION_CACHE_ALIAS = "default"
2. 手动释放会话锁(针对数据库会话)
如果不想更换会话后端,可以在长耗时任务执行前手动保存会话以释放锁,避免阻塞后续请求。
代码修改示例
class CreateOrderAPIView(APIView): def post(self, request): # 提前保存会话,释放数据库锁 request.session.save() # 执行长耗时的第三方服务调用 # 比如:response = requests.post("https://third-party-service.com/api", ...) # 如果后续修改了会话,需要再次保存 # request.session['order_id'] = order.id # request.session.save() return Response(order_data, status=201)
注意:如果长耗时任务中需要修改会话,要确保任务结束后重新保存会话,否则会话变更会丢失。
3. 使用异步视图(Django 3.1+)
将视图改为异步模式,用异步IO处理长耗时的第三方调用,避免阻塞请求线程,同一用户的其他请求可以在空闲线程中处理。
代码示例(使用aiohttp异步调用第三方服务)
首先安装aiohttp:
pip install aiohttp
然后修改视图:
import aiohttp from rest_framework.views import APIView from rest_framework.response import Response class CreateOrderAPIView(APIView): async def post(self, request): # 异步调用第三方服务 async with aiohttp.ClientSession() as session: async with session.post("https://third-party-service.com/api", json=request.data) as resp: third_party_data = await resp.json() # 创建订单逻辑 order_data = {"id": 123, "status": "created", **third_party_data} return Response(order_data, status=201)
如果第三方服务没有异步SDK,可以用asyncio.to_thread包装同步调用,避免阻塞事件循环:
import asyncio import requests async def post(self, request): # 用线程池运行同步请求 third_party_response = await asyncio.to_thread( requests.post, "https://third-party-service.com/api", json=request.data ) third_party_data = third_party_response.json() # 创建订单逻辑 return Response(order_data, status=201)
4. 调整会话保存策略
如果你的长耗时请求没有修改用户会话,可以在settings.py中关闭每次请求都保存会话的行为,这样Django不会对会话加锁:
# settings.py SESSION_SAVE_EVERY_REQUEST = False
限制:如果请求过程中修改了会话(比如设置request.session['key'] = value),这个方法无效,因为Django仍会加锁保存会话。
内容的提问来源于stack exchange,提问作者Tanaji Kolekar
相关产品推荐
相关产品推荐

