簡體   English   中英

API 電報機器人的速率限制

[英]API rate limit for telegram bot

我正在開發一個帶有模塊 pyTelegramBotAPI 的機器人,它通過 webhooks 工作,並安裝了 Flask+Gunicorn 作為 webhooks 服務器。 Gunicorn 正在與 5 名工人合作以提高速度,我的項目結構如下所示:

app.py
bot.py

在 bot.py 中,我有一個 function 用於處理更新:

def process_the_update(update):
    logger.info(update)
    update = types.Update.de_json(update)
    bot.process_new_updates([update])

在 app.py 中,我導入了這個 function,因此,每當更新到來時,app.py 都會調用這個 function,bot 將處理更新。 在我的機器人中,用戶可以調用一個命令,該命令將使用外部 api 來獲取一些信息。 問題是,這個外部 api 限制為每秒 3 個請求。 我需要配置一個具有這種速率限制的機器人。 首先,我想到用 Queue 來做,代碼如下:

lock_queue = Queue(1)
requests_queue = Queue(3)
def api_request(argument):
    if lock_queue.empty():
        try:
            requests_queue.put_nowait(time.time())
        except queue.Full:
            lock_queue.put(1)
            first_request_time = requests_queue.get()
            logger.info('First request time: ' + str(first_request_time))
            current_time = time.time()
            passed_time = current_time - first_request_time
            if passed_time >= 1:
                requests_queue.put_nowait(time.time())
                lock_queue.get()
            else:
                logger.info(passed_time)
                time.sleep(1 - passed_time)
                requests_queue.put_nowait(time.time())
                lock_queue.get()
    else:
        lock_queue.put(1)
        first_request_time = vk_requests_queue.get()
        logger.info('First request time: ' + str(first_request_time))
        current_time = time.time()
        passed_time = current_time - first_request_time
        if passed_time >= 1:
            requests_queue.put_nowait(time.time())
            lock_queue.get()
        else:
            logger.info(passed_time)
            time.sleep(1 - passed_time)
            requests_queue.put_nowait(time.time())
            lock_queue.get()
    result = make_api_request(argument) # requests are made too by external module.
    return result 

邏輯是,正如我所想的那樣,因為模塊 pyTelegramBotAPI 使用線程來更快地處理更新,所有線程都會檢查 requests_queue,這將有 3 個最后 api_requests 的時間,因此 3 個請求中的第一個的時間將與當前時間(檢查是否過了一秒)。 而且,因為我需要確定只有一個線程會同時進行這種比較,所以我創建了 lock_queue。 但是,問題在於,首先,gunicorn 使用了 5 個 worker,因此總是有可能來自用戶的所有消息將在不同的進程中處理,而這些進程將有自己的隊列。 其次,即使我將工人數量設置為默認值(1 名工人),我仍然會收到 429 錯誤,所以我認為我的代碼根本無法按我想要的方式工作。

我想用 redis 進行速率限制,所以每次在每個線程和進程中機器人都會檢查最后 3 個請求的時間,但我仍然不確定,這是正確的方法,我不確定如何寫這個。

很高興,如果有人提出任何想法甚至代碼示例(外部 api 不提供任何 x-rate-limit 標頭)

寫了這個function,使用redis來統計請求(基於這個https://www.binpress.com/tutorial/introduction-to-rate-limiting-with-redis/155教程)

import redis

r_db = redis.Redis(port=port, db=db)

def limit_request(request_to_make, limit=3, per=1, request_name='test', **kwargs):
    over_limit_lua_ = '''
    local key_name = KEYS[1]
    local limit = tonumber(ARGV[1])
    local duration = ARGV[2]

    local key = key_name .. '_num_of_requests'
    local count = redis.call('INCR', key)
    if tonumber(count) > limit then
        local time_left = redis.call('PTTL', key)
        return time_left
    end
    redis.call('EXPIRE', key, duration)
    return -2
    '''

    if not hasattr(r_db, 'over_limit_lua'):
        r_db.over_limit_lua = r_db.register_script(over_limit_lua_)

    request_possibility = int(r_db.over_limit_lua(keys=request_name, args=[limit, per]))
    if request_possibility > 0:
        time.sleep(request_possibility / 1000.0)
        return limit_request(request_to_make, limit, per, request_name, **kwargs)
    else:
        request_result = request_to_make(**kwargs)
        return request_result

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM