如何解决Django Channels向React前端返回长轮询响应异常问题?
Django Channels长轮询响应延迟及Axios接收异常问题解决
我正在用Django Channels给React前端+Django REST后端实现长轮询,选用AsyncHttpConsumer(场景不支持WebSocket)。目前核心问题是:Consumer返回的响应无法立即到达前端,必须等Consumer再次发送响应,之前的或两次响应才会一起出现。
更新:发现问题与axios.get(...)请求相关,替换为fetch(...)后每次都能正常接收响应,但Axios平均每隔一次才能收到响应,暂未明确原因。
相关配置与代码
Asgi.py配置
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project.settings') django_asgi_app = get_asgi_application() application = ProtocolTypeRouter( { "http": URLRouter( longpoll_urlpatterns + [re_path(r"", django_asgi_app)] ), } )
Routing.py配置
longpoll_urlpatterns = [ # 尝试过re_path但未生效 path("analysisSubscription/<int:analysisId>/", consumers.AnalysisConsumer.as_asgi()), ]
Consumers.py实现
class AnalysisConsumer(AsyncHttpConsumer): async def handle(self, body): print("In Handle") print(self.scope) self.analysisId = self.scope["url_route"]["kwargs"]["analysisId"] self.analysis_group_name = f"analysis_{self.analysisId}" # 注册到对应频道组 await self.channel_layer.group_add(self.analysis_group_name, self.channel_name) await self.send_headers(headers=[ (b"Content-type", b"application/json"), (b"Access-Control-Allow-Origin", b"*"), (b"Access-Control-Allow-Headers", b"Origin, X-Requested-With, Content-Type, Accept, Authorization"),]) # 必须先发送空body才能触发服务器发送headers await self.send_body(b"", more_body=True) print("Registered consumer for ID: ", self.analysisId, " and group: ", self.analysis_group_name) # await self.channel_layer.group_send(self.analysis_group_name, {"type": "analysis.update", "text": self.analysisId}) async def http_request(self, message): print("In Request") print(message) if "body" in message: self.body.append(message["body"]) if not message.get("more_body"): try: await self.handle(b"".join(self.body)) except: print("Stopping") # 出错则断开连接,父类方法会直接断开导致长轮询失效 await self.disconnect() raise StopConsumer() async def disconnect(self): print("Disconnecting!") await self.channel_layer.group_discard(self.analysis_group_name, self.channel_name) async def analysis_update(self, event): print(event) print("Inside Analysis Consumer") analysisId = event['id'] analysisData = "" try: analysisData = await self.getAnalysis(analysisId) analysisData = json.dumps(analysisData) except Exception as ex: print(f"Failed to retrieve Analysis object: {ex}") return print("Retrieved analysis:\n\t", analysisData) await self.send_body(analysisData.encode('utf-8')) print("Sent the response") await asyncio.sleep(1) await self.http_disconnect(None) @database_sync_to_async def getAnalysis(self, id): return AnalysisSerializer(Analysis.objects.filter(id=id)[0]).data
Views.py触发更新代码
async_to_sync(layers.group_send)(f"analysis_{idAnalysis}", {"type": "analysis.update", "id": idAnalysis})
前端React请求代码
await axios.get(ApiUrl.analysisSubscribe(analysisId), { timeout: 60000, })
问题解决思路
1. 修复Consumer响应延迟问题
- 明确响应结束标记:在
analysis_update中发送响应时,需明确设置more_body=False,告知服务器响应已完成,立即推送给前端:# 替换原send_body调用 await self.send_body(analysisData.encode('utf-8'), more_body=False) - 移除不必要的延迟:删除
await asyncio.sleep(1),发送完响应后直接调用await self.http_disconnect(None),避免无意义等待。 - 修正频道组移除方法:
group_discard已废弃,改用group_remove:async def disconnect(self): print("Disconnecting!") await self.channel_layer.group_remove(self.analysis_group_name, self.channel_name)
2. 修复Axios接收异常问题
- 禁用GET请求缓存:Axios默认缓存GET请求,添加时间戳参数避免缓存:
await axios.get(ApiUrl.analysisSubscribe(analysisId), { timeout: 60000, params: { _: Date.now() // 随机参数禁用缓存 } }) - 明确响应类型:指定
responseType确保Axios正确解析响应:await axios.get(ApiUrl.analysisSubscribe(analysisId), { timeout: 60000, responseType: 'json', params: { _: Date.now() } })
3. 其他优化点
- 初始化请求body:在
http_request开头初始化self.body,避免未定义报错:async def http_request(self, message): print("In Request") print(message) if not hasattr(self, 'body'): self.body = [] if "body" in message: self.body.append(message["body"]) # ... 后续代码 - 缩小异常捕获范围:将
except:改为捕获具体异常,避免误吞错误导致调试困难。
内容的提问来源于stack exchange,提问作者Doug Steiert
相关产品推荐
相关产品推荐

