我对使用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
参数,将其设置为较高和较低,但是我看不出它有多大帮助。也尝试过在一些不同的服务器上运行它,但是看起来是一样的。有什么方法可以考虑如何更有效地设置这些值?
请多多指教我在这里可能做错的事情!