我可以将aiohttp和psycopg2一起正确使用吗?

时间:2018-07-03 12:00:33

标签: python psycopg2 aiohttp

我对使用asyncio / aiohttp非常陌生,但是我有一个Python脚本,可以从Postgres表中读取一批URL:s,下载URL:s,并在每次下载时运行处理功能(与问题),然后将处理结果保存回表中。

简化形式如下:

import asyncio
import psycopg2
from aiohttp import ClientSession, TCPConnector

BATCH_SIZE = 100

def _get_pgconn():
    return psycopg2.connect()

def db_conn(func):
    def _db_conn(*args, **kwargs):
        with _get_pgconn() as conn:
            with conn.cursor() as cur:
                return func(cur, *args, **kwargs)
            conn.commit()
    return _db_conn

async def run():
    async with ClientSession(connector=TCPConnector(ssl=False, limit=100)) as session:
        while True:
            count = await run_batch(session)
            if count == 0:
                break

async def run_batch(session):
    tasks = []
    for url in get_batch():
        task = asyncio.ensure_future(process_url(url, session))
        tasks.append(task)

    await asyncio.gather(*tasks)
    results = [task.result() for task in tasks]
    save_batch_result(results)
    return len(results)

async def process_url(url, session):
    try:
        async with session.get(url, timeout=15) as response:
            body = await response.read()
            return process_body(body)
    except:
        return {...}

@db_conn
def get_batch(cur):
    sql = "SELECT id, url FROM db.urls WHERE processed IS NULL LIMIT %s"
    cur.execute(sql, (BATCH_SIZE,))
    return cur.fetchall()


@db_conn
def save_batch_result(cur, results):
    sql = "UPDATE db.urls SET a = %(a)s, processed = true WHERE id = %(id)s"
    cur.executemany(sql, tuple(results))


loop = asyncio.get_event_loop()
loop.run_until_complete(run())

但是我有一种感觉,我必须在这里错过一些东西。该脚本可以运行,但是每批脚本似乎变得越来越慢。特别是,随着时间的流逝,对process_url函数的调用似乎变得越来越慢。另外,使用的内存还在增长,因此我猜可能在运行之间可能无法正确清理某些内容?

我也有很多增加批处理大小的问题,如果我的批处理数量超过200,那么从调用session.get看来,异常的比例会更高。我尝试使用TCPConnector的limit参数,将其设置为较高和较低,但是我看不出它有多大帮助。也尝试过在一些不同的服务器上运行它,但是看起来是一样的。有什么方法可以考虑如何更有效地设置这些值?

请多多指教我在这里可能做错的事情!

1 个答案:

答案 0 :(得分:2)

您的代码问题是将异步 aiohttp 库与同步 psycopg2 客户端混合在一起。

因此,对DB的调用会阻塞事件循环,从而完全影响所有其他并行任务。

要解决此问题,您需要使用异步数据库客户端:aiopg(围绕 psycopg2 异步模式的包装器)或asyncpg(具有不同的API,但运行速度更快)