【问题标题】:publish a message to a sns topic from lambda从 lambda 向 sns 主题发布消息
【发布时间】:2020-07-08 07:17:22
【问题描述】:

我有一个通过 AWS lambda 函数处理 POST 请求的 lambda 函数。它处理 post 请求的正文并进行查询并返回响应。

我的 LAMBDA 函数

const { Pool, Client } = require("pg");
const userName = 'blah';
const hostEndPoint = 'blah';
const databaseType = 'blahblah';
const pwd = 'pass pass';
const portNumber = 5432;
var AWS = require('aws-sdk');

const pool = new Pool({
  user: userName,
  host: hostEndPoint,
  database: databaseType,
  password: pwd,
  port: portNumber
});


exports.handler = async (event) => {
  let body = JSON.parse(event.body);
  let name = body.name;
  let money = body.money;
  let todayDate = new Date();
  var status = 0;
  let text = 'INSERT INTO employee(name, date, salary) VALUES($1, $2, $3) RETURNING *';
  let values = [name, todayDate, money];
  var message = '';
  var status = 0;

  try {
    const res = await pool.query(text, values)
    message += 'successful'
    status = 200;
  } catch (err) {
    message += 'unsuccessful'
    if (err.code === '23505') {
      status = 406;
    }
  }

  var params = {
    Message: 'Hello From Lambda', /* required */
    TopicArn: 'arn:aws:sns:us-east-1:blahblahblah'
  };

  // Create promise and SNS service object
  var publishTextPromise = new AWS.SNS({ apiVersion: '2010-03-31' }).publish(params).promise();

  publishTextPromise.then(
    function (data) {
      console.log(`Message ${params.Message} send sent to the topic ${params.TopicArn}`);
      console.log("MessageID is " + data.MessageId);
    }).catch(
      function (err) {
        console.error(err, err.stack);
      });



  const response = {
    statusCode: status,
    body: JSON.stringify(message),
    headers: {
      "Access-Control-Allow-Origtin": '*'
    }
  };
  return response;
};

我的 Lambda 资源政策看起来像这样

{
  "Version": "2012-10-17",
  "Id": "default",
  "Statement": [
    {
      "Sid": "blah-blah-blah-blah",
      "Effect": "Allow",
      "Principal": {
        "Service": "apigateway.amazonaws.com"
      },
      "Action": "lambda:InvokeFunction",
      "Resource": "arn:aws:lambda:us-east-1:blah-blah-blah-blah-blah",
      "Condition": {
        "ArnLike": {
          "AWS:SourceArn": "arn:aws:execute-api:us-east-1:blahblahblahblha:blah/*/POST/"
        }
      }
    }
  ]
}

我还创建了一个 SNS 主题。如果我的状态为 200,我想向 SNS 主题发布一条消息。所以在我返回响应之前。我想做这样的事情。

if (status === 200){
  pubish some message to my sns topic
}

我对 aws 非常陌生,希望能获得一些关于如何发布消息的指导。我觉得我很亲近。我确实遇到过这个topic,但这让我很困惑,因为我正在编辑 lambda 函数中的代码,所以为什么我需要 aws-sdk,而且他们也没有谈论更改权限。

尝试以下建议的答案后更新代码

