BizTalk 中有序交付的重新排序策略:
我最近回复了 LinkedIn 用户的 question 关于 BizTalk 中的订购交付选项。
我认为了解使用 BizTalk 对消息重新排序的一些策略对人们很有用。
通常作为 BizTalk 开发人员,您需要集成到不可更改的业务线系统。这可能是由于许多不同原因中的一个或多个。例如,更改系统的成本可能太高,或者供应商许可声明如果更改系统可能会取消支持。
这通常不代表供应商提供了精心设计的 API 作为集成点端点的问题。然而,正如许多集成开发人员很快了解到的那样,这种情况很少见。
精心设计的 API 是什么意思?好吧,除了所有 SODA 原则(服务组合、故障契约等)之外,API 最重要的特性是支持使用以错误顺序到达的数据。
这是一件相当简单的事情。例如,如果您是供应商并且您提供 HTTP 操作作为集成点,那么您可以在操作中公开的字段之一是时间戳,或者更好的是序列号。这意味着,如果使用过时的有效负载进行调用,相关的补偿机制可以启动——这可以像丢弃数据一样简单。
本文讨论了当供应商没有将此功能内置到 API 中时该怎么做,因此作为集成商,您必须将端到端的订购交付作为集成解决方案的一部分来实施。
正如我的response 在 LinkedIn 上的用户帖子中所述(请参阅上面的链接),在 BizTalk 中,除了最简单的情况外,任何情况下的订单交付充其量都是复杂的,而在最坏的情况下,这两种情况都可能意味着增加复杂性的巨大成本。在发展和支持方面。根本原因是BizTalk被设计成海量并发来实现高吞吐量,并发和排序之间存在直接且不可避免的冲突。向 BizTalk 解决方案中严格执行 E2E 有序交付依赖于诸如单例编排之类的人工制品,这会引入复杂性并增加故障率和每次故障的成本。
更好的解决方案是将并发处理保持在尽可能接近业务线系统端点的位置,然后在每个端点周围实施所谓的 重新排序器包装器这需要以正确的顺序传递数据。
如何在 BizTalk 中实现这样的包装取决于一些因素,下表概述了这些因素:
|Sequencing |Messages|Database |Wrapper |
|field |are |integration?|strategy |
| |deltas? | | |
|--------------|--------|------------|----------------------------------|
|n of a total m| N | Y |Stored procedure |
|n of a total m| N | N |Singleton orchestration |
|n of a total m| Y | Y |Batched singleton orchestration |
|n of a total m| Y | N |Batched singleton orchestration |
|Timestamp | N | Y |Stored procedure |
|Timestamp | N | N |Singleton orchestration |
|Timestamp | Y | Y |Buffer table with staggered reader|
|Timestamp | Y | N |Buffer table with staggered reader|
第一个因素排序字段与这样一种想法有关,即为了实现任何类型的重新排序器包装器,您至少需要您的消息数据包含一些排序信息。这可以采用源时间戳的形式;然而,一种更好但更罕见的排序信息由序列号和消息总数组成,例如,10 条消息中的 1 条、10 条中的 2 条等。
第二个因素消息是增量?与消息的有效负载是否包含对数据的单个状态更改或所有过去更改的总和有关数据。换句话说,是否可以从该消息中重建数据的完整当前状态?如果消息有效负载仅包含一个更改,则可能无法从单个消息重建数据状态,在这种情况下,您的消息是 delta。
第三个因素数据库集成?与系统的集成入口点是否是数据库有关。这很重要的原因是,在数据库层集成是一种相当常见的集成场景,如果可用的话,可以大大简化重新排序的处理。
上表中的策略详细描述如下:
存储过程包装器
这是最简单的重测序策略。创建一个新的存储过程,在决定是否更新目标数据之前查询目标数据。决策可以简单到 我拥有的数据是否比目标系统中的数据更新?
当然,为了实现该策略,目标数据还必须包含源数据的排序字段,尽管必要时可以通过依赖可能已经存在于目标中的现有时间戳来进行近似数据。存储过程包装器可以包含在目标数据库中,也可以理想地包含在单独的数据库中。
单例编排包装器
这种策略背后的想法是单例编排。这是一种您可以实施的模式,以确保在任何时候都只存在一个编排实例。网上有很多文章演示了如何在 BizTalk 中实现这种模式。
这个想法的核心是单例简单地跟踪最近成功处理的消息序列(或时间戳)。如果单例接收到比最近序列更旧的消息,则将其简单地丢弃。这是可行的,因为消息是非增量,因此目标系统只能提交许多消息中的最新消息,并且数据将处于最新状态。只有当数据提交成功时,单例持有的最新序列才会更新。
批处理单例编排包装器
该策略基于上面的 Singleton 编排包装器,只是它更复杂。单例需要在内存中创建和保存消息的工作集,而不是只将最新的序列信息保存在内存中,它将重新排序,然后在批处理中的所有预期消息都完成后处理到达的。这是因为消息是增量,因此目标系统必须按照预期的顺序接收每条消息。一旦批处理成功发送,单例就可以终止。
要做到这一点,源数据必须包含一个相关标识符,该描述允许定义消息批次。例如,处理来自客户的一组定义的订单,入站消息必须包含客户的标识符。然后可以使用它来将消息路由到与该客户相关的单例编排实例。此外,可用的消息序列字段必须是 n of a total m 形式。
一旦初始化单例,它就会在内存中组装一组工作消息,并在新消息到达时继续填充它。我看到的一种方法是使用 System.Collections.Generic.List 作为工作集的容器。一旦列表已完全填充(列表长度 = m),则假定已接收到批处理中的所有消息,然后编排按顺序循环工作集并将消息处理到目标系统中。
批处理单例编排包装器的好处之一是它允许通过相关标识符进行并发处理。在上面的示例中,这意味着来自两个客户的消息将同时处理。
带有交错阅读器包装的缓冲表
可以说是提出的最复杂的策略,当您使用基于时间戳的排序字段进行增量消息传递时,将使用此解决方案。它可以通过某种描述的数据库来实现,该数据库充当重新排序缓冲区。
这里值得注意的是,这个重新排序的包装器并不能保证有序交付,但如果使用得当,它很有可能会有序交付。
当消息到达时,它们被写入缓冲区,并且在同一个操作中,缓冲区被重新排序,因此缓冲区中保存的消息的顺序总是正确的。
要创建缓冲区读取器,有一个接收位置,它在将消息传递到启用了有序传递的发送端口之前读取缓冲区中的消息,然后将消息处理到目标系统中。如果您的目标系统的 API 语义对于发送端口来说过于复杂,您也可以使用单例编排作为中介。
但是,使用我上面描述的这个包装器将不会启用有序传递,因为消息几乎肯定会以错误的顺序提交到缓冲区,这将导致消息被处理到目标系统中相同(错误)的顺序。这就是 交错查询 的用武之地。这是一种奇特的说法,您的缓冲区查询只需要在时间间隔 T 中选择数据,并且只选择那些行号小于缓冲区总行数减去 C。
这具有允许在适当的时间跨度进行排序的效果。 T 对于大多数 BizTalk 开发人员来说是熟悉的一些适配器(例如 WCF-SQL 适配器)的轮询间隔。 C 稍微难以设置,但是通过增加这个数字,您可以减少在轮询时错过比检索数据集中的最新消息更早的消息的机会。
T 和 C 是什么取决于很多因素,尽管这些值应基于您的延迟 SLA 和您的消息量(或吞吐量)。作为指导,如果您有一个 SLA 可以在 30 秒内将数据传送到您的目标系统,并且您每秒处理 10 条消息,那么 T 应该在 10 秒左右,而 C 应该大约 100 行。
当然,这仅在源系统在短时间内(理想情况下是背靠背)发送给定关联 ID 的消息时才有效。发送间隔越长,C的要求值越大,包装器的效果就越差。
此策略的一个好处是,如果您的数据源容易发送重复消息并且您的目标系统端点不是幂等的,您还可以对缓冲区中的消息执行重复数据删除。您还可以使用缓冲区来实现 FILO 和其他非标准排队语义。
结论
我在此处讨论的策略是将 BizTalk 弯曲到并非旨在完成的任务的方法。因此,每种方法都有关于支持成本和复杂性的警告,并且在某些情况下也可能不起作用。我想听听任何在 BizTalk 中实现了其他模式以进行订购交付的人。