如何使用python asyncio从EC2实例调用AWS Lambda函数

Ber*_*one 1 amazon-web-services python-3.x python-asyncio boto3 aws-lambda

我最近发布了一个有关如何仅允许从VPC内的EC2实例调用AWS Lambda函数的问题。通过将带有“ AWS lambda角色”策略的IAM角色附加到EC2实例,我设法使其正常工作,现在我可以使用boto3调用lambda函数。

现在,我想使用asyncio await语法异步调用lambda函数。我读到lambda函数通过设置提供了一个异步版本InvokeType='Event',但是实际上使调用立即返回而没有得到函数的结果。

由于函数需要一些时间,因此我想并行启动许多函数,因此我想避免在等待函数返回时阻塞执行。

我尝试使用aiobotocore,但这仅支持基本的“ s3”服务功能。

解决这个问题的最佳方法(谦虚)是使用AWS API Gateway服务通过GET / POST请求调用lambda函数,该请求可以使用aiohttp轻松处理。

但是,我无法使其正常运行。

我在EC2 IAM角色中添加了策略“ AmazonAPIGatewayInvokeFullAccess”,但每次尝试执行以下操作:

import requests
r = requests.get('https://url_to_api_gateway_for_function')
Run Code Online (Sandbox Code Playgroud)

我得到禁止的回应<Response [403]>

我直接使用lambda函数中的触发器创建了API网关。

我还尝试编辑API网关设置,方法是在函数路径中添加post方法并设置“ AWS_IAM”身份验证,然后将其部署为“ prod”部署。仍然是相同的禁止响应。当我通过“ API网关上的测试屏幕”对其进行测试时,它可以正常工作。

任何想法如何解决这个问题?我错过了一步吗?

Ber*_*one 5

经过一些努力,我设法解决了我的问题。

问题在于,curl和python模块(例如python的请求)不会使用运行它们的EC2计算机的IAM凭据对http请求进行签名。对AWS GATEWAY API的http请求必须使用AWS v4登录协议进行签名。

此处是一个示例:http : //docs.aws.amazon.com/general/latest/gr/sigv4-signed-request-examples.html

幸运的是,为了简单起见,有一些帮助器模块,例如request-aws-sign:https : //github.com/jmenga/requests-aws-sign

最后,代码可能类似于:

import aiohttp
import asyncio
from requests_aws_sign import AWSV4Sign
from boto3 import session

session = session.Session()
credentials = session.get_credentials()
region = session.region_name or 'ap-southeast-2'
service = 'execute-api'
url = "get_it_from_api->stages->your_deployment->invoke_url"
auth=AWSV4Sign(credentials, region, service)

async def invoke_func(loop):
    async with aiohttp.request('GET', url, auth=auth, loop=loop) as resp:
        html = await resp.text()
        print(html)

loop = asyncio.get_event_loop()
loop.run_until_complete(main(loop))
Run Code Online (Sandbox Code Playgroud)

希望这可以节省其他人的时间!

编辑:

为了完整性和帮助他人,我不得不说,由于requests_aws_sign与aiohttp不兼容的事实,上面的代码不起作用。我收到一些“验证字段错误”。

我试图通过使用以下方法解决它:

async with session.get(url, headers=update_headers()) as resp:
Run Code Online (Sandbox Code Playgroud)

其中update_headers()是一个简单的函数,用于模仿requests_aws_sign对标头所做的操作(这样,我就可以使用header参数将它们直接设置为上述请求)。看起来像这样:

def update_headers(sim_id):
    url = urlparse("get_it_from_api->stages->your_deployment->invoke_url")
    path = url.path or '/'
    querystring = ''
    if url.query:
        querystring = '?' + urlencode(parse_qs(url.query), doseq=True)
    safe_url = url.scheme + '://' + url.netloc.split(':')[0] + path + querystring
    request = AWSRequest(method='GET', url=safe_url)
    SigV4Auth(credentials, service, region).add_auth(request)
    return dict(request.headers.items())
Run Code Online (Sandbox Code Playgroud)