【问题标题】:Invoking second async function in lambda not invoking在 lambda 中调用第二个异步函数不调用
【发布时间】:2022-01-13 16:51:59
【问题描述】:

我正在使用无服务器来部署 aws lambda。目前我的设置是 api 网关触发一个 lambda,然后我异步调用另一个 lambda。

serverless.yml

service: scraper
useDotenv: true #added this because deprecation notice
frameworkVersion: '2'

provider:
  name: aws
  runtime: nodejs14.x
  lambdaHashingVersion: 20201021
  stage: prod
  region: us-east-2

functions:
  scraperRunner:
    handler: handler.scraperRunner
    provisionedConcurrency: 0
    timeout: 90
    events:
      - http:
          path: process/run
          method: post
          async: true
  runTargetSite:
    handler: handler.runTargetSite

plugins:
 - serverless-offline

然后我有一个 handler.js 文件,我在其中定义了两个处理函数:

'use strict';

const startScraper = require('./crawler/runner');
const AWS = require('aws-sdk');
AWS.config.region = 'us-east-2';
const lambda = new AWS.Lambda();

function processResponse(msg, statusCode, event) {
    return {
        statusCode: statusCode,
        headers: {
            'Access-Control-Allow-Origin': '*',
            'Access-Control-Allow-Headers': 'Content-Type',
            "Access-Control-Allow-Methods": "OPTIONS,POST,GET"
        },
        body: JSON.stringify({
            message: msg,
            input: event
        })
    }
}


module.exports.scraperRunner = async (event) => {
    if (event) {
        const data = event;
        if (data.hasOwnProperty('target_id')) {

            const prov = [<array of objects>]

            for (const p of prov) {
                data['target'] = p.name
                const params = {
                    FunctionName: 'provider-scraper-prod-runTargetSite',
                    InvocationType: 'Event',
                    // LogType: 'Tail',
                    Payload: JSON.stringify(data)
                };

                await lambda.invoke(params).promise()
            }
            return processResponse(
                'Event successfully received',
                200, event)

        }
    }

    return processResponse(
        'Invalid body payload',
        402, event)
};

module.exports.runTargetSite = async function(event, context) {
    await startScraper(event)
    return processResponse(
        'Event successfully received for runTargetSite',
        200, event)
}

我还添加了必要的角色(我认为):

  • AWSLambdaExecute
  • AWSLambdaBasicExecutionRole
  • AWSLambdaRole
  • 调用函数
  • InvokeAsync

使用命令行,我可以调用第一个 lambda,然后调用第二个:

serverless invoke local --function scraperRunner --data '{"target": "_tpp_"}'

但是,当向 API Gateway 端点提交发布请求时,第一个 lambda (scraperRunner) 会触发,但不会调用第二个。

【问题讨论】:

  • 调用实际的 Lambda 而不是本地的会发生什么?
  • 第一个 lambda 通过 post 到 api 网关端点执行。第二个没有执行。
  • lambdas 是否在同一个区域?

标签: node.js aws-lambda aws-sdk-js


【解决方案1】:

当您使用serverless invoke local 命令时,您将直接调用 lambda 函数,事件定义为 '{"target": "tpp"}'。当您通过 API Gateway 调用 Lambda 函数时,API 请求正文不会直接发送到 Lambda 函数。相反,根据您将 AWS API Gateway 与 Lambda 集成的方式,API Gateway 将转换传入请求。这里有两种可能:

  • AWS_PROXY 与 Lambda 集成:Lambda 事件将采用以下格式:
{
  "resource": "/my/path",
  "path": "/my/path",
  "httpMethod": "GET",
  "headers": {
    "header1": "value1",
    "header2": "value2"
  },
  "multiValueHeaders": {
    "header1": [
      "value1"
    ],
    "header2": [
      "value1",
      "value2"
    ]
  },
  "queryStringParameters": {
    "parameter1": "value1",
    "parameter2": "value"
  },
  "multiValueQueryStringParameters": {
    "parameter1": [
      "value1",
      "value2"
    ],
    "parameter2": [
      "value"
    ]
  },
  "requestContext": {
    "accountId": "123456789012",
    "apiId": "id",
    "authorizer": {
      "claims": null,
      "scopes": null
    },
    "domainName": "id.execute-api.us-east-1.amazonaws.com",
    "domainPrefix": "id",
    "extendedRequestId": "request-id",
    "httpMethod": "GET",
    "identity": {
      "accessKey": null,
      "accountId": null,
      "caller": null,
      "cognitoAuthenticationProvider": null,
      "cognitoAuthenticationType": null,
      "cognitoIdentityId": null,
      "cognitoIdentityPoolId": null,
      "principalOrgId": null,
      "sourceIp": "IP",
      "user": null,
      "userAgent": "user-agent",
      "userArn": null,
      "clientCert": {
        "clientCertPem": "CERT_CONTENT",
        "subjectDN": "www.example.com",
        "issuerDN": "Example issuer",
        "serialNumber": "a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1:a1",
        "validity": {
          "notBefore": "May 28 12:30:02 2019 GMT",
          "notAfter": "Aug  5 09:36:04 2021 GMT"
        }
      }
    },
    "path": "/my/path",
    "protocol": "HTTP/1.1",
    "requestId": "id=",
    "requestTime": "04/Mar/2020:19:15:17 +0000",
    "requestTimeEpoch": 1583349317135,
    "resourceId": null,
    "resourcePath": "/my/path",
    "stage": "$default"
  },
  "pathParameters": null,
  "stageVariables": null,
  "body": "Hello from Lambda!",
  "isBase64Encoded": false
}
  • AWS 与 Lambda 集成:Lambda 事件取决于定义的集成请求模板。模板决定如何将传入请求转换为 Lambda 事件。

