AWS SNS 订阅到 HTTP 的过程及消息报文

AWS SNS(Simple Notification Service) 以消息订阅,推送的方式对组件进行解藕。当有新消息发送到 SNS 主题中,SNS 会向当前所有的订阅者发送一个消息(广播),它本身不像 SQS 那样会存储消息,而只是一个纯粹的消息路由。SNS 消息可以订阅到 Amazon Kinesis Data Firehose, SQS, Lambda, Email, Email-JSON, HTTP, HTTPs, Platform application endpoint, 和 SMS。同邮件列表一样,订阅 SNS 消息也是需要确认的,不然 SNS 消息就可能恶意满天飞。

本文试验如何用 HTTP 端点订阅 SNS 消息,订阅确认,以及发送消息到 SNS 主题后消息推送到 HTTP 端点的细节,重点是了解订阅及被推送过来消息时的 HTTP 报文内容。SNS 的 HTTP 端点订阅需要一个公网上的 HTTP URL, 对 SNS 可见,所以我在本地测试时在家中路由器上加一个端口映射,对 Modem 获得的公有 IP 的 8080 端口访问转发到写此文用所用机器的 8080 端口上。

在本机需要在 8080 端口上启动一个 HTTP 服务以迎接 AWS 消息的到来,比如用 python 3 的话,简单运行命令 python -m http.server 8080。如果不想在 API 代码中分析 HTTP 报文数据,只需打开 Wireshark(过滤条件 tcp.port=8080 && http) 抓取 8080 上的 HTTP 数据通信即可。在 API 代码中如何处理 HTTP 请求数据不是本文的重点。

为方便起见,暂时设定了一个 DNS A 记录  sns.yanbin.blog 指向到 Modem 的 8080,拟定 HTTP 端点为 http://sns.yanbin.blog:8080/。

SNS 到 HTTP 端点的订阅与确认

首先在 Amazon 中创建一个 SNS topic, 命名为 test-topic,然后创建订阅时选择协议为 HTTP,在 Endpoint 中填入 "http://sns.yanbin.blog:8080/", 这里有个选项 "Enable raw message delivery", 先不勾选, 后面讲对比选与不选该项的不同; 在点击 "Create subscription" 按钮之前打开 Wireshark 准备捕获从 AWS 过来的报文。

现在点击 "Create subscription" 按钮,这时候在 "http://sns.yanbin.blog:8080/" 端上收到一个来自 AWS 的 HTTP POST 请求,它的 HTTP 协议文本是

现在 SNS 主题 test-topic 上有一个待确认的订阅

 

在这个界面上可以选择这个 Pending 的订阅,进行 "Confirm subscription", 确认时需要填入 "Subscription confirmation url", 就是在点击 "Create subscription" 按钮时收到 HTTP 请求中的 "SubscribeURL", 其中包含 Token。

或者直接访问 "SubscribeURL": "https://sns.us-east-1.amazonaws.com/?Action=ConfirmSubscription&TopicArn=arn:aws:sns:us-east-1:0123456789012:test-topic&Token=2336412f37fb687f5d51e6e2425c464de7ccff1bc58035297fde704abfc2c9ef4c5ba9ebca38627878248a51e55f6ad10793b1a7945993c8556591ca55772e787de1a6f2d06b27a4ff2a9e66db529fa4b317306d6587efa7341b550a811ee95d21091ab4ac99dddc2de3496837e2ebde" 立即进行订阅的确认,GET 或 POST 随意。

以下 curl 命令后的 URL 中 ?= , & 需要用 \ 进行转义

