【问题标题】:How to create a global connection with asyncio and redis如何使用 asyncio 和 redis 创建全局连接
【发布时间】:2017-08-11 00:17:40
【问题描述】:

我是来自 gevent 和 2.7 的 python 3 和 asyncio 的新手 ....

如何创建一个可供所有人用于 reids 的全局连接?例如。我将有 1 个进程,例如10 个异步线程,但我不希望每个线程有单独的连接。为什么?...会有例如100 个核心,每个核心 10 个线程,并且不希望有那么多连接到 redis

import asyncio
import asyncio_redis

async def worker():
    while True:
        data = await connection.brpop(['queue'], timeout=0)
        print(data)
        res = blocking_code(data)
        await connection.set('test',res)

#Process raw data here and all code is blocking
def blocking_code(data):
    results = {}
    return results

if __name__ == '__main__':
    connection = asyncio_redis.Connection.create(host='127.0.0.1', port=6379, poolsize=2)
    loop = asyncio.get_event_loop()
    tasks = [asyncio.ensure_future(worker()), asyncio.ensure_future(worker())]
    loop.run_until_complete(asyncio.gather(*tasks))
    connection.close()


    Traceback (most recent call last):
      File "/Users//worker.py", line 14, in <module>
        loop.run_until_complete(example())
      File "/Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/asyncio/base_events.py", line 466, in run_until_complete
        return future.result()
      File "/Users//worker.py", line 7, in example
        data = yield from connection.brpop(['queue'], timeout=0)
    AttributeError: 'generator' object has no attribute 'brpop'

所以在上面我有两个任务,但我只想要 1 个 redis 连接

【问题讨论】:

    标签: python-3.x python-asyncio


    【解决方案1】:

    10 个异步线程

    以防万一 - asyncio 协程在一个线程中运行。通过在 I/O 操作时在协程之间切换来实现并发。

    为什么你的代码不起作用?

    asyncio_redis.Connection.create - 是一个协程,您应该使用 yield from 等待此操作以从中获取结果:

    connection = yield from asyncio_redis.Connection.create(host='127.0.0.1', port=6379)
    

    如何创建全局连接

    如果您只有一个连接,您可能不会从使用 asyncio 中获得任何好处。并发请求可能需要可以使用的连接池。 asyncio_redis has 简单的方法,例如:

    import asyncio
    import asyncio_redis
    
    
    @asyncio.coroutine
    def main():
        connection = yield from asyncio_redis.Pool.create(host='127.0.0.1', port=6379, poolsize=10)
        try:
            # 3 requests running concurrently in single thread using connections from pool: 
            yield from asyncio.gather(
                connection.brpop(['queue:pixel'], timeout=0),
                connection.brpop(['queue:pixel'], timeout=0),
                connection.brpop(['queue:pixel'], timeout=0),
            )
        finally:
            connection.close()
    
    
    
    if __name__ == '__main__':
        loop = asyncio.get_event_loop()
        loop.run_until_complete(main())
        loop.close() 
    

    Python 3.5+

    如果您使用的是 Python 3.5+,请考虑使用 newer syntax 来定义和等待协程。

    更新:

    阻塞代码(例如,需要大量 CPU 时间的代码)不能直接在协程中使用:它会冻结你的事件循环并且你不会从 asyncio 中受益。它与连接数无关。

    您可以使用run_in_executor 在单独的进程中运行此代码,而不会阻塞事件循环:

    from concurrent.futures import ProcessPoolExecutor
    
    
    executor = ProcessPoolExecutor(max_workers=10)  # use number of cores here
    
    
    async def worker():
        while True:
            data = await connection.brpop(['queue'], timeout=0)
            print(data)
    
            # await blocking_code from separate process:
            loop = asyncio.get_event_loop()
            result = await loop.run_in_executor(executor, blocking_code, data)
    

    【讨论】:

    • 我编辑了我的代码,看看你的答案是否仍然有效。这是一个将永远运行的循环......它是一个工作服务器
    • 我不同意上述观点...在工作服务器上,我将为每个核心执行 1 个进程。虽然我是例如写回数据库代码阻塞代码将在下一个循环检查结果时运行。这就是它在工作服务器上的工作方式,至少它是如何与 gevent、线程等一起工作的……我只对非常长的阻塞代码使用多进程……我的阻塞代码相对较小。如果我为每个任务创建一个单独的 conn,我的代码确实可以工作,但这不是我想要的
    • 我觉得可以直接将返回的连接对象存入函数对象中;或者使用装饰器缓存返回的连接对象;或者将函数的创建过程包装在一个类方法中,并将返回的连接存储到cls对象中。
    猜你喜欢
    • 2018-11-12
    • 2017-11-09
    • 1970-01-01
    • 1970-01-01
    • 2017-07-24
    • 2017-11-01
    • 2015-10-15
    • 1970-01-01
    • 2022-01-10
    相关资源
    最近更新 更多