Amazon Simple Notification Service
开发人员指南 (API 版本 2010-03-31)
AWS 文档中描述的 AWS 服务或功能可能因区域而异。要查看适用于中国区域的差异,请参阅 Amazon AWS 入门

使用 Amazon SNS 筛选消息

默认情况下,Amazon SNS 主题的订阅者会收到发布到该主题的所有消息。若要仅接收一部分消息,订阅者可将筛选策略分配给主题订阅。

筛选策略是简单的 JSON 对象。该策略包含定义订阅者将收到的消息的属性。当您将一条消息发布到某个主题时,Amazon SNS 会比较该消息的属性与该主题的每个订阅的筛选策略中的属性。如果属性之间存在匹配项,Amazon SNS 则将消息发送给订阅者。否则,Amazon SNS 跳过订阅者而不向其发送消息。如果订阅缺少筛选策略,则订阅将收到发布到其主题的每条消息。

使用筛选策略后,您可以通过将消息筛选条件合并到主题订阅中来简化对 Amazon SNS 的使用。通过此合并,您可以从订阅者卸载消息筛选逻辑,从发布者卸载消息路由逻辑。这样,就不需要通过为每个筛选条件创建单独的主题来筛选消息。相反,您可以使用单个主题,并用属性来区分消息。每个订阅者只收到并处理其筛选策略所接受的消息。

例如,您可以使用单个主题来发布由您的零售网站上的事务生成的所有消息。对于每条消息,您可以分配一个属性来指示事务类型,如 order_placedorder_cancelledorder_declined。通过使用筛选策略来创建订阅,可以将每条消息路由到用于处理该消息的事务类型的队列。

若要观看展示怎样通过 AWS 管理控制台 使用信息筛选功能的演示视频,请查阅筛选发布至主题的消息。该演示视频展示的是怎样应用筛选器策略,以便将消息传送至不同的 Amazon SQS 队列。

订阅筛选策略

可以将筛选策略分配给 Amazon SNS 订阅。在一条策略中,可以指定属性名称,同时对于每个名称,可以分配一个值或多个值的列表。只有当消息包含的属性与订阅的筛选策略中指定的特性相匹配时,订阅才接受消息。具体来说,只有符合以下条件时,订阅才接受消息:

  1. 筛选策略中的每个属性名均与分配给消息的一个属性名匹配。

  2. 对于每个匹配的属性名,在筛选策略和消息中分配给该名称的值之间至少存在一个匹配项。

当 Amazon SNS 对照策略评估消息属性时,它会忽略在策略中未指定的消息属性。

下面的示例演示筛选策略如何接受或拒绝发布到主题的消息。

包含属性的消息示例

下面的示例演示由发布客户事务记录的 Amazon SNS 主题所发送的消息负载。此消息包括描述事务的属性:店铺名称、事件类型、客户爱好以及购买价格。

{ "Type" : "Notification", "MessageId" : "e3c4e17a-819b-5d95-a0e8-b306c25afda0", "TopicArn" : "arn:aws:sns:us-east-1:111122223333:MySnsTopic", "Message" : message body with transaction details . . . "Timestamp" : "2017-11-07T23:28:01.631Z", "SignatureVersion" : "1", "Signature" : signature . . . "UnsubscribeURL" : unsubscribe URL . . . "MessageAttributes" : { "customer_interests" : {"Type":"String.Array","Value":"[\"soccer\", \"rugby\", \"hockey\"]"}, "store" : {"Type":"String","Value":"example_corp"}, "event" : {"Type":"String","Value":"order_placed"}, "price_usd" : {"Type":"Number","Value":210.75} } }

有关将属性应用到消息的信息,请参阅 使用 Amazon SNS 消息属性

由于此消息包含属性,因此,任何包含筛选策略的主题订阅均可选择接受或拒绝此消息。

示例筛选策略

以下筛选策略基于属性名称和值接受或拒绝示例消息。

例 接受消息的策略

以下策略中的属性与分配给示例消息的属性匹配:

{ "store": ["example_corp"], "event": [{"anything-but":"order_cancelled"}], "customer_interests": ["rugby", "football", "baseball"], "price_usd": [{"numeric":[">=", 100]}] }

