简体   繁体   English

在 Python RQ 中执行等待/异步 function

[英]Executing an awaitable / async function in Python RQ

My jobs are all a series of requests that need to be made per object.我的工作都是需要按照 object 提出的一系列请求。 Ie, its a User with several data points (potentially hundreds) that need to be added to that user with requests.即,它的User有几个数据点(可能有数百个),需要通过请求添加到该用户。 I had originally written those requests to run synchronously but it was blocking and slow.我最初编写了这些请求以同步运行,但它阻塞且缓慢。 I was sending each User job to Python RQ and have 10 workers going through the Users sent down the queue.我将每个User作业发送到 Python RQ 并有 10 名工作人员通过将用户发送到队列中。 1 worker, 1 user, blocking requests. 1 个工作人员,1 个用户,阻塞请求。

I've re-written my User job to use aiohttp instead of python requests, and its significantly faster.我已经重新编写了我的用户作业以使用 aiohttp 而不是 python 请求,并且速度明显更快。 On the Python RQ documentation it says that 'Any Python function call can be put on an RQ queue.'在 Python RQ 文档中,它说“任何 Python function 调用都可以放在 RQ 队列中。” but I can't figure out how to send my async function down the queue?但我不知道如何将我的异步 function 发送到队列中?


async def get_prices(calls: List[dict]) -> List[dict]:
     async with aiohttp.ClientSession() as session:
         for price in prices.items():
                price_type, date = price
                price = await pg.get_price(
                    session=session, lookup_date=date
                )
        do_some_other_stuff()
        await session.close()


from core.extensions import test_queue
from prices import get_prices
job = test_queue.enqueue(get_prices, kwargs={"username":'username'})

The problem is that get_prices is never awaited, it just remains a coroutine futures.... How can I await my function on the queue?问题是永远不会等待get_prices ,它只是一个协程期货......我如何在队列中等待我的 function?

Since python-rq won't support asyncio directly, you can use a synchronous function that calls asyncio.run instead.由于python-rq不会直接支持 asyncio,因此您可以使用调用asyncio.run的同步 function 来代替。

async def _get_prices(calls: List[dict]) -> List[dict]:
    # ...

def get_prices(*args, **kwargs):
    asyncio.run(_get_prices(*args, **kwargs))

Note, however, that asyncio.run only works if there's no other running event loop.但是请注意, asyncio.run仅在没有其他正在运行的事件循环时才有效。 If you expect an asyncio loop to already be running, use loop.create_task instead.如果您希望 asyncio 循环已经在运行,请改用loop.create_task

def get_prices(*args, **kwargs):
    loop = asyncio.get_event_loop()
    coro = _get_prices(*args, **kwargs)
    loop.create_task(coro)

Then when python-rq calls get_prices it will cause the async function to be executed.然后当python-rq调用get_prices时,它会导致异步 function 被执行。

Another option would be to not use asyncio for making requests, like using grequests , threads, or something like that which will work with synchronous functions.另一种选择是不使用 asyncio 来发出请求,例如使用grequests 、线程或类似的东西,它可以与同步函数一起使用。

You might consider using arq .您可以考虑使用arq

Created by the maintainer of Pydantic, it is not the same thing, but was inspired on rq.由 Pydantic 的维护者创建,它不是一回事,而是受到 rq 的启发。

Besides, it's still Redis and queues (with asyncio now).此外,它仍然是 Redis 和队列(现在使用asyncio )。

From the docs :文档

Job queues and RPC in python with asyncio and redis. python 中的作业队列和 RPC,带有 asyncio 和 redis。

arq was conceived as a simple, modern and performant successor to rq. arq 被认为是 rq 的简单、现代和高性能的继任者。

Simple usage:简单用法:

import asyncio
from aiohttp import ClientSession
from arq import create_pool
from arq.connections import RedisSettings

async def download_content(ctx, url):
    session: ClientSession = ctx['session']
    async with session.get(url) as response:
        content = await response.text()
        print(f'{url}: {content:.80}...')
    return len(content)

async def startup(ctx):
    ctx['session'] = ClientSession()

async def shutdown(ctx):
    await ctx['session'].close()

async def main():
    redis = await create_pool(RedisSettings())
    for url in ('https://facebook.com', 'https://microsoft.com', 'https://github.com'):
        await redis.enqueue_job('download_content', url)

# WorkerSettings defines the settings to use when creating the work,
# it's used by the arq cli.
# For a list of available settings, see https://arq-docs.helpmanual.io/#arq.worker.Worker
class WorkerSettings:
    functions = [download_content]
    on_startup = startup
    on_shutdown = shutdown

if __name__ == '__main__':
    asyncio.run(main())

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM