Использование boto для вызова лямбда-функций, как это сделать асинхронно?

Я использую boto для вызова своих лямбда-функций и проверки моего бэкэнда. Я хочу вызвать их асинхронно. Я отметил, что "invoke_async" устарел и не должен использоваться. Вместо этого вы должны использовать "invoke" с InvocationType "Event" для выполнения функции асинхронно.

Я не могу понять, как получить ответы от функций, когда они вернутся. Я пробовал следующее:

payload3=b"""{
"latitude": 39.5732160891,
"longitude": -119.672918997,
"radius": 100
}"""

client = boto3.client('lambda')
for x in range (0, 5):
    response = client.invoke(
        FunctionName="loadSpotsAroundPoint",
        InvocationType='Event',
        Payload=payload3
    )
    time.sleep(15)
    print(json.loads(response['Payload'].read()))
    print("\n")

Даже если я скажу, что код спал в течение 15 секунд, переменная ответа по-прежнему пуста, когда я пытаюсь ее распечатать. Если я изменяю InvokationType invokation на "RequestResponse", все работает отлично, а переменная переменной печатается, но это синхронно. Я что-то пропустил? Как выполнить какой-либо код, например, распечатать результат, когда возвращается асинхронная invocation?

Спасибо.

Ответ 1

Асинхронно выполненная функция AWS Lambda не возвращает результат выполнения. Если запрос асинхронного вызова выполнен успешно (т.е. Не было ошибок из-за разрешений и т.д.), AWS Lambda немедленно возвращает код состояния HTTP 202 ACCEPTED и не несет никакой ответственности за передачу какой-либо информации об итогах этого асинхронного вызова.

Из документации AWS Lambda Invoke action:

Синтаксис ответа

HTTP/1.1 StatusCode
X-Amz-Function-Error: FunctionError
X-Amz-Log-Result: LogResult

Payload

Элементы ответа

Если действие выполнено успешно, служба отправляет обратно следующий HTTP-запрос Ответ.

StatusCode

Код состояния HTTP будет в диапазоне 200 для успешного запроса. Для типа вызова RequestResonse этот код состояния будет 200. Для типа вызова Event этот код состояния будет 202. Для типа вызова DryRun код состояния будет равен 204.

[...]

Ответ возвращает в качестве тела HTTP следующее.

Payload

Это представление JSON объекта, возвращаемого лямбдой функция. Это присутствует только в том случае, если тип вызова RequestResponse.

Ответ 2

Существует различие между "async AWS лямбда-вызовом" и "асинхронным кодом python". Когда вы установите InvocationType на 'Event', по определению, он никогда не отправляет ответ.

В вашем примере invoke() немедленно возвращает None и не подразумевает запуск чего-либо в фоновом режиме для изменения этого значения позднее (спасибо доброте!). Итак, когда вы посмотрите на значение response через 15 секунд, оно все равно None.

Кажется, что вам действительно нужен тип вызова RequestResponse, с асинхронным кодом Python. У вас есть выбор вариантов, но мой любимый concurrent.futures. Другой - threading.

Здесь пример с использованием concurrent.futures:

(Если вы используете Python2, вам нужно pip install futures)

from concurrent.futures import ThreadPoolExecutor
import json

payload = {...}

with ThreadPoolExecutor(max_workers=5) as executor:
    futs = []
    for x in xrange(0, 5):
        futs.append(
            executor.submit(client.invoke,
                FunctionName   = "loadSpotsAroundPoint",
                InvocationType = "RequestResponse",
                Payload        = bytes(json.dumps(payload))
            )
        )
    results = [ fut.result() for fut in futs ]

print results

Еще один шаблон, который вы, возможно, захотите изучить, - это использовать тип вызова Event, и ваша функция Lambda передает сообщения SNS, которые затем потребляются другой функцией Lambda. Вы можете ознакомиться с учебным пособием для функций лямбда-функции, запускаемых SNS здесь.