Django基于StreamingHttpResponse封装RTSP流:WSGI正常ASGI下持续加载问题咨询
ASGI与WSGI下Django多部分图片流的运行差异与问题根因
核心运行机制差异
两者的底层运行逻辑区别是出现异常的核心原因:
- WSGI采用同步阻塞的请求处理模型,每个摄像头流请求会独占独立的工作线程/进程。
StreamingHttpResponse的无限循环生成器会持续逐块产出帧,服务器端会主动把内容推送给客户端,哪怕代码全是同步阻塞操作,也不会影响当前流请求的响应输出,因此可以正常运行。 - ASGI采用单线程事件驱动的异步处理模型,默认通过事件循环调度所有请求的IO操作。如果代码没有适配异步模型,出现阻塞逻辑就会直接卡死整个事件循环,导致生成器产出的内容无法被推送到客户端,最终表现为浏览器页面一直处于加载状态。
当前实现的具体异常点
- 同步阻塞操作堵死ASGI事件循环
实现中get_frame里的RTSP取流、图像resize、JPEG编码都是CPU密集+IO阻塞的同步操作,这类代码如果直接在ASGI的主事件循环里运行,会完全占满事件循环的调度资源,根本没有空闲时机把生成器产出的帧推送给客户端。 - ASGI兼容层的流式响应适配问题
部分版本的Django ASGI兼容层,对同步生成器形式的StreamingHttpResponse存在缓冲逻辑,会默认等生成器执行完成再统一返回响应内容。由于你的生成器是while True的无限循环,永远不会执行完成,自然就会一直卡在加载状态。 - 代码缩进导致的类定义错误
参考实现中LiveWebCam类的__del__、get_frame方法没有正确缩进,实际运行时这两个方法不属于类的成员,会直接触发属性调用错误,也是ASGI环境下触发异常的隐藏bug。
注:项目本身的asgi.py路由配置没有错误,无需调整,异常根源全部来自视图层的流式响应实现未适配ASGI的异步运行模型。
适配ASGI环境的调整方案
- 把所有同步阻塞的OpenCV取帧操作,通过
asgiref.sync.sync_to_async丢到独立线程池运行,绝对不能让阻塞代码占住ASGI主事件循环。 - 把原本的同步无限循环生成器替换为异步生成器,每次产出一帧就立刻推送给客户端,同时在循环里加入适当的异步休眠,匹配视频帧率避免CPU被打满。
- 给无限循环加退出判断:监听客户端断开连接的信号,一旦连接中断就立刻退出生成器、释放RTSP流资源,避免出现僵尸连接占满服务器资源的问题。
- 确保运行ASGI的服务器(Daphne/Uvicorn)开启逐块传输配置,不要对整个响应做统一缓冲。
适配后的核心代码片段参考:
from asgiref.sync import sync_to_async import asyncio import cv2 class LiveWebCam(object): def __init__(self): self.url = cv2.VideoCapture("<RTSP link here>") def __del__(self): cv2.destroyAllWindows() def get_frame(self): success,imgNp = self.url.read() resize = cv2.resize(imgNp, (640, 480), interpolation = cv2.INTER_LINEAR) ret, jpeg = cv2.imencode('.jpg', resize) return jpeg.tobytes() async def async_gen(camera): try: while True: # 同步取帧逻辑丢线程池,不堵事件循环 frame = await sync_to_async(camera.get_frame)() yield (b'--frame\r\n' b'Content-Type: image/jpeg\r\n\r\n' + frame + b'\r\n\r\n') # 按30fps的帧率做异步休眠,避免占满CPU await asyncio.sleep(1/30) except asyncio.CancelledError: # 客户端断开时自动清理资源 del camera async def livecam_feed(request): return StreamingHttpResponse(async_gen(LiveWebCam()), content_type='multipart/x-mixed-replace; boundary=frame')
内容的提问来源于stack exchange,提问作者Keyur Tailor
相关产品推荐
相关产品推荐

