Python asyncio 跳过处理直到函数 return

Python asyncio skip processing untill function return

我对 asyncio 的工作原理仍然很困惑,所以我试图设置一个简单的示例但无法实现。

以下示例是一个 Web 服务器 (Quart),它接收生成大型 PDF 的请求,服务器然后 return 在开始处理 PDF 之前做出响应,然后开始处理它并发送下载link 稍后发送电子邮件。

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()

我该怎么做?在上面的示例中,我不希望在 return.

之前等待 5 秒

我什至不确定 asyncio 是否是我需要的。

我担心在响应 returned 之后阻止服务器应用程序不是应该做的事情,但也不确定。

pdf 库也是同步的,但我想这又是个问题...

评论包含您响应网络请求和安排稍后生成 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()

重要的是要注意,因为它是 运行ning 在不同的进程中,所以 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. 我强烈建议您查看 Brad Solomon 在 python 中关于并行编程和 asyncio 的 explanatory article
  2. 出于异步执行任务的目的,不需要阻塞请求直到任务完成 - 我认为最好的选择是使用文章中的 queue that with a "PDFGenerator" class that consumes from the queue pattern(also covered)

对于生成大型 PDF 的任务,您可以使用异步 task/job 队列。例如,您可以使用 Celery. Since you don't want to wait for the task, rather return a reply like - "generating PDF, please wait a minute/second". So when a request comes to the "generate PDF" endpoint, you will create a task in Celery and Celery will process it asynchronously and after completion, you can push to client or client can use the "task lookup" using the task-id (or as you implement). Here is an example answer - How to check task status in Celery?

Celery 和 Asyncio 的区别在于,Celery 可以在完全分离的环境中执行任务,并且与服务器的通信通过分布式消息传递完成,如 RabbitMQ。 Asyncio 使用协程来利用阻塞 I/O 时间。它将使用您的服务器所在的相同环境和处理器。