Flask应用程序

时间:2018-05-02 11:22:18

标签: python flask background-process python-multithreading python-asyncio

我有一个使用Flask和Graphene包构建的GraphQL API,运行在 Python 3.5.4 上。其中一个GraphQL突变需要一些时间来执行(2到5分钟),我不希望最终用户等待其执行完成。我希望在后台执行变异方法,并立即向用户返回一条消息。

我已经查看了 asyncio 包但由于某种原因,执行仍然出现在最前端,我的脚本正在等待。你知道我做错了什么吗?脚本很长,所以我在下面总结了与asyncio相关的关键元素。

文件mutation_migration_plan.py

from migration_script import Migration, main
import asyncio
[...]

class executeMigrationPlan(graphene.Mutation):
    """Mutation to execute a migration plan."""
    [...]

    @staticmethod
    def mutate(root, info, input):
    [...]
        # Execute migration asynchronously
        print('Execute migration asynchronously')
        loop = asyncio.get_event_loop()
        loop.run_until_complete(main(migration_plan))
        print('Migration plan execution has started. You will receive an e-mail when it is terminated.')

        ok = True
        message = 'Migration plan execution has started. You will receive an e-mail when it is terminated.'
        return executeMigrationPlan(ok=ok, message=message)

文件migration_script.py

class Migration():
    """Class to execute migration of Plan, Step, Object."""

    @staticmethod
    async def migrate(migration_plan, migration_step=None, migration_object=None):
        [...]

async def main(migration_plan, migration_step=None, migration_object=None):
    asyncio.ensure_future(Migration.migrate(migration_plan, migration_step, migration_object))

基本上我希望在方法print('Migration plan execution has started. You will receive an e-mail when it is terminated.')中几乎立即在我的控制台窗口中看到loop.run_until_complete(main(migration_plan)),但现在情况并非如此,并且打印仅出现在执行结束。

[UPDATE]

根据以下@Vincent的回答,我更新了第一个文件文件mutation_migration_plan.py 以使用 ThreadPoolExecutor ,并从两个文件中删除了与asyncio相关的所有内容。

文件mutation_migration_plan.py

from migration_script import Migration
from concurrent.futures import ThreadPoolExecutor
[...]

class executeMigrationPlan(graphene.Mutation):
    """Mutation to execute a migration plan."""
    [...]

    @staticmethod
    def mutate(root, info, input):
        [...]
        # Execute migration asynchronously
        print('Execute migration asynchronously')
        executor = ThreadPoolExecutor(max_workers=1)
        future = executor.submit(Migration.migrate, migration_plan)
        # print(future.result())
        print('Migration plan execution has started. You will receive an e-mail when it is terminated.')

        ok = True
        message = 'Migration plan execution has started. You will receive an e-mail when it is terminated.'
        return executeMigrationPlan(ok=ok, message=message)

当我添加print(future.result())行时,我的脚本运行正常,但它不在后台执行(因为我试图打印结果,所以有意义)。但是,当我注释掉打印时,我的方法Migration.migrate似乎没有正确执行(我知道它,因为我没有在我的数据库中看到结果)。知道为什么吗?

[UPDATE BIS]

文件mutation_migration_plan.py

我已经能够使用ProcessPoolExecutor异步执行我的方法,并在两个文件上删除对asyncio的所有引用。请参阅以下代码:

文件mutation_migration_plan.py

from concurrent.futures import ProcessPoolExecutor
[...]

class executeMigrationPlan(graphene.Mutation):
    """Mutation to execute a migration plan."""
    [...]

    @staticmethod
    def mutate(root, info, input):
        [...]
        # Execute migration asynchronously
        print('Execute migration asynchronously')
        executor = ProcessPoolExecutor()
        executor.submit(Migration.migrate, migration_plan.id)
        print('Migration plan execution has started. You will receive an e-mail when it is terminated.')

        ok = True
        message = 'Migration plan execution has started. You will receive an e-mail when it is terminated.'
        return executeMigrationPlan(ok=ok, message=message)

它正在工作但是虽然进程在后端执行,但我的Falsk应用程序需要很长时间才能发送http响应,而且响应有时是空的。

1 个答案:

答案 0 :(得分:3)

这是一种常见的误解,但您无法将asyncio插入现有应用程序并期望它能够正常运行。在asyncio中,每个阻塞调用都必须在协同程序的上下文中使用await语法。这是它实现单线程并发的唯一方法。这意味着您必须使用aiohttp代替烧瓶,以及aiohttp-graphql等库。

这需要对您的应用程序进行重大改写。如果您不想通过它,还有其他解决方案可以很好地与烧瓶集成。您可以使用@dirn指定的celeryconcurrent.futures提供的执行程序之一。