声明-检查模式
将大消息拆分成认领凭证和有效负载。 将认领凭证发送到消息传递平台,并将有效负载存储到外部服务。 此模式允许处理大消息,同时避免消息总线和客户端过载或速度减慢。 此模式还有助于降低成本,因为存储通常比消息传递平台使用的资源单位更便宜。 此模式也称为基于引用的消息传递,最初在 Gregor Hohpe 和 Bobby Woolf 撰写的《Enterprise Integration Patterns》(企业集成模式)一书中做了介绍。
上下文和问题
在某些时候,基于消息传递的体系结构必须能够发送、接收和操作大消息。 此类消息可以包含任何内容,包括图像(例如 MRI 扫描图像)、声音文件(例如呼叫中心的通话记录)、文本文档或任意大小、任何类型的二进制数据。 不建议将这么大的消息直接发送到消息总线,因为它们需要消耗更多的资源和带宽。 大消息还会减慢整个解决方案的速度,因为消息传递平台通常经过微调,以处理大量的小消息。 此外,大多数消息传递平台对消息大小施加了限制,因此对于大消息,可能需要克服这些限制。
解决方案
将整个消息有效负载存储到外部服务(例如数据库)中。 获取对存储的有效负载的引用,并将该引用发送到消息总线。 引用的作用类似于用于取回某件行李的认领凭证,该模式的名称也由此得来。 如果需要,对处理该特定消息感兴趣的客户端可以使用获取的引用来检索有效负载。
- 发送消息
- 在数据存储上存储消息
- 将消息的参考排队
- 读取消息的参考
- 检索消息
- 处理消息
问题和注意事项
在决定如何实现此模式时,请考虑以下几点:
- 如果不需要存档消息,请考虑在用完消息数据后将其删除。 虽然 Blob 存储相对便宜,但从长远来看,它也会产生一定的费用,尤其是有大量数据的情况下。 可以由接收和处理消息的应用程序以同步方式删除消息,或者由单独的专用进程以异步方式删除消息。 异步方法会删除旧数据,且不影响接收方应用程序的吞吐量和消息处理性能。
- 存储和检索消息会造成一些额外的开销和延迟。 建议在发送方应用程序中实现一个逻辑,以便仅在消息大小超过消息总线的数据限制时才使用此模式。 对于较小的消息,将跳过该模式。 此方法将建立有条件的认领凭证模式。
何时使用此模式
每当消息无法满足所选消息总线技术支持的消息限制时,就可以使用此模式。 例如,服务总线当前的限制为 100 MB(高级层),而事件网格支持最大 1 MB 的消息。 如果有效负载只能由有权查看它的服务访问,也可以使用该模式。 通过将有效负载卸载到外部资源,可以实施更严格的身份验证和授权规则,以确保在有效负载中存储敏感数据时强制实施安全性。
示例
在 Azure 上,这种模式可以通过多种方式和不同的技术来实现,但主要有两种实现类别。 在这两种情况下,接收方都需要负责读取认领凭证并使用它来检索有效负载。
- 自动生成认领凭证。 此方法使用 Azure 事件网格自动生成认领凭证,并将其推送到消息总线。
- 手动生成认领凭证。 使用此方法时,发送方需要负责管理有效负载。 发送方使用相应的服务来存储有效负载,获取或生成认领凭证,并将认领凭证发送到消息总线。
事件网格是一个事件路由服务,它尝试在最长 24 小时的可配置时间内传递事件。 之后,要么丢弃事件,要么将事件加入死信队列。 如果需要存档事件有效负载或重播事件流,可将事件网格订阅添加到事件中心或队列存储,在其中消息可以保留更长时间并支持存档消息。 有关微调事件网格消息传递和重试以及死信配置的信息,请参阅死信和重试策略。
使用 Blob 存储和事件网格自动生成认领凭证
使用此方法时,发送方会将消息有效负载放入指定的 Azure Blob 存储容器。 事件网格自动生成标记/引用,并将其发送到支持的消息总线,例如 Azure 存储队列。 接收方可以轮询队列,获取消息,然后使用存储的引用数据直接从 Blob 存储下载有效负载。 Azure Functions 可以直接使用同一事件网格消息,而无需通过消息总线。 此方法充分利用了事件网格和 Functions 的无服务器特性。 可在此处找到此方法的示例代码。
事件网格与事件中心
类似于上面的示例,将有效负载写入 Azure Blob 容器时,事件网格会自动生成一条消息。 但在此示例中,消息总线是使用事件中心实现的。 客户端可以注册自身以在将消息写入事件中心时接收消息流。 还可将事件中心配置为存档消息,以便以 Avro 文件的形式提供这些消息,可以使用 Apache Spark、Apache Drill 等工具或任何可用的 Avro 库来查询该文件。 可在此处找到此方法的示例代码。
使用服务总线生成认领凭证
此解决方案利用了特定的服务总线插件 ServiceBus.AttachmentPlugin,使认领凭证工作流易于实现。 该插件将任何消息正文转换为附件,发送消息时,该附件将存储在 Azure Blob 存储中。
using ServiceBus.AttachmentPlugin;
...
// Getting connection information
var serviceBusConnectionString = Environment.GetEnvironmentVariable("SERVICE_BUS_CONNECTION_STRING");
var queueName = Environment.GetEnvironmentVariable("QUEUE_NAME");
var storageConnectionString = Environment.GetEnvironmentVariable("STORAGE_CONNECTION_STRING");
// Creating config for sending message
var config = new AzureStorageAttachmentConfiguration(storageConnectionString);
// Creating and registering the sender using Service Bus Connection String and Queue Name
var sender = new MessageSender(serviceBusConnectionString, queueName);
sender.RegisterAzureStorageAttachmentPlugin(config);
// Create payload
var payload = new { data = "random data string for testing" };
var serialized = JsonConvert.SerializeObject(payload);
var payloadAsBytes = Encoding.UTF8.GetBytes(serialized);
var message = new Message(payloadAsBytes);
// Send the message
await sender.SendAsync(message);
服务总线消息充当客户端可以订阅的通知队列。 当使用者收到消息时,可以通过该插件直接从 Blob 存储中读取消息数据。 然后,你可以选择如何进一步处理消息。 此方法的一个优点在于,它抽象化了发送方和接收方的认领凭证工作流。 可在此处找到此方法的示例代码。
使用 Kafka 手动生成认领凭证
在此示例中,Kafka 客户端将有效负载写入 Azure Blob 存储。 然后它使用已启用 Kafka 的事件中心发送通知消息。 使用者接收消息并可以从 Blob 存储访问有效负载。 此示例演示了如何使用不同的消息传递协议在 Azure 中实现认领凭证模式。 例如,你可能需要支持现有的 Kafka 客户端。 可在此处找到此方法的示例代码。
后续步骤
- GitHub 中提供了上述示例。
- 企业集成模式站点上介绍了此模式。
- 有关另一个示例,请参阅博客文章 Dealing with large Service Bus messages using claim check pattern(使用认领凭证模式处理大型服务总线消息)。
- 用于处理大消息的一种替代模式是拆分和聚合。
- NServiceBus 等库通过其“数据总线”功能为此模式提供开箱即用的支持。