Azure Blob异步download_blob调用asyncio.run二次执行失败问题排查
问题原因分析
- 异步
ContainerClient实例在第一次通过async with上下文管理器使用后,会自动关闭内部的HTTP会话(如aiohttp会话),释放相关资源。 - 第二次调用时,客户端实例已处于关闭状态,内部会话对象变为
None,执行download_blob时无法重建连接,因此抛出AttributeError。 - 另外,
asyncio.run每次调用都会创建新事件循环,第一次运行后客户端资源已被清理,无法跨事件循环复用。
解决办法
方案一:每次调用创建新的ContainerClient实例
将客户端创建逻辑放入异步函数内部,确保每次调用使用全新客户端:
from azure.storage.blob.aio import ContainerClient from azure.identity import DefaultAzureCredential import asyncio async def test(container_url, blob_path): credentials = DefaultAzureCredential() async with ContainerClient.from_container_url(container_url=container_url, credential=credentials) as client_conn: stream = await client_conn.download_blob(blob_path) data = await stream.readall() return data if __name__ == "__main__": my_container_url = "你的容器URL" my_path = 'test_path' data = asyncio.run(test(my_container_url, my_path)) data2 = asyncio.run(test(my_container_url, my_path))
方案二:在同一事件循环内执行多次调用
避免多次调用asyncio.run,在单个事件循环中完成所有下载操作:
from azure.storage.blob.aio import ContainerClient from azure.identity import DefaultAzureCredential import asyncio async def download_blob(client, blob_path): stream = await client.download_blob(blob_path) return await stream.readall() async def main(): my_container_url = "你的容器URL" my_path = 'test_path' credentials = DefaultAzureCredential() async with ContainerClient.from_container_url(container_url=my_container_url, credential=credentials) as client: data = await download_blob(client, my_path) data2 = await download_blob(client, my_path) return data, data2 if __name__ == "__main__": data, data2 = asyncio.run(main())
方案三:手动管理客户端生命周期(不推荐)
若需复用客户端,需避免async with自动关闭,手动控制生命周期,但要注意跨事件循环复用可能引发资源问题:
from azure.storage.blob.aio import ContainerClient from azure.identity import DefaultAzureCredential import asyncio async def test(conn_client, blob_path): stream = await conn_client.download_blob(blob_path) data = await stream.readall() return data if __name__ == "__main__": my_container_url = "你的容器URL" my_path = 'test_path' credentials = DefaultAzureCredential() my_client = ContainerClient.from_container_url(container_url=my_container_url, credential=credentials) try: data = asyncio.run(test(my_client, my_path)) data2 = asyncio.run(test(my_client, my_path)) finally: asyncio.run(my_client.close())
关键说明
- 异步Azure SDK客户端(如
ContainerClient)设计为单次上下文管理器使用或在同一事件循环内复用,不支持跨多次asyncio.run调用复用。 asyncio.run每次调用结束会关闭事件循环并清理所有资源,第一次运行后客户端内部状态已失效。
内容的提问来源于stack exchange,提问作者Ziur Olpa
相关产品推荐
相关产品推荐