如果此策略中的任何一个属性与分配给该消息的属性不匹配,策略将拒绝该消息。

例 拒绝消息的策略

以下策略在其属性与分配给示例消息的属性之间存在多处不匹配情况:

{ "store": ["example_corp"], "event": ["order_cancelled"], "encrypted": [false], "customer_interests": ["basketball", "baseball"] }

一个或多个不匹配导致策略拒绝该消息。

由于消息属性中不存在 encrypted 属性名称,因此该策略属性拒绝消息,而不管分配给它的值如何。

筛选策略属性可以包含字符串或数值。对于字符串和数值,您可以使用以下操作来匹配消息属性和筛选消息。

字符串值匹配

字符串值包含在策略 JSON 中的双引号内。对于字符串,您可以使用以下操作来匹配消息属性。

精确匹配 (白名单)

在策略属性值与一个或多个消息属性值匹配时,发生此匹配。例如,策略属性:

"customer_interests": ["rugby"]

匹配消息属性:

"customer_interests" : {"Type":"String","Value":"rugby"}
Anything-but 匹配 (黑名单)

当策略属性值包含 anything-but 关键字时,它匹配 包含该策略属性值的任何消息属性。例如,策略属性:

"customer_interests": [{"anything-but":"rugby"}]

匹配消息属性,例如:

"customer_interests" : {"Type":"String","Value":"baseball"}

And:

"customer_interests" : {"Type":"String","Value":"football"}

但不匹配:

"customer_interests" : {"Type":"String","Value":"rugby"}
前缀匹配

当策略属性值包含 prefix 关键字时,其值匹配以指定字符开头的任何消息属性值。例如,策略属性:

"customer_interests": [{"prefix":"bas"}]

匹配消息属性,例如:

"customer_interests" : {"Type":"String","Value":"baseball"}

And:

"customer_interests" : {"Type":"String","Value":"basketball"}

但不匹配:

"customer_interests" : {"Type":"String","Value":"rugby"}

数值匹配

在您的策略 JSON 中,数值不包含在双引号内。对于数值,您可以使用以下操作来匹配消息属性值。

精确匹配

当策略属性值包含 numeric 关键字和 = 运算符时,它将匹配具有相同名称和相同数值的任何消息属性。例如,策略属性:

"price_usd": [{"numeric":["=",301.5]}]

匹配消息属性,例如:

"price_usd" : {"Type":"Number","Value":301.5}

And:

"price_usd" : {"Type":"Number","Value":3.015e2}
范围匹配

= 运算符外,数字策略属性还可以包含 <<=>>=。例如,策略属性:

"price_usd": [{"numeric":["<", 0]}]

匹配具有负值的消息属性,以及:

"price_usd": [{"numeric":[">", 0, "<=", 150]}]

匹配具有正值 (最大为 150 ) 的消息属性。

AND/OR 逻辑

按如下所示将 AND/OR 逻辑应用于您的筛选策略。

AND 逻辑

通过使用多个属性名称 (键) 应用 AND 逻辑。例如,策略:

{ "customer_interests": ["rugby"], "price_usd": [{"numeric":[">", 100]}] }

匹配 customer_interests 值为 rugby price_usd 值高于 100 的消息。

OR 逻辑

通过将多个值分配给属性名称来应用 OR 逻辑。例如,策略属性:

"customer_interests": ["rugby", "football", "baseball"]

匹配 customer_interests 值为 rugbyfootball baseball 的消息。

约束

