Python aiohttp ClientSession 请求内存泄漏?

问题描述 投票:0回答:1

我相信在使用 aiohttp ClientSession 请求时,我在长期存在的应用程序中发现了内存泄漏。如果按顺序等待每个发出请求的协程,那么一切似乎都很好。然而,并发运行时似乎存在请求上下文管理器对象的泄漏。

请考虑以下示例代码:

import logging
import tracemalloc
import asyncio
import aiohttp


async def log_allocations_coro():

    while True:
        await asyncio.sleep(120)
        snapshot = tracemalloc.take_snapshot()
        top_stats = snapshot.statistics('lineno')
        str_list = [str(x) for x in top_stats[:5]]
        logging.info("\n".join(str_list))


async def do_request():

    try:
        async with session.request("GET", "http://192.168.1.1") as response:
            text = await response.text()
    except:
        logging.exception("Request failed")


async def main():

    tracemalloc.start()
    asyncio.ensure_future(log_allocations_coro())
    
    timeout = aiohttp.ClientTimeout(total=1)
    global session
    session = aiohttp.ClientSession(timeout=timeout)

    while True:

        tasks = [ do_request(), do_request() ]
        await asyncio.gather(*tasks)
        await asyncio.sleep(2)


if __name__ == '__main__':

    logging.basicConfig(format='%(asctime)s %(message)s', level=logging.INFO)
    loop = asyncio.get_event_loop()
    loop.run_until_complete(main())

tracemalloc
协程每两分钟记录一次内存分配。这显示了
aiohttp/client.py
中的分配计数,其中
request()
返回
_RequestContextManager
随着时间的推移而增加,最初很快,但随后减慢,直到达到峰值,然后似乎相当稳定。 但是, 随后观察到,如果出现网络问题并且请求开始失败,则计数会再次上升 - 并且在问题解决后不会再下降。

这是泄漏吗?如果是这样,有办法解决吗?

感谢您的阅读!

python python-asyncio aiohttp
1个回答
0
投票

正如我在上面的评论中提到的,

while True
给我带来了协程泄漏的问题。考虑将循环重构为如下所示:

async def main():
    tracemalloc.start()
    asyncio.ensure_future(log_allocations_coro())
    
    timeout = aiohttp.ClientTimeout(total=1)
    global session
    session = aiohttp.ClientSession(timeout=timeout)

    await forever_do_request()

async def forever_do_request(loop)
    tasks = [ do_request(), do_request() ]
    await asyncio.gather(*tasks)
    await asyncio.get_event_loop().call_later(2,forever_do_request)

以这种方式,当forever_do_request被

call_later
d时,它的未来在
tasks
中超出范围,并且可以被垃圾收集/从事件循环中删除,这与用于延迟的
asyncio.sleep
具有类似的效果执行循环代码块。

© www.soinside.com 2019 - 2024. All rights reserved.