Как ограничить время выполнения запроса? - PullRequest
1 голос
/ 07 октября 2019

Я должен дать ответ в течение 3 секунд.

Мой обработчик общается со сторонним сервером, который мог ответить в течение 3 секунд или не смог.

Я думаю о следующем коде -

class MainReply(webapp2.RequestHandler):
    def get(self):
        # do something
        # start task to talk with 3rd server
        for i in range(300): # wait 3 seconds
            # check task status
            # if finished, then break
            time.sleep(0.01)
        # if not finished, inform user

Это правильный подход? Или есть лучшее решение?

Upd. Я работаю над ботом Voice Assistant (что-то похожее на Google Assistant), где бот должен ответить в течение 3 секунд. И бот не может сам инициировать ответ, т.е. я не могу дать другой ответ, как только запрос будет выполнен. А так как это голосовой помощник, я не могу дать ссылку. Я думал о следующем подходе - если я могу дать нормальный ответ в течение 3 секунд, то дать его. Если нет - попросите пользователя спросить еще раз с простым словом, как «Статус».

Ответы [ 3 ]

0 голосов
/ 07 октября 2019

Помимо сомнительного пользовательского опыта - медленного ответа и, возможно, не ответа, которого ожидают ваши пользователи - такой подход повышает риск для целостности вашего сервера: при каждом ожидающем запросе ваш сервер будет использовать ресурсы, а в случае высокого входящегопиковые запросы могут выполняться либо по доступному пулу или ресурсам (если они ограничены), либо могут увеличить ваши расходы (например, запуская дополнительные экземпляры, чтобы не отставать от других входящих запросов, если он настроен на автоматическое масштабирование).

Как правило, для запросов, которые могут занимать много времени, лучше сразу отвечать с идентификатором ожидающего запроса и дополнительными URL-адресами, где клиент может проверить состояние запроса и в конечном итоге получить ожидаемый ответ, когда он будет доступен. Ваш сервер позаботится - в фоновом режиме, а не в исходной ветке входящих запросов - о получении ответа от стороннего сервера. Таким образом, вы можете обрабатывать любые запросы, на получение ответа которых может потребоваться много времени, либо от третьей стороны, либо от внутренней обработки.

0 голосов
/ 09 октября 2019

Вы общаетесь с этим сторонним сервером через http? Вы можете просто использовать параметр timeout в запросах Python?

class MainReply(webapp2.RequestHandler):
    def get(self):
        # do something
        try:
            r = requests.post(... , timeout=3)
            # finished, inform user
        except requests.exceptions.Timeout:
            # not finished, inform user
0 голосов
/ 07 октября 2019

Основное правило: вы никогда не должны создавать пул, как описано в вашем посте, потому что (1) вы «вручную» блокируете больше ресурсов, чем необходимо (2) вы путаете код, имитирующий асинхронный код, который на самом деле не является, и (3)Языки, как правило, предоставляют инструменты для параллельного программирования, которые специально предназначены для задач такого типа, и это делает работу для вас гораздо лучше, чем вы могли бы ее реализовать (в большинстве случаев).

Следуя вашим требованиям. Я написал простую программу, которую вы можете проверить. Проверьте здесь для asyncio.wait_for документации. Найдите здесь также несколько полезных руководств по асинхронности.

Скопируйте код в файл и запустите $ python [filename]

import asyncio
import random
from concurrent.futures._base import TimeoutError

async def safe_get():
    result = "I'm currently out of my office."
    try:
        result = await asyncio.wait_for(get(), 3)
    except TimeoutError as e:
        print("Responder thought too much about his answer")
    return result

async def get(a=1,b=6):
    random_time = random.randint(a,b)
    await asyncio.sleep(random_time)
    return "I'm doing good thanks! Sorry it took me " + str(random_time) + "s to answer."

async def main():
    # Toggle comment on the two following lines to see (1) unwanted behaviour and (2) wanted behaviour
    # response = await get()
    response = await safe_get()
    print(response)

if __name__ == "__main__":
    asyncio.run(main())

...