Будут ли запросы на мой сайт запаздывать и медленно работать в django в ожидании результатов celery?
Я использую django для создания конвертера pdf в docx с использованием библиотеки pdf2docx, и мне нужно дождаться выполнения задачи celery и получить от нее результат. Будет ли мой сайт зависать и работать медленно, если им будет пользоваться много пользователей, и как я могу сделать это лучше? вот мои просмотры и код celery
views.py "'
from django.shortcuts import render
from django.http import JsonResponse, HttpResponse
from django.http.request import HttpRequest
import tempfile
import os
from .forms import FileUploadForm
from django.views.decorators.csrf import csrf_protect
from . import tasks
from celery.result import AsyncResult
@csrf_protect
def main_page(request: HttpRequest):
if request.method == "GET":
# get
form = FileUploadForm(request.POST, request.FILES)
context = {
"form": form
}
return render(request, 'main/main_page.html', context)
if request.method == 'POST' and request.FILES.get('file'):
form = FileUploadForm(request.POST, request.FILES)
if form.is_valid():
# get file
file = request.FILES['file']
size_limit = 2 * 1024 * 1024
# save pdf
with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as temp_file:
if file.size > size_limit:
for chunk in file.chunks():
temp_file.write(chunk)
else:
temp_file.write(file.read())
temp_pdf_path = temp_file.name
# start convertor task
task: AsyncResult = tasks.convert_pdf_to_docx.delay(temp_pdf_path)
# get docx path
temp_docx_path = task.wait(timeout=None, interval=0.5)
converted_file_name = str(file).replace(".pdf", "")
# read docx file and set it in response
with open(temp_docx_path, 'rb') as docx_file:
file_data = docx_file.read()
response = HttpResponse(file_data, content_type='application/vnd.openxmlformats-officedocument.wordprocessingml.document')
response['Content-Disposition'] = f'attachment; filename={converted_file_name}.docx'
response['Content-Length'] = len(file_data) # calculate length of content
# Clean up temp files
os.remove(temp_pdf_path)
os.remove(temp_docx_path)
return response
else:
context = {
"form": form,
"submit_error": True,
}
return render(request, 'main/main_page.html', context)
'''
tasks.py "'
from celery import shared_task
from pdf2docx import Converter
@shared_task()
def convert_pdf_to_docx(temp_pdf_path):
# Convert to DOCX
temp_docx_path = temp_pdf_path.replace(".pdf", ".docx")
cv = Converter(temp_pdf_path)
cv.convert(temp_docx_path)
cv.close()
return temp_docx_path
'''
для запуска celery я использую: '''
celery -A pdfconverter worker --loglevel=info -P gevent
'''
Да, ваш сайт будет зависать и работать медленно. Использование task.wait() блокирует рабочий поток Django, не позволяя ему обрабатывать другие запросы. Когда вы вызываете task.wait(), процесс Django останавливается и ожидает завершения Celery, что противоречит цели использования Celery для фоновых задач. Если у вас ограниченное количество рабочих элементов Django (что типично для развертываний gunicorn/uwsgi), одновременные пользователи будут стоять в очереди в ожидании доступных рабочих элементов, создавая узкое место, где каждое преобразование файлов монополизирует рабочий поток и значительно снижает способность вашего приложения обрабатывать несколько одновременных запросов.
Чтобы решить эту проблему, вы можете разделить процесс на два этапа: загрузку и скачивание файлов.
Шаг 1 - Загрузите и запустите задание:
@csrf_protect
def upload_file(request):
if request.method == 'POST' and request.FILES.get('file'):
form = FileUploadForm(request.POST, request.FILES)
if form.is_valid():
file = request.FILES['file']
# Save file temporarily
with tempfile.NamedTemporaryFile(delete=False, suffix=".pdf") as temp_file:
for chunk in file.chunks():
temp_file.write(chunk)
temp_pdf_path = temp_file.name
# Start task
task = tasks.convert_pdf_to_docx.delay(temp_pdf_path)
# Return task ID to frontend
return JsonResponse({
'task_id': task.id,
'status': 'processing'
})
Шаг 2 - Проверьте статус и загрузите:
def check_task_status(request, task_id):
task = AsyncResult(task_id)
if task.state == 'PENDING':
return JsonResponse({'status': 'processing'})
elif task.state == 'SUCCESS':
docx_path = task.result
with open(docx_path, 'rb') as docx_file:
response = HttpResponse(
docx_file.read(),
content_type='application/vnd.openxmlformats-officedocument.wordprocessingml.document'
)
response['Content-Disposition'] = 'attachment; filename=converted.docx'
# Clean up
os.remove(docx_path)
return response
else:
return JsonResponse({'status': 'error', 'message': str(task.info)})
Интерфейс JavaScript:
// After uploading
function checkStatus(taskId) {
fetch(`/check-status/${taskId}/`)
.then(response => response.json())
.then(data => {
if (data.status === 'processing') {
setTimeout(() => checkStatus(taskId), 2000);
} else if (data.status === 'success') {
window.location.href = `/download/${taskId}/`;
}
});
}
Такой подход позволяет вашим сотрудникам Django свободно обрабатывать другие запросы, в то время как Celery обрабатывает преобразование файлов в фоновом режиме. Интерфейс опрашивает сервер каждые 2 секунды, чтобы проверить, завершено ли преобразование, обеспечивая гораздо лучший пользовательский опыт без блокировки вашего приложения.
Для получения более подробных обновлений в режиме реального времени рассмотрите возможность использования каналов Django с WebSockets, чтобы передавать ход конверсии непосредственно в браузер пользователя вместо опроса. Это устраняет необходимость в повторных HTTP-запросах и обеспечивает мгновенную обратную связь по завершении преобразования.
В этом случае, в идеале, Celery должен использоваться для того, чтобы позволить вашему HTTP-серверу быстро реагировать на HTTP-запрос, просто "ставя в очередь" работу, связанную с преобразованием pdf-файла.
Такая конфигурация позволила бы многим запросам помещать работу в очередь, не блокируя дальнейшие HTTP-запросы. При условии, что обработка выполняется на другом сервере или не влияет на системные ресурсы путем ограничения рабочих процессов или памяти, выделенной для рабочих процессов.
Однако, вызывая wait, вы предлагаете подождать, пока работа не будет завершена, а затем продолжить, что фактически сводит на нет все преимущества использования Celery. И, следовательно, блокирует возможность ваших HTTP-серверов отвечать на дальнейшие запросы.
Использование Celery в текущей конфигурации не дает никаких преимуществ, на самом деле это просто создает дополнительные накладные расходы.
Я не уверен в конечном результате, который вы конкретно ищете, но я бы предположил, что это означает начать работу, и как только преобразование будет завершено, интерфейс ответит, предоставив пользователю загрузку.
Чтобы не создавать проблем для вашего HTTP-сервера, вам нужно удалить вызов wait (который говорит, что я буду сидеть здесь и ничего не делать, пока вы все не закончите) и вместо этого предложить другой метод проверки завершения преобразования.
Это гораздо более масштабная задача. Вам понадобится способ идентифицировать конверсию, у меня мало опыта работы с Celery, но я бы предположил, что задаче присваивается идентификатор, и вы можете запросить, выполнен ли этот идентификатор или нет. В противном случае вам потребуется реализовать способ уникальной идентификации преобразования файлов и определения того, завершено оно или нет.
Как только вы это сделаете, вам понадобится интерфейс, чтобы проверить, выполнено ли это, и если да, запустить загрузку.
Есть много способов сделать это. Я бы предположил, что вам нужны опросы или веб-сокеты. Однако эти темы лучше всего поискать в Google, если вы впервые сталкиваетесь с проблемой.
Однако текущая конфигурация, которая у вас есть, не позволяет использовать Celery для каких-либо целей. Идея, которую вы преследуете, заключается в том, чтобы сообщить Celery, что у вас есть какая-то работа, которую вы хотели бы выполнить, а затем продолжить обработку / возврат HTTP-запроса. А затем позволить сельдерею добраться до него в какой-то момент в будущем.