You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何修复异步请求以太坊地址余额时的RuntimeError等错误?

以太坊地址余额异步请求错误排查与修复

问题背景

同步代码可正常请求以太坊地址余额,但处理1000个地址时性能不足,改用异步实现后出现错误。

同步代码(可正常运行)

import requests
import time
import pandas as pd

start = time.time()
df = pd.read_csv('ethereumaddresses.csv') 
Wallet_Address=(df.loc[:,'Address'])
results = []

start = time.time()
for address in Wallet_Address:
    url = f"https://blockscout.com/eth/mainnet/api?module=account&action=eth_get_balance&address={address}"
    response = requests.get(url)
    results = response.json()
    
    print(results)
    
end = time.time()

total_time = end - start

print(f"It took {total_time} to make {len(Wallet_Address)} API calls")

异步实现代码(运行报错)

import asyncio
import aiohttp
import time
import pandas as pd


start = time.time()
df = pd.read_csv('Ethereum/ethereumaddresses.csv') 
Wallet_Address=(df.loc[:,'Address'])
results = []

def get_tasks(session):
    tasks = []
    for address in Wallet_Address:
        url = f"https://blockscout.com/eth/mainnet/api?module=account&action=eth_get_balance&address={address}"
        tasks.append(session.get(url,ssl=False))
        print(address)
    return tasks

session_timeout = aiohttp.ClientTimeout(total=None)

async def get_balances():
    async with aiohttp.ClientSession(timeout=session_timeout) as session:
       tasks = get_tasks(session)
       responses = await asyncio.gather(*tasks) 
       for response in responses:
        results.append(await response.json())
    
asyncio.run(get_balances()) 
  
end = time.time()
total_time = end - start
print(f"It took {total_time} seconds to make {len(Wallet_Address)} API calls")

运行错误信息

RuntimeError: await wasn't used with future
_OverlappedFuture exception was never retrieved
future: <_OverlappedFuture finished exception=OSError(22, 'The I/O operation has been aborted because of either a thread exit or an application request', None, 995, None)>
Traceback (most recent call last):
  File "AppData\Local\Programs\Python\Python310\lib\asyncio\windows_events.py", line 817, in _poll
    value = callback(transferred, key, ov)
  File "AppData\Local\Programs\Python\Python310\lib\asyncio\windows_events.py", line 604, in finish_connect
    ov.getresult()
OSError: [WinError 995] The I/O operation has been aborted because of either a thread exit or an application request

问题分析与修复

核心问题

  1. 无限制并发请求:一次性发起1000个请求,目标服务器会触发限流或直接拒绝连接,导致I/O操作中断,出现WinError 995。
  2. 全局变量风险:异步环境下使用全局results变量,虽单线程异步不会直接冲突,但代码耦合性高、可读性差。
  3. 超时设置不合理:total=None意味着请求无超时限制,可能导致请求长期挂起占用资源。
  4. SSL验证随意关闭:ssl=False存在安全风险,且多数场景下无需关闭。

修复后的代码

import asyncio
import aiohttp
import time
import pandas as pd

async def fetch_balance(session, address, semaphore):
    url = f"https://blockscout.com/eth/mainnet/api?module=account&action=eth_get_balance&address={address}"
    async with semaphore:
        try:
            async with session.get(url, timeout=aiohttp.ClientTimeout(total=10)) as response:
                response.raise_for_status()  # 捕获HTTP状态码错误
                return await response.json()
        except Exception as e:
            print(f"请求地址 {address} 失败: {str(e)}")
            return {"address": address, "error": str(e)}

async def get_balances(addresses):
    semaphore = asyncio.Semaphore(20)  # 限制同时发起20个请求
    async with aiohttp.ClientSession() as session:
        tasks = [fetch_balance(session, addr, semaphore) for addr in addresses]
        results = await asyncio.gather(*tasks)
    return results

if __name__ == "__main__":
    start = time.time()
    df = pd.read_csv('Ethereum/ethereumaddresses.csv')
    wallet_addresses = df.loc[:, 'Address'].tolist()
    
    results = asyncio.run(get_balances(wallet_addresses))
    
    end = time.time()
    total_time = end - start
    print(f"完成 {len(wallet_addresses)} 个API请求,耗时 {total_time:.2f} 秒")
    # 可选:将结果保存到CSV
    # pd.DataFrame(results).to_csv('eth_balances.csv', index=False)

关键修改说明

  • 并发数控制:用asyncio.Semaphore限制同时请求数(示例设为20),避免触发服务器限流。
  • 独立请求封装:每个地址请求封装为fetch_balance函数,单独处理异常,确保一个请求失败不影响其他请求。
  • 合理超时设置:给每个请求设置10秒超时,防止请求无限挂起。
  • 避免全局变量:在函数内部维护结果列表,代码更清晰安全。
  • Windows兼容优化:用if __name__ == "__main__":包裹主逻辑,解决Windows下异步运行的线程退出问题。

内容的提问来源于stack exchange,提问作者ChaosX

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 12:20:22