我对 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 库也是同步的,但我想这是另一天的问题......
最佳答案
评论包含您响应网络请求和安排稍后生成 pdf 所需的一切。
asyncio.create_task(generatePdf())
但是,如果 pdf 处理缓慢,则不是一个好主意,因为它会阻塞异步事件线程。即当前请求将得到快速响应,但后续请求必须等到 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)
注意:所有辅助函数,如downloadImage
、queryData
都需要重写以支持async
。这样,即使数据库或图像服务器很慢,请求也不会被阻止。一切都在同一个异步线程中运行。
如果它们中的一些还不是异步的,那么它们可以与 run_in_executor
一起使用,并且应该可以很好地与其他异步函数一起使用。
关于Python asyncio 跳过处理直到函数返回,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54370505/