在 BizTalk 中实现消息代理





0/5 (0投票)
本文描述了在 BizTalk 2006/R2/BizTalk 2009 上实现的一种功能,该功能可以将收到的消息拆分并发送到多个不同的目的地。
引言
本文描述了在 BizTalk 2006/R2/BizTalk 2009 上实现的一种功能,该功能可以将收到的消息拆分并发送到多个不同的目的地。目的地的数量在运行时是未知的。BizTalk 从消息中读取关于接收者的信息,并将消息发送到每个目标系统。BizTalk 从每个目标接收响应,并将它们聚合到一个响应消息中,然后发送回发送者。
此实现的一个用例是当一个 Web 应用程序需要比较不同提供商产品的价格时。Web 应用程序将请求发送到代理,并收到一个包含不同提供商价格的响应。
概述
此实现包含两个不同的编排。父编排接收消息,计算接收者数量,创建消息的副本,并为每个接收者启动一个子编排。
子编排从消息中读取接收者的 ID,并使用该 ID 从数据库中查找传输信息和地址。所有关于接收者的信息都存储在数据库中,消息中只包含接收者的 ID。使用一个单独的 .NET 程序集来获取此信息。子编排根据接收者的信息配置一个动态发送端口,并将消息发送到目标系统。它还从目标系统接收响应。
父编排使用一个自相关绑定端口来接收来自子编排的响应。它将所有响应添加到一条消息中,并在收到所有响应后,调用一个管道将消息聚合到信封中,然后将包含所有响应的信封发送回发送者。
消息
此实现包含四种不同的消息,每种消息都有自己的模式。一种模式是代理接收的主请求模式,另一种是发送回发送者的主响应模式。还有一种模式是发送到目标系统的接收者请求模式,以及目标系统发送回代理的接收者响应模式。
父编排
在这种情况下,BizTalk 接收的消息包含一个主接收者记录和一个或多个副本目的地记录。消息由静态接收端口接收,接收形状激活编排。编排的事务类型设置为“长期运行”,超时设置为一天。
计算接收者目的地
首先要计算副本目的地。这在一个表达式形状中完成。总数存储在一个名为 intTotalRecipients
的变量中。
intTotalRecipients = System.Convert.ToInt32(xpath(msgMainRequestIN,
"count(/*[local-name()='Request' and namespace-uri()='http://Stm.MessageBroker" +
".Schemas.MainRequest']/*[local-name()='CopyDest' and " +
"namespace-uri()='http://Stm.MessageBroker.Schemas.MainRequest'])"));
intRecipientNumber = 0;
为每个目的地构建单独的消息
单独的消息是在一个循环中创建的,该循环运行直到变量 intRecipientNumber
小于或等于接收者总数。变量 intRecipientNumber
用于保存当前接收者,并在循环结束时增加一。首先,为主要目的地创建一个消息。这在一个构造形状中完成,该形状只需将接收到的主请求映射到单独的接收者请求,如果 intRecipientNumber
等于 0。
对于每个副本目的地,首先构造一个辅助消息。此消息基于一个只有两个字段的简单模式,其中包含当前接收者和 intRecipientNumber
的值。(另一个字段未使用。)两者都是区分字段。消息赋值表达式如下所示:
xmlDocSplitHelper.LoadXml("<splitmainrequesthelper " +
"xmlns:ns0="http://Stm.MessageBroker.Schemas.SplitMainRequestHelper" />" +
"<recipientnumber />10</recipientnumber /><recipientstotal />" +
"10</recipientstotal /></splitmainrequesthelper />");
msgSplitMainRequestHelper = xmlDocSplitHelper;
msgSplitMainRequestHelper.RecipientNumber = intRecipientNumber;
此消息与主请求一起,在构造形状中用于映射到接收者请求,在该形状中构造接收者请求。(在选择在转换形状中创建新映射时,可以创建一个具有两个输入模式的映射。)在此映射中,使用 Index functoids 从正确的副本目的地记录中获取值。Index functoid 的输入是辅助消息的值(intRecipientNumber
的当前值)和副本目的地记录的值(副本目的地 ID)。
此时,单独的请求已正确构造
启动子编排并等待响应
使用一个自相关绑定端口来接收来自子编排的响应。端口标识符为 ReceiveFromChildPort
,端口类型为 FromChildPortType
。此端口和上面构造的接收者请求用作启动子编排的参数。然后,编排引擎会为消息生成一个与编排实例相关的关联令牌。这提供了在不使用关联集的情况下将响应传回父编排实例的能力。
将变量 intRecipientNumber
设置为 0,并使用一个新循环通过端口 ReceiveFromChildPort
接收响应。intRecipientNumber
在循环结束时增加,循环运行直到 intRecipientNumber
等于 intTotalRecipients
并且所有响应都已接收。
聚合响应并将响应发送回发送者
在上述循环中的表达式形状中,接收到的响应被添加到类型为 SendPipelineInputMessages
的变量 MessagesToAggregate
中。
MessagesToAggregate.Add(msgRecipientResponse);
intRecipientNumber = intRecipientNumber + 1;
循环结束后,此变量包含所有响应。然后,通过调用发送管道聚合响应来构造一个类型为 XmlDocument
的新消息 AggregatedMessage
。此发送管道包含一个 XMLassembler
,其中主响应模式被指定为信封模式,接收者响应模式被指定为文档模式。为了使此工作正常运行,信封模式必须包含一个 Any Element 来包含接收者响应。用于构造 AggregatedMessage
并调用发送管道的构造形状中的表达式如下所示:
AggregatedMessage = null;
Microsoft.XLANGs.Pipeline.XLANGPipelineManager.ExecuteSendPipeline(
typeof(Stm.MessageBroker.Pipelines.XMLAggregate),MessagesToAggregate,AggregatedMessage);
然后,AggregatedMessage
被发送回发送者。
子编排
子编排有两个编排参数:一个消息参数,即接收者请求消息;一个端口参数,其端口类型与父编排中的自相关绑定端口 FromChildPortType
相同。
获取接收者信息并配置动态发送端口
所有关于不同接收者的信息都存储在数据库中。在此示例中,有一个名为 Partners 的表,其中包含字段 PartnerID、PartnerName 和 TransportType。然后,为每种可能的传输类型有一个表。FILEInfo 表包含配置 FILE 传输类型伙伴的动态 FILE 端口所需的所有信息。同样,也有一个针对 SMTP 传输类型的伙伴的类似表。外部程序集有助于从数据库中获取此信息。此程序集提供一个 Transport
对象,在编排中,有一个此类型的变量 objTransport
。此程序集不是代理的一部分,因此本文档将不进行解释。但是,源代码已包含在下载内容中。请注意,此程序集只是获取此数据的简单示例。
第一个表达式形状从区分字段获取接收者 ID,并初始化 objTransport
对象。
strPartnerId = msgRecipientRequest.Receiver.Id;
objTransport.InitializeByPartnerId(strPartnerId);
调用 InitializeByPartnerId
方法时,外部程序集会在 Partners 表中查找以找到正确的传输类型。然后,它会在正确的表中查找,并将 objTransport
属性填充信息。objTransport
对象包含所有传输类型的属性。
根据 objTransport.TransportType
属性,构造一个新的接收者请求消息,并配置动态发送端口。在 SMTP 的情况下,构造形状中的消息赋值形状如下所示:
SndRequestTargetPort(Microsoft.XLANGs.BaseTypes.Address) = objTransport.Address;
SndRequestTargetPort(Microsoft.XLANGs.BaseTypes.TransportType) = objTransport.TransportType;
msgOUT = msgRecipientRequest;
msgOUT(*) = msgRecipientRequest(*);
msgOUT(SMTP.SMTPHost) = objTransport.SMTPHost;
msgOUT(SMTP.SMTPAuthenticate) = objTransport.SMTPAuthenticate;
msgOUT(SMTP.EmailBodyTextCharset) = objTransport.EmailBodyTextCharset;
msgOUT(SMTP.EmailBodyText) = "Message from BizTalk";
msgOUT(SMTP.MessagePartsAttachments) = 1; // Message Body as attachment
msgOUT(SMTP.Username) = objTransport.Username;
msgOUT(SMTP.Password) = objTransport.Password;
msgOUT(SMTP.From) = "stmbiztalk@gmail.com";
msgOUT(SMTP.Subject) = "Test from biztalk";
然后,构造的消息通过动态发送端口发送到目标系统。
接收目标响应并将其发送到父编排
将消息发送到目标的发送形状基于接收者请求中的 MsgId 和接收者响应中的 OriginalMsgId 字段初始化一个关联集。这些是提升的字段。接收者响应需要包含原始接收者请求的 MsgId 才能进行关联。(此 MsgId 在从主请求到接收者请求的映射中为每个接收者唯一。)接收形状接收响应并遵循此关联集。
发送形状通过一个端口类型为 FromChildPortType
的发送端口将响应发送回父编排。请注意,这与在父编排中接收响应时定义的端口类型相同,也是子编排中的端口参数。
通用异常处理程序
添加了一个通用异常处理程序,以便在发生错误或在超时限制内未收到响应时,将构造的响应发送回父编排。捕获异常有多种方法。在此情况下,会创建一个带有错误信息的简单响应并将其发送到父编排。
测试和安装
测试此实现所需的所有源代码都包含在下载文件中的一个解决方案中。数据库未包含在内,但在源代码中很容易看出数据库的设计方式,用于外部 .NET 程序集。
.NET 程序集需要被构建并安装到 GAC。解决方案中的所有其他项目都需要部署到 BizTalk。