我正在尝试使用 Django Channels 为 React 前端 Web 应用程序和 Django REST 后端实现长轮询。我相信我所拥有的大部分内容在某种程度上都有效,但某些东西必须错误配置或编码才能产生意想不到的结果。
UPDATE:看来问题出在
axios.get(...)
请求上。当用 fetch(...)
替换该请求时,该 fetch 每次都会收到一个响应,而 Axios 调用平均每隔一次都会收到一个响应。目前不确定解决方案。
简而言之,我收到的问题是,当 Django Channels Consumer 发回响应时,它不会立即返回到前端(按照代码中的console.log(...)
);相反,消费者似乎必须发送另一个响应,然后前一个或两个响应都会出现在前端。
我正在尝试使用AsyncHttpConsumer
来实现,因为 Websockets 在我们的用例中是不可能的。
Asgi.pyos.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)]
),
}
)
路由.py
longpoll_urlpatterns = [
# Tried using re_path but did not work
path("analysisSubscription/<int:analysisId>/", consumers.AnalysisConsumer.as_asgi()),
]
消费者.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}"
# Register with the appropriate channel
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"),])
#The server won't send the headers unless we start sending the body
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")
# If something goes wrong, disconnect
# In the parent this ALWAYS disconnects and thus long-polling breaks
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("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
然后,在更新 Analysis 期间,在 Views.py 文件中,我调用以下行与 Consumer 组进行通信,并调用之前定义的函数
analysis_update
。
async_to_sync(layers.group_send)(f"analysis_{idAnalysis}", {"type": "analysis.update", "id": idAnalysis})
前端 ReactJS 代码基本上通过执行以下操作并检查
response
来循环。
await axios.get(ApiUrl.analysisSubscribe(analysisId), {
timeout: 60000,
})