Python asyncio跳过处理,直到函数返回

时间:2019-01-25 17:53:12

标签: python python-asyncio python-3.7 quart asgi

我仍然对asyncio的工作方式感到困惑,因此我试图树立一个简单的例子,但无法实现。

下面的示例是一个Web服务器(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 :(得分:6)

评论包含您需要的所有内容,以响应Web请求并计划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。这样,即使数据库或图像服务器运行缓慢,请求也不会被阻止。一切都在同一个异步线程中运行。

如果其中一些尚未异步,则可以将它们与run_in_executor一起使用,并且应该可以与其他异步功能配合使用。

答案 1 :(得分:1)

  1. 我强烈建议您阅读Brad Solomon撰写的有关explanatory article的有关python中的并行编程和异步的内容。
  2. 出于异步执行任务的目的,而无需在任务完成之前阻塞请求-我认为最好的选择是将queue与“ PDFGenerator”类一起使用,该类从队列模式(也在文章中covered

答案 2 :(得分:0)

对于您的任务,生成大型PDF,可以使用异步任务/作业队列。例如,您可以使用Celery。由于您不想等待任务,因此请返回诸如“生成PDF,请等待一分钟/秒”之类的答复。因此,当请求到达“生成PDF”端点时,您将在Celery中创建一个任务,而Celery将异步处理该任务,完成后,您可以推送到客户端,或者客户端可以使用task-id使用“任务查找”(或实施时)。这是一个示例答案-How to check task status in Celery?

Celery和Asyncio之间的区别在于,Celery可以在完全独立的环境中执行任务,并且与服务器的通信是通过像RabbitMQ这样的分布式消息来完成的。 Asyncio使用协程来利用阻塞I / O时间的地方。它将使用服务器所在的环境和处理器。