Python run_in_executor忘记了吗?

时间:2019-02-18 14:17:07

标签: python python-3.x python-asyncio python-3.7 sanic

如何将阻塞函数设置为在执行程序中运行,其结果无关紧要,所以主线程不应等待或减慢它的运行速度。

说实话,我不确定这是否是正确的解决方案,我所要做的就是将某种类型的处理队列与主进程分开,以便不阻止服务器应用程序返回请求,因为这种类型的网络服务器要为多个请求运行一个工作服务器。

优选地,我想远离Celery之类的解决方案,但是如果那是最理想的,我将乐于学习。

这里的上下文是一个异步Web服务器,该服务器生成具有大图像的pdf文件。

app = Sanic()
#App "global" worker
executor = ProcessPoolExecutor(max_workers=5)

app.route('/')
async def getPdf(request):
  asyncio.create_task(renderPdfsInExecutor(request.json))
  #This should be returned "instantly" regardless of pdf generation time
  return response.text('Pdf being generated, it will be sent to your email when done')

async def renderPdfsInExecutor(json):
  asyncio.get_running_loop.run_in_executor(executor, syncRenderPdfs, json)

def syncRenderPdfs(json)
  #Some PDF Library that downloads images synchronously
  pdfs = somePdfLibrary.generatePdfsFromJson(json)
  sendToDefaultMail(pdfs)

上面的代码给出了错误(是的,它以admin身份运行):

PermissionError [WinError 5] Access denied
Future exception was never retrieved

奖金问题:通过在执行程序内部运行asyncio循环,我可以获得任何收益吗?因此,如果它一次处理多个PDF请求,它将在它们之间分配处理。如果是,该怎么办?

2 个答案:

答案 0 :(得分:4)

好,所以首先有一个误会。

async def getPdf(request):
    asyncio.create_task(renderPdfsInExecutor(request.json))
    ...

async def renderPdfsInExecutor(json):
    asyncio.get_running_loop.run_in_executor(executor, syncRenderPdfs, json)

是多余的。足够了

async def getPdf(request):
    asyncio.get_running_loop.run_in_executor(executor, syncRenderPdfs, request.json)
    ...

或者(因为您不想等待)甚至更好

async def getPdf(request):
    executor.submit(syncRenderPdfs, request.json)
    ...

现在,您遇到的问题是因为syncRenderPdfs抛出了PermissionError。它没有被处理,因此Python警告您“嘿,某些后台代码引发了错误。但是该代码不归任何人所有,这到底是什么?”。这就是为什么您得到Future exception was never retrieved的原因。 您对pdf库本身有疑问,而不是asyncio。解决了内部问题后,安全起来也是一个好主意:

def syncRenderPdfs(json)
    try:
        #Some PDF Library that downloads images synchronously
        pdfs = somePdfLibrary.generatePdfsFromJson(json)
        sendToDefaultMail(pdfs)
    except Exception:
        logger.exception('Something went wrong')  # or whatever

您的“权限被拒绝”问题是完全不同的事情,您应该对其进行调试和/或发布一个单独的问题。

关于最后一个问题:是的,执行者将在工作人员之间排队并平均分配任务。

编辑:正如我们在评论中所述,实际的问题可能出在您使用的Windows环境上。或更确切地说,使用ProcessPoolExecutor,即,生成程序可能会更改权限。我建议使用ThreadPoolExecutor,假设它在平台上可以正常工作。

答案 1 :(得分:0)

您可以查看asyncio.gather(* tasks)以并行运行多个。