使用 Django 和 uvicorn 异步提供文件
Serving file asyncronously with Django and uvicorn
我有一个提供文件内容的 Django 视图。直到最近,Django 应用程序还是 运行 WSGI。这很好用。然后我调整我的应用程序以使用 ASGI 运行 uvicorn。文件服务现在已中断,因为它似乎失去了连接。
如何使用 Django 和 uvicorn 异步提供文件?
当前视图:
class FileServeView(View):
def get(self, request, *args, **kwargs):
# ...
return HttpResponse(
FileWrapper(file_content), content_type="application/octet-stream"
)
服务器抛出以下错误:
ERROR: Exception in ASGI application
Traceback (most recent call last):
File "/usr/local/lib/python3.8/site-packages/uvicorn/protocols/http/h11_impl.py", line 377, in run_asgi
result = await app(self.scope, self.receive, self.send)
File "/usr/local/lib/python3.8/site-packages/uvicorn/middleware/proxy_headers.py", line 75, in __call__
return await self.app(scope, receive, send)
File "/usr/local/lib/python3.8/site-packages/uvicorn/middleware/asgi2.py", line 17, in __call__
await instance(receive, send)
File "/usr/local/lib/python3.8/site-packages/channels/http.py", line 192, in __call__
await self.handle(body_stream)
File "/usr/local/lib/python3.8/site-packages/asgiref/sync.py", line 382, in __call__
raise RuntimeError(
RuntimeError: Single thread executor already being used, would deadlock
我正在使用:
Django 3.2.13
uvicorn 0.17.1
channels 2.3.1
asgiref 3.5.0
我无法用你的例子重现,但这个问题似乎与你的问题有关:channels#1722。
勾选 dr-luk response:
As @fmgoncalves has mentioned, one way is to alter how the files are served, but I have found a little more reliable patch that I have implemented in my Django Channels Setup based on the information provided by their post.
It seems that the change of thread_sensitive=False
to default to True
is causing these deadlock detection messages. As far as I can see, Django Channels doesn't seem to mind the occurrences of the deadlocks.
That being said, I felt it would be safe to monkey patch the sync_to_async
function for my Django installation.
project_app/moneky_patches.py
from asgiref.sync import sync_to_async
def patch_sync_to_async(*args, **kwargs):
"""
Monkey Patch the sync_to_async decorator
---------------------------------------
ASGIRef made a change in their defaults that has caused major problems
for channels. The decorator below needs to be updated to use
thread_sensitive=False, thats why we are patching it on our side for now.
https://github.com/django/channels/blob/main/channels/http.py#L220
"""
kwargs['thread_sensitive'] = False
return sync_to_async(*args, **kwargs)
Then you just overwrite the existing instance of asgiref's sync_to_async method with our patched wrapper that enforces thread_sensitive=False
project_app/__init__.py
from . import monkey_patches
import asgiref
### Monkey Patches
asgiref.sync.sync_to_async = monkey_patches.patch_sync_to_async
This is make channels, and all of Django run in a insensitive manner like it did before the ASGIRef update.
我有一个提供文件内容的 Django 视图。直到最近,Django 应用程序还是 运行 WSGI。这很好用。然后我调整我的应用程序以使用 ASGI 运行 uvicorn。文件服务现在已中断,因为它似乎失去了连接。
如何使用 Django 和 uvicorn 异步提供文件?
当前视图:
class FileServeView(View):
def get(self, request, *args, **kwargs):
# ...
return HttpResponse(
FileWrapper(file_content), content_type="application/octet-stream"
)
服务器抛出以下错误:
ERROR: Exception in ASGI application
Traceback (most recent call last):
File "/usr/local/lib/python3.8/site-packages/uvicorn/protocols/http/h11_impl.py", line 377, in run_asgi
result = await app(self.scope, self.receive, self.send)
File "/usr/local/lib/python3.8/site-packages/uvicorn/middleware/proxy_headers.py", line 75, in __call__
return await self.app(scope, receive, send)
File "/usr/local/lib/python3.8/site-packages/uvicorn/middleware/asgi2.py", line 17, in __call__
await instance(receive, send)
File "/usr/local/lib/python3.8/site-packages/channels/http.py", line 192, in __call__
await self.handle(body_stream)
File "/usr/local/lib/python3.8/site-packages/asgiref/sync.py", line 382, in __call__
raise RuntimeError(
RuntimeError: Single thread executor already being used, would deadlock
我正在使用:
Django 3.2.13
uvicorn 0.17.1
channels 2.3.1
asgiref 3.5.0
我无法用你的例子重现,但这个问题似乎与你的问题有关:channels#1722。
勾选 dr-luk response:
As @fmgoncalves has mentioned, one way is to alter how the files are served, but I have found a little more reliable patch that I have implemented in my Django Channels Setup based on the information provided by their post.
It seems that the change of
thread_sensitive=False
to default toTrue
is causing these deadlock detection messages. As far as I can see, Django Channels doesn't seem to mind the occurrences of the deadlocks.That being said, I felt it would be safe to monkey patch the
sync_to_async
function for my Django installation.
project_app/moneky_patches.py
from asgiref.sync import sync_to_async def patch_sync_to_async(*args, **kwargs): """ Monkey Patch the sync_to_async decorator --------------------------------------- ASGIRef made a change in their defaults that has caused major problems for channels. The decorator below needs to be updated to use thread_sensitive=False, thats why we are patching it on our side for now. https://github.com/django/channels/blob/main/channels/http.py#L220 """ kwargs['thread_sensitive'] = False return sync_to_async(*args, **kwargs)
Then you just overwrite the existing instance of asgiref's sync_to_async method with our patched wrapper that enforces thread_sensitive=False
project_app/__init__.py
from . import monkey_patches import asgiref ### Monkey Patches asgiref.sync.sync_to_async = monkey_patches.patch_sync_to_async
This is make channels, and all of Django run in a insensitive manner like it did before the ASGIRef update.