Python asyncio пропустить обработку до возврата функции - PullRequest
0 голосов
/ 25 января 2019

Я все еще очень озадачен тем, как работает asyncio, поэтому я пытался показать простой пример, но не смог его достичь.

В следующем примере показан веб-сервер (Quart), который получает запрос на создание большого PDF-файла, затем сервер возвращает ответ перед началом обработки PDF-файла, затем начинает его обработку и отправляет ссылку для загрузки на электронную почту позже.

from quart import Quart
import asyncio
import time

app = Quart(__name__)

@app.route('/')
async def pdf():
    t1 = time.time()
    await generatePdf()
    return 'Time to execute : {} seconds'.format(time.time() - t1)

async def generatePdf():
    await asyncio.sleep(5)
    #sync generatepdf
    #send pdf link to email

app.run()

Как бы я поступил по этому поводу? в приведенном выше примере я не хочу, чтобы 5 секунд ждали до возврата.

Я даже не уверен, что мне нужно asyncio.

И я боюсь, что блокировка приложения сервера после возвращения ответа - это не то, что нужно делать, но и не уверен.

Кроме того, pdf-библиотека работает синхронно, но, думаю, это проблема для другого дня ...

Ответы [ 3 ]

0 голосов
/ 01 февраля 2019
  1. Я настоятельно рекомендую ознакомиться с этой пояснительной статьей Брэда Соломона о параллельном программировании и asyncio в python.
  2. В целях асинхронного выполнения задачи без необходимости блокировать запрос до тех пор, пока задача не будет завершена - я думаю, что лучшим вариантом является использование очереди с классом "PDFGenerator", который потребляет из шаблона очереди (также охватывается в статье)
0 голосов
/ 04 февраля 2019

Для вашей задачи, генерируя большой PDF, вы можете использовать асинхронную задачу / очередь заданий.Например, вы можете использовать Сельдерей .Так как вы не хотите ждать задания, верните ответ типа «генерирование PDF, пожалуйста, подождите минуту / секунду».Поэтому, когда запрос приходит к конечной точке «Сгенерировать PDF», вы создаете задачу в Celery, а Celery обрабатывает ее асинхронно, и после ее завершения вы можете отправить запрос клиенту или клиент может использовать «поиск задачи», используя идентификатор задачи (или как вы реализуете).Вот пример ответа - Как проверить состояние задачи в Celery?

Разница между Celery и Asyncio заключается в том, что Celery может выполнять задачу в совершенно отдельной среде и при общении с сервером.выполняется распределенной передачей сообщений типа RabbitMQ .Где Asyncio использует сопрограммы, чтобы использовать время блокировки ввода-вывода.Он будет использовать ту же среду и процессоры, где находится ваш сервер.

0 голосов
/ 28 января 2019

В комментарии есть все, что вам нужно, чтобы ответить на веб-запрос и запланировать генерацию PDF на будущее.

asyncio.create_task(generatePdf())

Однако не рекомендуется, если обработка pdf идет медленно, поскольку она блокирует поток событий asyncio.т. е. текущий запрос будет обработан быстро, но следующий запрос должен будет дождаться завершения генерации pdf.

Правильный способ будет выполнить задачу в исполнителе (особенно ProcessPoolExecutor ).

from quart import Quart
import asyncio
import time
from concurrent.futures import ProcessPoolExecutor

app = Quart(__name__)
executor = ProcessPoolExecutor(max_workers=5)

@app.route('/')
async def pdf():
    t1 = time.time()
    asyncio.get_running_loop().run_in_executor(executor, generatePdf)
    # await generatePdf()
    return 'Time to execute : {} seconds'.format(time.time() - t1)

def generatePdf():
    #sync generatepdf
    #send pdf link to email

app.run()

Важно отметить, что, поскольку он работает в другом процессе, generatePdf не может получить доступ к каким-либо данным без синхронизации.Поэтому передайте все, что нужно функции при вызове функции.


Обновление

Если вы можете реорганизовать функцию generatePdf и сделать ее асинхронной, она лучше всего работает.

Пример, если сгенерированный pdf выглядит так:

def generatePdf():
    image1 = downloadImage(image1Url)
    image2 = downloadImage(image2Url)
    data = queryData()
    pdfFile = makePdf(image1, image2, data)
    link = upLoadToS3(pdfFile)
    sendEmail(link)

Вы можете сделать функцию асинхронной, например:

async def generatePdf():
    image1, image2, data = await asyncio.gather(downloadImage(image1Url), downloadImage(image2Url), queryData())
    pdfFile = makePdf(image1, image2, data)
    link = await upLoadToS3(pdfFile)
    await sendEmail(link) 

Примечание. Все вспомогательные функции, такие как downloadImagequeryData необходимо переписать для поддержки async.Таким образом, запросы не будут блокироваться, даже если серверы баз данных или изображений работают медленно.Все работает в том же потоке asyncio.

Если некоторые из них еще не являются асинхронными, они могут использоваться с run_in_executor и должны хорошо работать с другими асинхронными функциями.

...