Асинхронное обслуживание файлов с помощью Django и 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:
Как отметил @fmgoncalves, один из способов - изменить способ обслуживания файлов, но я нашел немного более надежный патч, который я внедрил в мою настройку Django Channels Setup, основываясь на информации, предоставленной в их посте.
Похоже, что изменение
thread_sensitive=Falseпо умолчанию наTrueвызывает эти сообщения об обнаружении тупика. Насколько я могу судить, Django Channels, похоже, не возражает против возникновения тупиков.Учитывая это, я посчитал, что будет безопасно поставить обезьяний патч на функцию
sync_to_asyncдля моей установки Django.
project_app/moneky_patches.pyfrom 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)Затем вы просто перезаписываете существующий экземпляр метода asgiref's sync_to_async нашей исправленной оберткой, которая обеспечивает чувствительность к потокам=False
.
project_app/__init__.pyfrom . import monkey_patches import asgiref ### Monkey Patches asgiref.sync.sync_to_async = monkey_patches.patch_sync_to_asyncЭто заставит каналы и весь Django работать нечувствительным образом, как это было до обновления ASGIRef.