从NodeJS中的AWS SQS获取所有消息

时间:2019-05-01 09:54:51

标签: node.js amazon-web-services amazon-sqs

我具有以下功能,可从aws SQS获取消息,问题是我一次收到一个消息,并且希望获取所有消息,因为我需要检查每条消息的ID:

function getSQSMessages() {

    const params = {
        QueueUrl: 'some url',
    };

    sqs.receiveMessage(params, (err, data) => {
        if(err) {
            console.log(err, err.stack)
            return(err);
        }
        return data.Messages;
    });

};

function sendMessagesBack() {

    return new Promise((resolve, reject) => {
        if(Array.isArray(getSQSMessages())) {
            resolve(getSQSMessages());
        } else {
            reject(getSQSMessages());
        };
    });

};

函数 sendMessagesBack()用于另一个异步/等待函数。 我不确定如何获取所有消息,因为我一直在寻找如何获取消息,人们提到了循环,但是我无法弄清楚如何在我的情况下实现它。 我假设必须将 sqs.receiveMessage()放入循环中,但是随后我对需要检查的内容以及何时停止循环感到困惑,这样我才能获得每条消息的ID?

如果有人有任何提示,请分享。 谢谢。

4 个答案:

答案 0 :(得分:2)

我建议您使用Promise api,它将使您可以立即使用async / await语法。

const { Messages } = await sqs.receiveMessage(params).promise();
// Messages will contain all your needed info
await sqs.sendMessage(params).promise();

通过这种方式,您将不需要用Promises包装回调API。

答案 1 :(得分:2)

SQS在响应中返回的消息不超过10条。要获取所有可用消息,您需要递归调用getSQSMessages函数。 如果您从getSQSMessages返回承诺,则可以执行以下操作。

getSQSMessages()
.then(data => {
  if(!data.Messages || data.Messages.length === 0){
      // no messages are available. return
  }
  // continue processing for each message or push the messages into array and call 
 //getSQSMessages function again. 
});

答案 2 :(得分:1)

我们永远不能保证将所有消息都放入队列中,除非获得其中的一部分,然后将它们从队列中删除-这样才能确保下一个请求返回不同的记录选择。

每个请求将返回“最多” 10条消息,如果您不将其删除,则很有可能下一个对“最多” 10条消息的请求将返回您已经看到的消息的混合,还有一些新的-因此您永远不会真正知道何时看到它们。

队列可能不是在您的用例中使用的正确工具-但由于我不知道您的用例,因此很难说。

答案 3 :(得分:0)

我知道这有点死机,但我昨晚在尝试从 SQS 的死信队列中提取一些所有消息时来到这里。虽然接受的答案,“你不能保证从队列中获取所有消息”是绝对正确的,但我确实想为任何可能登陆这里并且需要绕过每个请求的 10 消息限制的人提供一个答案来自 AWS。

依赖项

就我而言,我的项目中已经有一些依赖项,我过去常常使用它们来简化工作。

  • lodash - 这是我们在代码中使用的东西,以帮助使事情发挥作用。我认为我没有在下面使用它,但我将它包含在文件中。
  • cli-progress - 这为您的 CLI 提供了一个不错的小进度条。

免责声明

在对与另一个系统集成的一些生产错误进行故障排除时,将以下内容放在一起。我们的 DLQ 消息包含我需要的一些标识符,以便制定云监视查询以进行故障排除。鉴于这是 AWS 中的两个不同 GUI,来回切换很麻烦,因为我们的 AWS 会话是通过一种联合形式进行的,并且会话最多只能持续一个小时。

脚本

#!/usr/bin/env node

const _ = require('lodash');
const aswSdk = require('aws-sdk');
const cliProgress = require('cli-progress');

const queueUrl = 'https://[put-your-url-here]';
const queueRegion = 'us-west-1';

const getMessages = async (sqs) => {
  const resp = await sqs.receiveMessage({
    QueueUrl: queueUrl,
    MaxNumberOfMessages: 10,
  }).promise();

  return resp.Messages;
};

const main = async () => {
  const sqs = new aswSdk.SQS({ region: queueRegion });

  // First thing we need to do is get the current number of messages in the DLQ. 
  const attributes = await sqs.getQueueAttributes({
    QueueUrl: queueUrl,
    AttributeNames: ['All'], // Probably could thin this down but its late
  }).promise();

  const numberOfMessage = Number(attributes.Attributes.ApproximateNumberOfMessages);

  // Next we create a in-memory cache for the messages
  const allMessages = {};
  let running = true;

  // Honesty here: The examples we have in existing code use the multi-bar. It was about 10PM and I had 28 DLQ messages I was looking into. I didn't feel it was worth converting the multi-bar to a single-bar. Look into the docs on the github page if this is really a sticking point for you.
  const progress = new cliProgress.MultiBar({
    format: ' {bar} | {name} | {value}/{total}',
    hideCursor: true,
    clearOnComplete: true,
    stopOnComplete: true
  }, cliProgress.Presets.shades_grey);
  const progressBar = progress.create(numberOfMessage, 0, { name: 'Messages' });

  // TODO: put in a time limit to avoid an infinite loop. 
  // NOTE: For 28 messages I managed to get them all with this approach in about 15 seconds. When/if I cleanup this script I plan to add the time based short-circuit at that point.
  while (running) {
    // Fetch all the messages we can from the queue. The number of messages is not guaranteed per the AWS documentation. 
    let messages = await getMessages(sqs);
    for (let i = 0; i < messages.length; i++) {
      // Loop though the existing messages and only copy messages we have not already cached.
      let message = messages[i];
      let data = allMessages[message.MessageId];
      if (data === undefined) {
        allMessages[message.MessageId] = message;
      }
    }

    // Update our progress bar with the current progress
    const discoveredMessageCount = Object.keys(allMessages).length;
    progressBar.update(discoveredMessageCount);

    // Give a quick pause just to make sure we don't get rate limited or something
    await new Promise((resolve) => setTimeout(resolve, 1000));
    running = discoveredMessageCount !== numberOfMessage;
  }

  // Now that we have all the messages I printed them to console so I could copy/paste the output into LibreCalc (excel-like tool). I split on the semicolon for rows out of habit since sometimes similar scripts deal with data that has commas in it.
  const keys = Object.keys(allMessages);
  console.log('Message ID;ID');
  for (let i = 0; i < keys.length; i++) {
    const message = allMessages[keys[i]];
    const decodedBody = JSON.parse(message.Body);
    console.log(`${message.MessageId};${decodedBody.id}`);
  }
};

main();