Асинхронное обслуживание файлов с помощью 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.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)

Затем вы просто перезаписываете существующий экземпляр метода asgiref's sync_to_async нашей исправленной оберткой, которая обеспечивает чувствительность к потокам=False

.

project_app/__init__.py

from . import monkey_patches
import asgiref

### Monkey Patches
asgiref.sync.sync_to_async = monkey_patches.patch_sync_to_async

Это заставит каналы и весь Django работать нечувствительным образом, как это было до обновления ASGIRef.

Вернуться на верх