创建筛选策略时,请记住以下约束:

  • Amazon SNS 仅将策略属性与数据类型为 StringString.ArrayNumber 的消息属性进行比较。数据类型为 Binary 的消息属性被忽略。

  • 对于字符串而言,策略和消息之间的属性比较区分大小写。

  • 作为 JSON 对象,筛选策略可以包含用引号括起来的字符串、数字和无引号关键字 truefalsenull

  • 一个筛选策略最多可以具有 10 个属性名称。

  • 数值策略属性的值可以介于 -109 到 109 之间,精确至小数点后 5 位数。

  • 策略的最大大小为 256 KB.

  • 在您使用 Amazon SNS API 时,必须将策略 JSON 作为有效的 UTF-8 字符串传递。

  • 默认情况下,每个地区的每个 AWS 账户最多可使用 100 项筛选器策略。若要提高该最高限制,请提交一份 SNS Limit Increase case

  • 各值的组合总和不能超过 100。通过将每个数组内所含值的个数相乘来计算总的组合数。例如,在以下策略中,第一个数组有三个值,第二个有一个值,第三个有两个值。总组合数的计算方法为 3 x 1 x 2 = 6。

    { "key_a": ["value_one", "value_two", "value_three"], "key_b": ["value_one"], "key_c": ["value_one", "value_two"] }

应用订阅筛选策略

您可以使用 Amazon SNS 控制台将筛选策略应用于 Amazon SNS 订阅。或者,如果以编程方式应用策略,则可以使用 Amazon SNS API、AWS Command Line Interface (AWS CLI) 或任何支持 Amazon SNS 的 AWS 软件开发工具包,如 AWS SDK for Java。

使用控制台应用筛选策略

使用 Amazon SNS 控制台完成以下应用筛选策略的步骤:

  1. 通过以下网址登录 AWS 管理控制台 并打开 Amazon SNS 控制台:https://console.amazonaws.cn/sns/v2/home

  2. 在导航窗格中,选择订阅Subscriptions (订阅) 页面提供您在所选区域具有的所有 Amazon SNS 订阅。

  3. 如果您没有订阅,则选择 Create subscription,并为 Topic ARNProtocolEndpoint 提供值。

  4. 如果您有一个或多个订阅,请选择要向其应用筛选策略的订阅。

  5. 选择 Actions,并在菜单中选择 Edit subscription filter policy

  6. Edit subscription filter policy 窗口中,提供筛选策略的 JSON 正文。

  7. 选择 Set subscription filter policy。Amazon SNS 将您的筛选策略应用到订阅。

使用 AWS CLI 应用筛选策略

要使用 AWS Command Line Interface (AWS CLI) 应用筛选策略,请使用 set-subscription-attributes 命令,如以下示例中所示:

$ aws sns set-subscription-attributes --subscription-arn arn:aws:sns: ... --attribute-name FilterPolicy --attribute-value '{"store":["example_corp"],"event":["order_placed"]}'

对于 --attribute-name 选项,请指定 FilterPolicy。对于 --attribute-value,请指定您的 JSON 策略。

要为您的策略提供有效的 JSON,请用双引号将属性名和值括起来。此外,您必须用引号将整个策略参数括起来。要避免转义引号,您可以使用单引号将策略括起来,并使用双引号将 JSON 名称和值括起来,如示例中所示。

要验证是否已应用您的筛选策略,请使用 get-subscription-attributes 命令。终端输出中的属性应显示 FilterPolicy 键的筛选策略,如以下示例中所示:

$ aws sns get-subscription-attributes --subscription-arn arn:aws:sns: ... { "Attributes": { "Endpoint": "endpoint . . .", "Protocol": "https", "RawMessageDelivery": "false", "EffectiveDeliveryPolicy": "delivery policy . . .", "ConfirmationWasAuthenticated": "true", "FilterPolicy": "{\"store\": [\"example_corp\"], \"event\": [\"order_placed\"]}", "Owner": "111122223333", "SubscriptionArn": "arn:aws:sns: . . .", "TopicArn": "arn:aws:sns: . . ." } }

使用 AWS SDK for Java 应用筛选策略

要使用 AWS SDK for Java 应用筛选策略,请使用 AmazonSNS 客户端的 setSubscriptionAttributes 方法。提供 SetSubscriptionAttributesRequest 对象作为参数,如以下示例所示:

AmazonSNS snsClient = AmazonSNSClientBuilder.defaultClient(); String filterPolicyString = "{\"store\":[\"example_corp\"],\"event\":[\"order_placed\"]}"; SetSubscriptionAttributesRequest request = new SetSubscriptionAttributesRequest(subscriptionArn, "FilterPolicy", filterPolicyString); snsClient.setSubscriptionAttributes(request);

