我的以下功能未按预期运行。它是一个异步函数,我希望它能够在响应为 401 并且锁未锁定时删除
api_keys
中的第一个值。当锁被锁定时,我希望它重试,因为 api_keys
当前正在被修改。但是,输出会连续打印多个状态代码 401,但我的代码应该在返回 401 时更改当前的 api 密钥,并且应该从那里开始工作。
api_keys = []
lock = asyncio.Lock()
async def fetch_data(session, url, params, delay):
await asyncio.sleep(delay)
global api_keys
global lock
while True:
if lock.locked():
print("Lock is locked, continuing...")
continue
os.environ["API_KEY"] = api_keys[0]
params["api_key"] = os.getenv("API_KEY")
async with session.get(url, params=params) as response:
if response.status == 200:
data = await response.json()
print("Remaining requests", response.headers["x-requests-remaining"])
print("Used requests", response.headers["x-requests-used"])
return data
else:
print(
f"Failed to get odds: status_code {response.status}, response body {await response.text()}".rstrip()
)
if response.status == 401:
async with lock:
if len(api_keys) > 1:
print("Using next API key in list")
api_keys.pop(0)
continue
else:
print("No more API keys available")
return None
return None
我首先尝试不使用
if lock.locked()
线,并在 while 循环周围移动 async with lock
线,就在循环下方,但无济于事。
while True: if condition: continue
可能会造成一个繁忙的循环 - 这已经很糟糕了。更糟糕的是,在这样的循环中条件不能改变(在异步中,与多线程不同)。一旦进入,将是无限的。
api_keys
的锁因为当前可能被修改,在异步中没有多大意义(再次与多线程不同),任务切换不能在任何地方发生。
如果我正确理解逻辑,api_key 会被重复使用多次,直到第一个 401 错误,然后应该被淘汰。在当前程序中,如果多个任务都会收到 401 错误,所有任务都会从列表中删除第一个键,而不仅仅是要退役的键。
我的建议是做一个中央密钥存储。这将解决问题#1:任务在没有密钥的情况下无法继续,也解决问题#2:密钥必须仅停用一次。下面是一个没有实现的存根。
您的问题中没有提供如何获取新密钥的信息,我忽略了这一点。
async def get_key():
"""
Return the first API key from the key list.
Wait while the key list is empty (asyncio.Event suggested).
Do: key = await get_key() before each API transaction.
"""
def retire_key(key):
"""
Remove this key from the beginning of the key list.
Do nothing if the key is not there. Presumably it has
been removed by some other key user.
Do: retire_key(key) after a 401 error
"""