考虑这样一种情况:用orchestration处理一个order,在流程中做了处理后通过一个发送端口发送到商业伙伴那里,然后需要明确的知道order确实通过发送端口发送到商业伙伴之后才能继续之后的流程处理,如果发送order没有成功,则在orchestration中做异常处理。
按照biztalk的正常的消息发布订阅机制,上述的情况是物理发送端口订阅orchestration的逻辑发送端口发送的order消息,当orchestration把order消息发送到Messagebox,消息引擎根据订阅关系,把order消息发送到订阅这个order的物理发送端口,至此,orchestration发送order的过程就算完成,至于物理发送端口是否把消息顺利的发送出去(可能顺利的发送了出去,可能在发送端口内部出错,也可能从端口发送出去后从另一端返回错误信息)orchestration无从知道。
针对这种情况,biztalk提供一种反馈机制,发送端口(单向发送端口或者双向发送端口)可以根据需要返回发送成功的ACK消息(Positive Acknowledgment)和发送失败的NACK消息(Negative Acknowledgment),如果发送端口成功的把消息发送出去了,则可以返回ACK消息,如果发送端口发送消息失败(不管是发送端口内部出错还是发送出去后收到错误信息),则返回NACk消息。Orchestration收到ACK或者NACK消息后,就可以知道消息是否正常的发送出去了,根据得到的反馈消息,进行之后的流程。一般的收到ACK反馈消息,流程正常的继续下去,收到NACK消息,orchestration会生成一个DeliveryFailureException异常,在orchestration中可以设计一个异常捕获,捕获DeliveryFailureException异常后进行异常处理。
深入分析反馈机制
要使用发送端口反馈机制,需要做一些设置。
l 逻辑发送端口的Delivery Notification属性设置为 Transmitted
这是告诉biztalk消息引擎,发送出去的消息是需要反馈消息的。
l Send shap所在scope的Synchronizd属性设置为true
(但是测试表明,这个属性跟ACK或NACK的反馈关系不大)
l Scope形状可以设置Exception Handler捕获DeliveryFailureException异常
如果从物理发送端口返回的NACK消息,那么orchestration将在这个scope的位置产生一个DeliveryFailureException异常,可以被Scope形状的Exception Handler捕获,进而进行进一步的异常处理。
下面详细分析一个消息在orchestration被发送出去后,到orchestration收到ACK消息或者NACK消息的整个过程的底层机制。
一、 orchestration中消息从逻辑端口发出
消息在orchestration到达逻辑发送端口时,因为逻辑发送端口的Delivery Notification属性设置为 Transmitted,biztalk将会做两件事:
1、 在消息的上下文属性中增加一个系统属性:AckRequired = true
在将要发送出去的消息的上下文属性中,增加一个系统属性AckRequired = true,这个属性的作用是告诉接收这个消息的物理发送端口,此消息是需要反馈消息的,物理发送端口在收到的消息中发现有这个属性,就会在端口发送消息成功后形成ACK消息,在发送失败后生成NACK消息,并发布消息。
如果逻辑发送端口的Delivery Notification属性设置为none,消息中将不会包含AckRequired = true的系统属性,物理发送端口也不会生成ACK或NACK消息并发布。
2、 在逻辑发送端口位置新建一个实例订阅,订阅发送端口返回的ACK或NACK消息。
首先在逻辑发送端口处初始化一个correlation token(相关标识)赋给出站消息的上下文的系统属性CorrelationToken。
之后新建一个实例订阅,订阅者为这个orchestration服务实例的逻辑发送端口。看一下数据库Subscription订阅表中的一个此类实例订阅的一个示例:
uidSubID(此实例订阅的uid) -- 2b
uidClassID(产生订阅的服务类型) -- 226fc6b9-0416
uidServiceID(产生此订阅的具体服务)-- 63eb391b-37ee-3243-56b1-e93e8bd7ec05(这个orchestration的guid)
uidPortID(表示业务流程中订阅消息的端口shape)-- 63eb391b-37ee-3243-5622-7452edd13162(发送消息的逻辑端口,ACK或NACK还是由它来接收)
uidInstanceID(产生此订阅的实例的guid)-- ba1eff99-c1b2-447b-882d-d391e1b17b
这个实例订阅的条件在messagebox数据库中的EqualsPredicates表中,看一下这个实例订阅的订阅条件示例,条件就一个,消息的系统属性CorrelationToken为相关集的token:
uidPropID(属性guid)-- 593badb0-43fb
vtValue(属性的值) -- 31e
在给出站消息添加了系统属性AckRequired和CorrelationToken后,orchestration把消息发布到messagebox,同时生成一个实例订阅,订阅系统属性CorrelationToken相符的消息。在消息发布出去后,直到直接外层long running scope结束处,等待ACK或者NACK消息的返回。
二、 消息在发送端口
物理发送端口订阅了orchestration逻辑发送端口的消息,在收到消息后,物理发送端口发现有AckRequired和CorrelationToken属性,知道这个消息是需要提供反馈的。
物理端口执行正常的发送工作,将收到的消息经过发送管道,发送适配器发送出去。
如果消息成功的发送了出去,物理发送端口就会生成一个ACK消息,这个ACK消息只有上下文属性,没有消息正文,ACK包含的属性有:
属性名
|
描述
|
BTS.AckFailureCategory
|
Identifies the ErrorCategory, which gives the place and reason for the suspension.
|
BTS.AckFailureCode
|
Identifies the ErrorCode, which gives the place and reason for the suspension.
|
BTS.AckType
|
Value is ACK for a positive acknowledgment and NACK for a negative acknowledgment.
|
BTS.AckID
|
Identifies the MessageID of the original message.
|
BTS.AckOwnerID
|
Identifies the instance ID from the original message.
|
BTS.CorrelationToken
|
Identifies the correlation token from the original message if one is present.
|
BTS.AckDescription
|
Identifies the ErrorDescription, which gives the place and reason for the suspension.
|
BTS.AckSendPortID
|
Identifies the SendPortID from the original message.
|
BTS.AckSendPortName
|
Identifies the SendPortName from the original message.
|
BTS.AckOutboundTransportLocation
|
Identifies the OutboundTransportLocation from the original message.
|
BTS.AckReceivePortID
|
Identifies the ReceivePortID from the original message.
|
BTS.AckReceivePortName
|
Identifies the ReceivePortName from the original message.
|
BTS.AckInboundTransportLocation
|
Identifies the InboundTransportLocation from the original message.
|
其中的CorrelationToken属性就是发送端口从接收到消息上下文属性中这个属性获取的。发送端口发布这个ACK消息后,orchestration要靠匹配这个属性获得ACK消息。
如果消息发送失败,包括消息在物理发送端口内部出错,消息发送出了物理发送端口后从对方收到了错误信息,发送端口都会生成一个NACK消息。NACK消息跟ACK消息一样拥有上述的上下文属性,另外NACK还有一个SOAP fault的消息正文,一般形式如下:
<?xml version="1.0" encoding="utf-8"?>
<SOAP:Envelope xmlns:SOAP="http://schemas.xmlsoap.org/soap/envelope/" SOAP:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP:Body>
<SOAP:Fault>
<faultcode>Microsoft BizTalk Server Negative Acknowledgment </faultcode>
<faultstring>An error occurred while processing the message, refer to the details section for more information </faultstring>
<faultactor>C:\Projects\Sample\Locations\Response\FM_%MessageID%.xml</faultactor>
<detail>
<ns0:NACK Type="NACK" xmlns:ns0="http://schema.microsoft.com/BizTalk/2003/NACKMessage.xsd">
<NAckID>{FFB
<ErrorCode>0xc
<ErrorCategory>0</ErrorCategory>
<ErrorDescription>There was a failure executing the send pipeline: "Microsoft.BizTalk.DefaultPipelines.XMLTransmit, Microsoft.BizTalk.DefaultPipelines, Version=
</ns0:NACK>
</detail>
</SOAP:Fault>
</SOAP:Body>
</SOAP:Envelope> |
三、 ACK或NACK返回orchestration
发送端口发布ACK或NACK消息后,根据实例订阅的订阅条件(CorrelationToken),消息引擎把ACK或NACK消息路由到发送消息的那个orchestration服务实例。
如果接收到的是ACK消息,orchestration从之前停留等待反馈消息的发送形状那里继续按照正常的流程继续下去。
如果接收到的是NACK消息,orchestration在发送形状所在的scope产生一个DeliveryFailureException异常,异常可以被scope的exception handler捕获,在异常处理中做相应的处理。
在异常处理中可以:
// 把DeliveryFailureException类型转换成 SoapException类型
System.Web.Services.Protocols.SoapException se = (System.Web.Services.Protocols.SoapException)e.InnerException;
System.Diagnostics. EventLog.WriteEntry (“NACK内容”,se.Detail.InnerXml);
下面就是可以在windows日志中看到返回的NACK详细内容的xml:
<ns0:NACK Type="NACK" xmlns:ns0="http://schema.microsoft.com/BizTalk/2003/NACKMessage.xsd">
<NAckID>{BD6682EE-1741-4856-8CC7-B2EE36B7874E}</NAckID>
<ErrorCode>0xc
<ErrorCategory>0</ErrorCategory>
<ErrorDescription>The FILE send adapter cannot open file C:\Foo\DeliveryNotification\out\{
</ns0:NACK>
以上过程就是一个设置了Delivery Notification属性的发送端口发送消息的整个过程。
此外,ACK或NACK消息处理除可以被发送消息的orchestration本身接收外,还可以设计一个专门的orchestration,根据NACK消息的系统属性,通过Content-based的路由NACK消息,接收biztalk应用中所有的NACK消息,然后进行相关的处理,比如统一记入错误日志,做错误统计等等。
参考:
biztalk文档Development > Developing BizTalk Server Applications > Using the BizTalk Messaging Engine > Error Handling > Using Acknowledgments
Kevin B Smith 《Acknowledgments and Negative Acknowledgments (Part 1)》