【问题讨论】:

    标签: node.js amazon-web-services aws-lambda amazon-sns


    【解决方案1】:

    您可以看到,return response; 行将在publish 没有完成他们的任务时立即运行 - 向 SNS 发布消息。因为publishpublishTextPromise.thenreturn 只是同步代码,所以只需一个滴答声(~0,00..1 秒)即可完成。当处理函数调用return时,该函数将完成,这意味着所有正在执行的任务将被取消(包括publish进程,该进程需要太多时间(~一个tick才能完成) .

    您将async/await 语法与promise 语法(.then .catch)混合使用,那么进程将无法按照您的想法(或想要)工作,我建议您尽可能使用async/await 语法。

    使用您的代码,我邀请publish 任务不会影响响应,它只是尝试向SNS 发布消息。 我的建议,改变

      publishTextPromise.then(
        function (data) {
          console.log(`Message ${params.Message} send sent to the topic ${params.TopicArn}`);
          console.log("MessageID is " + data.MessageId);
        }).catch(
          function (err) {
            console.error(err, err.stack);
          });
    

      await publishTextPromise // wait until the task done
        .then((data) => {
          console.log(`Message ${params.Message} send sent to the topic ${params.TopicArn}`);
          console.log("MessageID is " + data.MessageId);
        })
        .catch((err) => {
          console.error(err, err.stack);
        });
    

    已添加await关键字,我更喜欢箭头函数语法

    【讨论】:

      【解决方案2】:

      我假设你的架构看起来像这样:

      API-Gateway 接受请求并调用您向我们展示的 lambda 函数。 该 Lambda 函数连接到数据库并插入记录。您现在希望此 Lambda 函数也发布到 SNS 主题。

      要做到这一点,你需要做两件事:

      1. 授予 Lambda 函数对 SNS 主题的权限
      2. 编写代码向主题发布消息

      步骤 1 应该首先出现,为此您需要编辑您的函数正在使用的 IAM 角色。 IAM 角色指定允许此 lambda 函数调用哪些 AWS 服务。您向我们展示的 Lambda 资源策略授予 API Gateway 调用/调用您的 Lambda 函数的权限 - 这对函数可以执行的操作没有影响。

      如果这只是为了测试,您可以在身份和访问管理中找到该功能的 IAM 角色并附加 AmazonSNSFullAccess 策略 - 不要对任何类型的生产环境执行此操作,这会带来更多好处不必要的许可(在生产环境中,您将添加一个自定义策略,仅允许在您的主题上执行sns:Publish 操作)。

      现在您的函数有权向您的主题发布消息。

      第 2 步表示您需要编辑代码。有必要按照documentation you linked 中的说明导入 AWS 开发工具包,因为您希望代码与 AWS 服务交互 - 为此您需要开发工具包。除了设置区域之外,该文档中的其他步骤似乎是合理的,如果您的主题与您的 lambda 函数位于同一 AWS 区域中,则您不需要这样做。

      一些额外的观察/建议:

      • 您的 CORS-Header Access-Control-Allow-Origtin 中有错字 - 它应该是 Origin。您也可以在 API Gateway 上进行设置,这样您的函数就不必处理它了。
      • 我建议您从 AWS Secrets Manager 或 Systems Manager Parameter Store 之类的地方获取数据库凭证,而不是将它们存储在代码中
      • 通过在处理程序之外设置连接池,您可以让您的代码重新使用现有的 DB-Connections,干得好!

      【讨论】:

      • 感谢您如此精彩而详细的回复以及额外的指示。所以我去了 IAM 并为我的 lambda 角色添加了 amazonsnsfullaccess 策略。然后我按照第 2 步添加了将消息发布到 sns 的代码,但是当我执行我的 lambda 时,它运行成功,但它没有发布消息,而且我的 cloudwatch 中也看不到任何内容。这让我很困惑。
      • 您能否将更新后的代码添加为原始问题下方的编辑?
      • 是的,刚刚更新了,你可以看看,我觉得它甚至没有点击 sns 发布可能是它的一些 JS 问题?
      • 无论如何,我都不是 JS 专家,但我认为代码创建了对 SNS 的异步请求,并且在继续向用户返回响应之前不会等待响应。这可能会导致函数在返回响应后被冻结,而不是实际调用 SNS。因此,您可能需要在publishTextPromise.then 结果处理程序中包含您的响应逻辑或执行其他一些异步/等待魔法,但我在这里超出了我的深度;-)
      猜你喜欢
      • 2021-02-15
      • 2021-09-14
      • 2019-11-05
      • 2018-04-21
      • 2022-01-27
      • 2022-01-03
      • 2021-10-18
      • 1970-01-01
      • 2018-04-09
      相关资源
      最近更新 更多