从 API 异步获取数据时,Asyncio 锁不起作用

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

我的以下功能未按预期运行。它是一个异步函数,我希望它能够在响应为 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
线,就在循环下方,但无济于事。

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

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
    """
© www.soinside.com 2019 - 2024. All rights reserved.