$ curl https://sns.us-east-1.amazonaws.com/\?Action\=ConfirmSubscription\&TopicArn\=arn:aws:sns:us-east-1:0123456789012:test-topic\&Token\=2336412f37fb687f5d51e6e2425c464de7ccff1bc58035297fde704abfc2c9ef4c5ba9ebca38627878248a51e55f6ad10793b1a7945993c8556591ca55772e787de1a6f2d06b27a4ff2a9e66db529fa4b317306d6587efa7341b550a811ee95d21091ab4ac99dddc2de3496837e2ebde
<ConfirmSubscriptionResponse xmlns="http://sns.amazonaws.com/doc/2010-03-31/">
  <ConfirmSubscriptionResult>
    <SubscriptionArn>arn:aws:sns:us-east-1:0123456789012:test-topic:0cc7bbc1-e16f-457e-a6d1-2183eddbe277</SubscriptionArn>
  </ConfirmSubscriptionResult>
  <ResponseMetadata>
    <RequestId>058113b3-8c26-5a9a-aed7-4fc3763f869b</RequestId>
  </ResponseMetadata>
</ConfirmSubscriptionResponse>

"Request confirmation" 按钮可重新发送 Type: SubscriptionConfirmation 的消息到 HTTP 端点,以防我们丢失了之前确认订阅用的 "SubscribeURL"

订阅确认后就变得可用

理解了这个过程之后,我们在实现 http://sns.yanbin.blog:8080/ API 时,当收到 AWS 过来的特定条件的 SNS "SubscriptionConfirmation" 消息后可以立马访问 "SubscribeURL" 来完成订阅的确认。

懂得了发生成后面的对话过程,无论是用 AWS CLI 还是 Terraform 来完成订阅与确认过程就好理解了,比如用 AWS CLI 的订阅过程是

$ aws sns subscribe --topic-arn arn:aws:sns:us-east-1:0123456789012:test-topic --protocol http --notification-endpoint http://sns.yanbin.blog:8080/
{
    "SubscriptionArn": "pending confirmation"
}
$ aws sns confirm-subscription --topic-arn arn:aws:sns:us-east-1:0123456789012:test-topic --token 2336412f37fb687f5d51e6e2425c464de7ccff1bc......
{
    "SubscriptionArn": "arn:aws:sns:us-east-1:0123456789012:test-topic:0cb76e08-16fa-422b-a5da-02d59501a720"
}

 Token 就是订阅时 POST 到 notification-endpoint 去 SubscribeURL 中的 token

SNS 向 HTTP 端点推着消息

HTTP 订阅确认后,我们现在往主题 test-topic 中发送一条消息来观察 http://sns.yanbin.blog:8080/ 上会收到什么 HTTP 报文。直接在 AWS SNS Web 控制台,选择 test-topic 主题,点击按钮 "Publish message"

  1. Subject: "test subject"
  2. Message body: "test message body"
  3. Message attributes: 加一条 "Type: String, Name: key1, Value: value1" 的属性条目

最后点击 "Publish message" 按钮,来到 Wireshark, 收到一个 POST http://sns.yanbin.blog:8080/ 请求, 报文为

标准的消息推着,有消息就调用一下订阅时设定的 HTTP 端点, post body 是一个 JSON, Type 为 "Notification", 包含有完整的消息内容,并且带上了友好的退订链接。

上面是订阅是未勾选 "Enable raw message delivery" 选项,我们可以修改现有的订阅,把该选项勾上,然后重新发送一条相同的消息内容,对应的 HTTP 报文是

收到一条只有消息体的 SNS 消息,没有 subject, 还有其他的 Message Attribute 也都被忽略掉了。

为了单一 HTTP 端点能够根据消息 Type 的类型进行复用,还是别勾选上 "Enable raw message delivery" 为好,当然某些时候该选项还是有意义的。

进行了上面的分析后,订阅 SNS 的 HTTP endpoint 中怎么无需多讲了,本文也就此告结了。

最后附一个 S3 Event 到 SNS topic, 然后订阅到 HTTP 的消息样例

S3 的消息以字符串形式包裹在 SNS 消息的 "Message" 体中,如果选择了 "Enable raw message delivery",将只有 S3 的消息部分,即 "{Records:[{...."

本文链接 https://yanbin.blog/aws-sns-subscribe-http-endpoint-messages/, 来自 隔叶黄莺 Yanbin Blog

[版权声明] Creative Commons License 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。

Subscribe
Notify of
guest

0 Comments
Inline Feedbacks
View all comments