如何设计优异的适配器
出于性能方面的考虑,在提交消息批、传输批和对批中消息执行一般性操作等方面,所有适配器都应该是识别批的。 适配器应该尝试公开与性能相关的可配置属性,例如批的大小或批中的字节数,从适配器的设计时用户界面可以配置这些属性。
如前所述,发送适配器应该始终执行非阻止发送,以避免降低发送主机的性能。 不建议进一步阻止消息引擎 API。
无论是写入还是读取消息上下文,都会影响运行时性能。 一般而言,适配器应避免读取、写入和升级数目过多的消息上下文属性。 由于在运行时在每个升级的属性上都会发生订阅评估,因此升级属性将造成额外的性能消耗。 但是,适配器可能会需要升级大量属性,这将显著影响性能。 此时,仍最好只升级确实需要升级的那些属性。
阻止发送和接收
在 BizTalk 引擎上的负载超出了已配置的阈值时,该引擎将阻止适配器和业务流程,以便确保提供最佳性能。 在接收端,当引擎上的工作负载超过给定阈值时,适配器对 IBTTransportBatch.Done 的调用将被阻止,直到负载充分降低。 这强制适配器只有在引擎可用时才将新工作提交到该引擎。 在发送端,在引擎阻止适配器发送出站消息时,该引擎在负载减轻前不会发送要传输的新消息。
出于上述原因,除非特别要求(例如,要限制与后端系统的连接数),否则,适配器无需考虑阻止问题。 对于这些类型的情况,引擎和适配器框架均不提供任何支持。
您可以根据是否控制适配器的源代码,通过几种方式阻止从自定义发送适配器发送的消息数目。
发送端阻止提高性能
如果您控制适配器的源代码,则可以通过试探法确定您在队列中随时要发送的消息的最大数目。 当消息引擎调用 TransmitMessage
方法并向发送适配器传递新消息时,可以选择阻止线程或检查以查看队列中的消息数是否大于之前确定的最大值。 如果已超出最大消息数,则可以使用 Resubmit
方法将消息重新提交到消息引擎。 请注意,如果适配器是同步的,则该消息可能已被阻止。
如果不控制适配器的源代码,可以通过更改 BizTalk 管理数据库中Adm_serviceclass表中的 Highwatermark 值来更改排队消息数。 Highwatermark 属性的最大值为 200。 还可以将 Lowwatermark 属性的值更改为较小的值。
请记住,异步适配器的 Highwatermark 属性的值会考虑消息引擎提供给适配器的消息数。 消息引擎通过 TransmitMessage
方法将它们传递给适配器,这些消息在其传输中可能仍然未完成,例如,如果适配器尚未对 DeleteMessage
、 Resubmit
、 MoveToNextTransport
或 Microsoft.BizTalk.TransportProxy.Interop.BatchOperationType.MoveToSuspendQ 方法进行相应的调用。 对于同步适配器, Highwatermark 属性仅考虑消息引擎使用 TransmitMessage 方法传递给适配器的消息数,因为此调用是同步处理的,从而阻止了调用消息引擎线程。
如果您在为本质上速度就较慢的协议(例如 HTTP、FTP 或双向 SOAP)编写发送适配器,请考虑以下方面:
此类适配器接收从 BizTalk 消息引擎传输的消息速度可能快于它传送消息的速度。 这一差异会导致不同层面的问题。 传输中的消息仍保留在内存中并且占用虚拟内存,这将减慢整个系统的速度。
适配器可能会占用特定于协议的资源。 例如,可能会打开过多与服务器的并行连接,从而可能导致远程服务器崩溃。
该适配器可能会影响其他适配器。 例如,如果过多的消息为特定适配器排队,则消息引擎会停止发出对该进程中其他发送适配器的请求。
解决方法是将慢速和快速适配器放置于不同的 BizTalk 主机中,并且通过使用“高水位”和“低水位”设置控制消息的数目。
接收端阻止提高性能
存在多种接收适配器接收消息的速度快于其余系统处理消息的速率的情况。 在发生此类情况时,MessageBox 数据库将开始积压。 此时,整个系统的性能都会显著下降。
如果在您的适配器上发生这种情况,则可以使用以下技术之一来降低接收适配器的速度:
减少消息引擎线程池大小。 您可以控制消息引擎用于将消息发布到 MessageBox 中的线程数目。 通过减少线程数目,您将降低接收适配器将消息接收到 MessageBox 中的速率。 只需对与适配器的接收处理程序相对应的主机进行此设置。 您不应为与适配器的发送处理程序相对应的主机进行此设置,除非您也想降低发送适配器的速度。
减少适配器的批大小。 大多数快速的接收适配器都将消息按批发送到 MessageBox。 这些批的大小通常可在接收位置属性页中进行配置。 通过减小批大小,您可以减少进入系统的消息的整体吞吐量。
更改其他特定于适配器的设置。 在您完成前两个步骤后,可以尝试调整其他适配器参数以便进一步降低吞吐量。 某些适配器公开可用于降低吞吐量的内部参数。 例如,MQSeries 适配器具有“按序送达”设置。 “按序送达”指定适配器将发布一批消息,等待该批消息完成,然后发布下一批消息。 通过启用此设置,您将从根本上取消与接收适配器的所有并行性。 反过来,通过使用相反的方法调整这些参数,可以提高接收适配器的接收速率。
一个适配器可以向传输代理提交所需的任何数目的批。 当系统受到严重压力时,对 IBTTransportBatch 接口的 Done 方法的调用将阻止消息,直到将所需的资源释放到系统。
计划异步接收和发送
BizTalk Server 消息 API 具有对异步编程的大量支持。 如果您想要编写可伸缩的适配器,则应该计划从头就开始使用异步模型,因为异步模型提供更好的并行性。
在接收端,当适配器通过调用 IBTTransportBatch::D one) 将一批消息提交到 BizTalk 消息引擎 (时,消息传送引擎会使用其内部线程池将工作排队并立即返回。 该引擎将处理单独线程上的消息,保持适配器可以自由读取来自源的更多消息,并且无需等待以前的消息完成处理即可提交它们。
在发送端,您的适配器可以是异步的或同步的。 但是,如果您的协议支持异步操作,则应利用这一支持来编写可伸缩适配器。 例如,FILE 和 HTTP 发送适配器是完全异步的,它们执行非常少的阻止/同步操作。
异步操作将确保消息引擎和您的适配器都将继续并行执行其各自的工作,在执行一般的消息处理时不必彼此等待。
使用批处理以改进性能
批处理是用于编写可伸缩适配器的最佳起点。 这一点在发送端和接收端适配器均成立。 即使您的适配器是非事务性的,每个批也都要通过 BizTalk Server 内的数据库事务。 因为存在与每个事务相关联的固定延迟,所以,您应该通过将多个操作合并为单个批,尝试尽量减少事务的数目。
不要让 .NET 线程池闲置
编写 BizTalk 适配器有助于练习编写 .NET 运行时代码,编写 .NET 运行时代码也一定有助于练习异步编程。
让 .NET 线程池闲置对于 .NET 中的所有异步编程都带来风险,因此是 BizTalk 适配器编程人员应极力避免的。
.NET 线程池是受到限制但全面共享的资源。 可以很容易地就编写使用某一 .NET 线程池线程并且将其保留很长时间的代码,并且该代码可阻止其他工作项执行。
每当使用 BeginInvoke 或使用计时器时,都使用 .NET 线程池线程。 如果要执行多个工作 (例如将消息从 MQSeries 复制到BizTalk Server) 中,则应执行一个工作项 (一批消息进入BizTalk Server) ,然后在线程池中重新排队(如果有更多工作要做)。 永远不要坐在线程上的循环中 while
。
具体来说,这意味着将 while
循环替换为对 BeginInvoke 的重复调用。 这一简单的更改可以显著改善整个实现的响应性和扩展能力。
在限制批大小时选择正确的度量
如果您将消息成批提交到 BizTalk Server,则不要只基于消息计数限制批大小。 考虑将适配器配置为仅基于消息计数进行批处理时会发生什么情况:如果批大小为 2,并且适配器分别收到大小分别为 4 KB、8 KB、1 MB 和 5MB 的 4 条消息,则第一批的大小为 12 KB,第二批的大小为 6 MB。
因为 BizTalk 消息引擎顺序处理单个批中的所有消息,所以,在此示例中,对第二个批的处理速度比对第一个批的处理速度要慢得多。 这将大幅降低吞吐量。 处理这一问题的一个较好的方法是基于消息计数和批中总字节数(即,按字节计算的批大小)进行批处理。 不存在针对总字节数的神奇数字。 但是,在一般的处理情况中,如果批大小超过 1 MB,并行性和吞吐量都将开始下降。
通常,适配器是不知道消息情况的,它们不知道生产环境中消息的大小。 传入的消息的大小很可能差别很大。 因此,应始终使用消息计数和总字节数来生成批。