要初始化 SetSubscriptionAttributesRequest 对象,请提供以下参数:

  • subscriptionArn – 要向其应用策略的订阅的 Amazon 资源名称 (ARN)。

  • attributeName – 必须为 "FilterPolicy"

  • attributeValue – 字符串形式的 JSON 筛选策略。因为您必须用双引号将字符串策略括起来,所以请记住对括起属性名称和值的双引号进行转义,如 \"store\" 中所示。

SetSubscriptionAttributesRequest 类接受筛选策略作为字符串。如果要将策略定义为 Java 集合,请创建一个映射来将每个属性名称与一个值列表相关联。要将策略分配给订阅,请先要从映射的内容生成策略的字符串版本。然后,您将该字符串作为 attributeValue 参数传递给 SetSubscriptionAttributesRequest

为了简化此流程,您可以向应用程序添加以下示例类并根据需要修改它:

import com.amazonaws.services.sns.AmazonSNS; import com.amazonaws.services.sns.model.SetSubscriptionAttributesRequest; import java.util.ArrayList; import java.util.HashMap; import java.util.Map; import java.util.stream.Collectors; public class SNSMessageFilterPolicy { private enum AttributeType { String, Numeric, Prefix, List, AnythingBut } private class Attribute<T> { protected final T value; protected final AttributeType type; public Attribute(AttributeType type, T value) { this.value = value; this.type = type; } public String toString() { switch(type) { case Prefix: return String.format("{\"prefix\":\"%s\"}", value.toString()); case Numeric: return String.format("{\"numeric\":%s}", value.toString()); case List: ArrayList<T> values = (ArrayList<T>)value; return values .stream() .map(entry -> entry.toString()) .collect(Collectors.joining(",", "[", "]")); case AnythingBut: return String.format("{\"anything-but\":\"%s\"}", value); default: return String.format("\"%s\"", value); } } } private class NumericValue<T extends Number> { private final T lower; private final T upper; private final String lowerOp; private final String upperOp; public NumericValue(String op, T value) { lower = value; lowerOp = op; upper = null; upperOp = null; } public NumericValue(String lowerOp, T lower, String upperOp, T upper) { this.lower = lower; this.lowerOp = lowerOp; this.upper = upper; this.upperOp = upperOp; } public String toString() { StringBuffer s = new StringBuffer("[") .append('\"').append(lowerOp).append("\",").append(lower); if (upper != null) { s.append(",\"").append(upperOp).append("\",").append(upper); } s.append("]"); return s.toString(); } } private final Map<String, Attribute> filterPolicy = new HashMap<>(); public void addAttribute(String attributeName, String attributeValue) { filterPolicy.put(attributeName, new Attribute(AttributeType.String, attributeValue)); } public void addAttribute(String attributeName, ArrayList<String> attributeValues) { ArrayList<Attribute> attributes = new ArrayList<>(); for (String s : attributeValues) { attributes.add(new Attribute(AttributeType.String, s)); } filterPolicy.put(attributeName, new Attribute(AttributeType.List, attributes)); } public void addAttributePrefix(String attributeName, String prefix) { filterPolicy.put(attributeName, new Attribute(AttributeType.Prefix, prefix)); } public void addAttributeAnythingBut(String attributeName, String value) { filterPolicy.put(attributeName, new Attribute(AttributeType.AnythingBut, value)); } public <T extends Number> void addAttribute(String attributeName, String op, T value) { filterPolicy.put(attributeName, new Attribute(AttributeType.Numeric, new NumericValue(op, value))); } public <T extends Number> void addAttributeRange( String attributeName, String lowerOp, T lower, String upperOp, T upper) { filterPolicy.put( attributeName, new Attribute(AttributeType.Numeric, new NumericValue(lowerOp, lower, upperOp, upper))); } public void apply(AmazonSNS snsClient, String subscriptionArn) { SetSubscriptionAttributesRequest request = new SetSubscriptionAttributesRequest(subscriptionArn, "FilterPolicy", formatFilterPolicy()); snsClient.setSubscriptionAttributes(request); } public String formatFilterPolicy() { return filterPolicy.entrySet() .stream() .map(entry -> "\"" + entry.getKey() + "\": [" + entry.getValue() + "]") .collect(Collectors.joining(", ", "{", "}")); } }

