当我从SNS主题订阅时,SQS AWS消息错误

时间:2020-04-24 14:15:40

标签: amazon-web-services aws-lambda amazon-sqs amazon-sns

我对下一个设计有疑问:

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 Lambda(示例代码)引发异常,因为收到的de消息的结构不是SQS事件:

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 :(得分:0)

在我看来,这没有得到很好的记录,但是一旦弄清楚,还不错。

第一件事是我不使用预定义的Lambda对象。我将所有内容读入String并从那里获取。因此,我的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();
}

当您从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");