您当前的代码假定 API 请求正文等同于事件,但事实并非如此。因此,if (data.hasOwnProperty('target_id')) 为 false,我们从不输入 if 条件。

在您的 serverless.yaml 中,您已将 lambda 函数事件触发器定义为:

    events:
      - http:
          path: process/run
          method: post
          async: true

来自serverless framework documentation

在使用事件集成 lambda 函数时使用 async: true 调用。这让 API Gateway 立即返回 200 lambda 继续运行时的状态代码。如果不是,否则 指定的集成类型将是 AWS。

请注意,它表示指定的集成类型将是 AWS。 Serverless Framework 自动添加集成请求模板,请求正文格式如下(Reference):

{
  "body": {},
  "method": "GET",
  "principalId": "",
  "stage": "dev",
  "cognitoPoolClaims": {
    "sub": ""
  },
  "enhancedAuthContext": {},
  "headers": {
    "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8",
    "Accept-Encoding": "gzip, deflate, br",
    "Accept-Language": "en-GB,en-US;q=0.8,en;q=0.6,zh-CN;q=0.4",
    "CloudFront-Forwarded-Proto": "https",
    "CloudFront-Is-Desktop-Viewer": "true",
    "CloudFront-Is-Mobile-Viewer": "false",
    "CloudFront-Is-SmartTV-Viewer": "false",
    "CloudFront-Is-Tablet-Viewer": "false",
    "CloudFront-Viewer-Country": "GB",
    "Host": "ec5ycylws8.execute-api.us-east-1.amazonaws.com",
    "upgrade-insecure-requests": "1",
    "User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36",
    "Via": "2.0 f165ce34daf8c0da182681179e863c24.cloudfront.net (CloudFront)",
    "X-Amz-Cf-Id": "l06CAg2QsrALeQcLAUSxGXbm8lgMoMIhR2AjKa4AiKuaVnnGsOFy5g==",
    "X-Amzn-Trace-Id": "Root=1-5970ef20-3e249c0321b2eef14aa513ae",
    "X-Forwarded-For": "94.117.120.169, 116.132.62.73",
    "X-Forwarded-Port": "443",
    "X-Forwarded-Proto": "https"
  },
  "query": {},
  "path": {},
  "identity": {
    "cognitoIdentityPoolId": "",
    "accountId": "",
    "cognitoIdentityId": "",
    "caller": "",
    "apiKey": "",
    "sourceIp": "94.197.120.169",
    "accessKey": "",
    "cognitoAuthenticationType": "",
    "cognitoAuthenticationProvider": "",
    "userArn": "",
    "userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_5) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36",
    "user": ""
  },
  "stageVariables": {},
  "requestPath": "/request/path"
}

async: true 导致每个 API 请求没有响应正文,只有状态代码 200。查看您的代码,我不确定您是否希望异步调用 Lambda,因为您正在代码中定义响应。如果您决定从 serverless.yaml 中删除 async: true,无服务器框架将切换到使用 AWS_PROXY 集成,因此您的 lambda 事件格式会有所不同。

在这两种情况下,您都应该能够通过更改来解决您的问题:

const data = event;

const data = JSON.parse(event.body);

在本地测试时:

serverless invoke local --function scraperRunner --data '{"body":"{\"target\": \"_tpp_\"}"}'

此外,如果您尚未使用 AWS Lambda 的 Amazon CloudWatch 日志记录,请查看它。这在默认情况下使用为 Lambda 函数定义的正确 IAM 权限启用,在调试已部署应用程序上的任何问题时非常有用。

【讨论】:

    猜你喜欢
    • 2022-01-07
    • 1970-01-01
    • 2019-02-12
    • 2020-03-04
    • 2021-06-04
    • 1970-01-01
    • 1970-01-01
    • 2020-03-05
    • 2016-09-18
    相关资源
    最近更新 更多