该类存储 filterPolicy 字段作为映射。您可以使用不同的 addAttribute 方法将属性添加到策略。这些方法接受属性名称作为字符串,并且它们专门用于接受不同类型的值。您可以将值作为字符串、字符串列表、数字或数字范围传递。您可以添加 anything-butprefix 属性。当您准备将策略应用于订阅时,请使用 apply 方法,并提供一个 AmazonSNS 客户端和订阅 ARN。该方法从 filterPolicy 映射的内容生成策略字符串,并且将策略应用于指定的订阅。

下面的代码演示如何初始化和使用示例 SNSMessageFilterPolicy 类:

// Initialize example filter policy class SNSMessageFilterPolicy fp = new SNSMessageFilterPolicy(); // Add filter policy attribute with single value fp.addAttribute("store", "example_corp"); fp.addAttribute("event", "order_placed"); // Add a prefix attribute filterPolicy.addAttributePrefix("customer_interests", "bas"); // Add an anything-but attribute filterPolicy.addAttributeAnythingBut("customer_interests", "baseball"); // Add filter policy attribute with a list of values ArrayList<String> attributeValues = new ArrayList<>(); attributeValues.add("rugby"); attributeValues.add("soccer"); attributeValues.add("hockey"); fp.addAttribute("customer_interests", attributeValues); // Add numeric attribute filterPolicy.addAttribute("price_usd", "=", 0); // Add numeric attribute with a range filterPolicy.addAttributeRange("price_usd", ">", 0, "<=", 100); // Apply filter policy attributes to SNS subscription fp.apply(snsClient, subscriptionArn);

使用 Amazon SNS API 应用筛选策略

要使用 Amazon SNS API 应用筛选策略,需要请求 SetSubscriptionAttributes 操作。将 AttributeName 参数设置为 FilterPolicy,并将 AttributeValue 参数设置为您的筛选策略 JSON。

删除订阅筛选策略

要停止筛选已发送到订阅的消息,请使用空白的 JSON 正文覆盖订阅的筛选策略以删除该策略。在删除该策略后,订阅会接受发布到它的每条消息。

使用控制台删除筛选策略

使用 Amazon SNS 控制台完成以下删除筛选策略的步骤:

  1. 通过以下网址登录 AWS 管理控制台 并打开 Amazon SNS 控制台:https://console.amazonaws.cn/sns/v2/home

  2. 在导航窗格中,选择订阅Subscriptions (订阅) 页面提供您在所选区域具有的所有 Amazon SNS 订阅。

  3. 选择要从中删除筛选策略的订阅。

  4. 选择 Actions,并在菜单中选择 Edit subscription filter policy

  5. Edit subscription filter policy (编辑订阅筛选策略) 窗口中,为筛选策略提供空白的 JSON 正文:{}

  6. 选择 Set subscription filter policy。Amazon SNS 会将空白的筛选策略应用于订阅。

使用 AWS CLI 删除筛选策略

要使用 AWS CLI 删除筛选策略,请使用 set-subscription-attributes 命令并为 --attribute-value 参数提供一个空白的 JSON 正文。

$ aws sns set-subscription-attributes --subscription-arn arn:aws:sns: ... --attribute-name FilterPolicy --attribute-value "{}"

使用 AWS SDK for Java 删除筛选策略

要使用 AWS SDK for Java 删除筛选策略,请使用 AmazonSNS 客户端的 setSubscriptionAttributes 方法。提供一个包含空白的 JSON 正文的字符串作为您的筛选策略:

AmazonSNS snsClient = AmazonSNSClientBuilder.defaultClient(); SetSubscriptionAttributesRequest request = new SetSubscriptionAttributesRequest(subscriptionArn, "FilterPolicy", "{}"); snsClient.setSubscriptionAttributes(request);

使用 Amazon SNS API 删除筛选策略

要使用 Amazon SNS API 删除筛选策略,需要请求 SetSubscriptionAttributes 操作。将 AttributeName 参数设置为 FilterPolicy,然后为 AttributeValue 参数提供一个空白的 JSON 正文。