Неправильное сообщение SQS AWS при подписке на тему SNS - PullRequest
2 голосов
/ 24 апреля 2020

У меня проблемы со следующим дизайном:

enter image description here

Когда я получаю сообщение в моем подписчике SQS, модель сообщения это неправильно, пример:

{
  "Type" : "Notification",
  "MessageId" : "7a6789f0-02f0-5ed3-8a11-deebcd08f145",
  "TopicArn" : "arn:aws:sns:us-east-2:167186109795:name_sns_topic",
  "Message" : "My JSON message",
  "Timestamp" : "1987-04-23T17:17:44.897Z",
  "SignatureVersion" : "1",
  "Signature" : "string",
  "SigningCertURL" : "url",
  "UnsubscribeURL" : "url",
  "MessageAttributes" : {
    "X-Header1" : {"Type":"String","Value":"value1"},
    "X-Header2" : {"Type":"String","Value":"value2"},
    "X-Header3" : {"Type":"String","Value":"value3"},
    "X-HeaderN" : {"Type":"String","Value":"value4"}
  }
}

Общая модель при получении сообщения от SQS должна быть:

{
  "Records": [
    {
      "messageId": "19dd0b57-b21e-4ac1-bd88-01bbb068cb78",
      "receiptHandle": "MessageReceiptHandle",
      "body": "Hello from SQS!",
      "attributes": {
        "ApproximateReceiveCount": "1",
        "SentTimestamp": "1523232000000",
        "SenderId": "123456789012",
        "ApproximateFirstReceiveTimestamp": "1523232000001"
      },
      "messageAttributes": {},
      "md5OfBody": "7b270e59b47ff90a553787216d55d91d",
      "eventSource": "aws:sqs",
      "eventSourceARN": "arn:{partition}:sqs:{region}:123456789012:MyQueue",
      "awsRegion": "{region}"
    }
  ]
}

В моем обработчике Java Лямбда (пример кода) выдает исключение, потому что структура полученного сообщения не является SQS Event:

public class MyHandler implements RequestHandler<SQSEvent, String> {

  @Override
  public String handleRequest(SQSEvent event, Context context) {
    LambdaLogger logger = context.getLogger();

    for (SQSEvent.SQSMessage msg : event.getRecords()) {
      logger.log("SQS message body: " + msg.getBody());
      logger.log("Get attributes: " + msg.getMessageAttributes().toString());

      msg.getMessageAttributes()
              .forEach(
                      (k, v) -> {
                        logger.log("key: " + k + "value: " + v.getStringValue());
                      });
    }
    return "Successful";
  }
}

How can I do for handle the message thats its receiving ?

1 Ответ

0 голосов
/ 25 апреля 2020

По моему мнению, это не слишком хорошо задокументировано, но это не плохо, когда вы это выясните.

Первое, что я не использую предопределенные объекты Lambda. Я читаю все в строку и беру оттуда. Итак, основа моей функции Lamda:

public void handleRequest(InputStream inputStream, OutputStream outputStream, Context context) throws IOException {
    // copy InputStream to String, avoiding 3rd party libraries
    ByteArrayOutputStream result = new ByteArrayOutputStream();
    byte[] buffer = new byte[1024];
    int length;
    while ((length = inputStream.read(buffer)) != -1) {
        result.write(buffer, 0, length);
    }

    String jsonString = result.toString();
}

Когда вы "go direct" из SNS в Lambda, сообщение выглядит примерно так (некоторые поля удалены ради длины):

{
    "Records": [
        {
            "EventSource": "aws:sns",
            "EventVersion": "1.0",
            "Sns": {
                "Type": "Notification",
                "Subject": "the message subject",
                "Message": "{\"message\": \"this is the message\", \"value\": 100}",
                "Timestamp": "2020-04-24T21:44:28.220Z",
                "SignatureVersion": "1"
            }
        }
    ]
}

Я отправил тестовое сообщение в JSON с двумя простыми полями. Используя JsonPath , поле «сообщения» внутри всего читается следующим образом:

String snsMessage = JsonPath.read(jsonString, "$.Records[0].Sns.Message");
String realMessage = JsonPath.read(snsMessage, "$.message");

Но когда оно идет SNS -> SQS -> Lambda (или, действительно, любой путь SNS -> SQS ) сообщение SNS теперь в основном упаковано и экранировано в сообщении SQS:

{
    "Records": [
        {
            "messageId": "ca8c53e5-8417-4479-a720-d4ecf970ca68",
            "body": "{\n  \"Type\" : \"Notification\",\n  \"Subject\" : \"the message subject\",\n  \"Message\" : \"{\\\"message\\\": \\\"this is the message\\\", \\\"value\\\": 100}\"\n}",
            "attributes": {
                "ApproximateReceiveCount": "1"
            },
            "md5OfBody": "6a4840230aca6a7bf7934bf191a529b8",
            "eventSource": "aws:sqs"
        }
    ]
}

Таким образом, в этом случае значение находится в Records[0].body, но содержит другой объект JSON. Я признаю, что, вероятно, есть более простой способ, но из того, что я нашел, мне пришлось проанализировать 3 раза:

String sqsBody = <as read in lambda>;
String recordBody = JsonPath.read(sqsBody, "$.Records[0].body");
String internalMessage = JsonPath.read(recordBody, "$.Message");
// now read out of the sns message
String theSnsMessage = JsonPath.read(message, "$.message");
...