a trace of thought on...BizTalk Server, Team Foundation Server, Windows Mobile, etc. RSS 2.0
 Friday, November 04, 2005

This great post from Tomas on receiving ack/nack notifications from the MSMQ[C] adapter prompted me to write up something I had researched a few weeks back...

But first, a bit of background...

When doing work with message queueing systems, a very common (and well supported) convention exists when two systems are exchanging messages: System 'A' sends a message to System 'B'.  When System 'B' wishes to reply, it uses the message identifier of the 'request' message as the correlation identifier of the 'reply' message.  The reply queue used is either well known by both parties, or is communicated as a property on the request message.  For purposes of discussion, let's call this the "queueing contract". 

Both MSMQ and MQSeries have MessageId and CorrelationId properties on messages.  When looking at messages in a reply queue, System 'A' can peek at the messages, looking for a particular CorrelationId prior to issuing a receive.  Alternatively, it might use the CorrelationId as a look-up into working state of some sort.  To facilitate request-response interactions, MQSeries has had a ReceiveByCorrelationId API for a long while, whereas MSMQ added this in 3.0 (that is, on Windows XP and Windows 2003.) (Actual property and method names may vary in real life...)

There are, of course, many deployed systems in the wild that implement the queueing contract - using both MSMQ and MQSeries.  You may find yourself needing to integrate with these.  At first blush, it seems that consuming a service such as this should be a breeze from within BizTalk orchestrations - after all, accomplishing correlation is a first-class-citizen feature for BizTalk, so how tough can this be?

Correlation in BizTalk is indeed a first-class citizen...for data within your documents (or promoted message context.)  The classic example is to define a property schema that embodies a business identifier like "PONumber", and then visit all schemas that will be used across a set of message interactions - promoting the appropriate field in each as a "PONumber".  Then, a correlation type and correlation set are defined which reference that property, and Send and Receive shapes are set to either initialize or follow the correlation as appropriate.

The problem with an orchestration consuming the queuing contract we described earlier is that this contract occurs at the transport layer.  To be a consumer of the queueing contract, an orchestration would like to send a message, and initialize a correlation based on the MessageId...but the MessageId is produced further downstream, at the point an actual MSMQ or MQSeries message is generated by an adapter.  Moreover, an orchestration would subsequently like to receive a message, following an initialized correlation - but the CorrelationId isn't promoted by the MSMQ adapter (though it is for the MQSeries adapter...)

Solving for the MSMQ Adapter

The MSMQ Adapter gives us a small amount of help to get past this...We can in fact do our initial send operation through a solicit-response port (rather than a one-way port) and get back the MessageId that was generated by the adapter -- it actually comes back in a single-element document with a tag named "MSMQMsgId".  Starting here, the approach I took to solving the whole of the problem looked like this:

  1. Send a message through a MSMQ solicit-response port.  The response operation in the orchestration designer can be of type "String".
  2. On the response half of the solicit-response port, execute a pipeline & pipeline-component that extracts the returned MessageId and promotes it as the CorrelationId (using the standard MSMQ adapter namespace for that property.)  (For tracing purposes & useability, the MessageId is also returned in a <string> wrapper by the pipeline component rather than the native <MSMQMsgId> wrapper.)
  3. When the initial message (containing the correlation id in both content and context!) is received back into the orchestration, it is sent back out to initialize a correlation set (typed as MSMQ.CorrelationId.)  This Send shape is just a "dummy send"...it exists just to initialize the correlation, because that is the only means given to us in the orchestration world.  I used Patrick Wellink's "NOPE" Adapter for this purpose, but there would be other options as well (including Tomas' Null Send Adapter, which I just recently rediscovered and which apparently performs better under load.)
  4. Next, we receive the "real" response message - following the correlation that was just initialized.  Because MSMQ.CorrelationId is not promoted by default, the response is brought through a second pipeline component that performs that service for us.

See the diagram below (and the downloadable sample) for more detail. 


(click)

What can go wrong with this approach?  Well...there is race condition that can occur if the "real" response message (step 4 above) arrives before steps 2 and 3 can execute and establish the correlation (and associated instance subscription.)  In other words, if the "real" response message comes back before the orchestration has had a chance to express interest in that particular message, a routing failure will occur.  This might be highly unlikely for your scenario (given response times of the service you are interacting with) but it is something to test for, nonetheless.  (With BizTalk 2006's ability to route failed messages, you could potentially catch and retry in this scenario...)

(Note - if you are going to rely on the technique in the sample, consider asking Microsoft support for BizTalk 2004 QFE 1647, which addresses an issue with truncated correlation identifiers being returned to BizTalk...)

Solving for the MQSeries Adapter

Consuming the queueing contract with the MQSeries adapter for BizTalk can be done with a technique quite close to the one just discussed - but with a little less work.  The response to the initial solicit-response interaction returns with MQMD_MsgID already copied to a  MQSeries.BizTalk_CorrelationId property.  Likewise, the when receiving the "real" response message, MQMD_CorrelationId is copied to the MQSeries.BizTalk_CorrelationId property.  This means all correlation can be done with this one property - no pipelines/pipeline-components are needed!

From the documentation:


(click)

(The "dummy send" - though not pictured - would be required here as well to initialize the correlation set.)

In addition, there is a much cleaner solution that the BizTalk 2004 MQSeries adapter provides.  It turns out that MQSeries allows the "caller" to assign the message ID (unlike MSMQ), so within BizTalk, you can in fact set MQMD_MsgID and MQMD_CorrelationId to the same value prior to doing your initial send.  Now, you can initialize a correlation set based on MQMD_CorrelationId with the first outgoing message, and follow the correlation for the "real" response message.  Slick!

From the documentation:


(click)

Solving for the MQSeries Adapter in BizTalk 2006

Ahhhh, here we have what looks like real simplicity...There is a new feature in the BizTalk 2006 MQSeries adapter termed "Dynamic Receive" (referenced in the BizTalk 2006 Adapter Enhancements document.)  For a solicit-response port, Dynamic Receive allows context properties on the outbound message to determine which server, queue manager, and queue should be "watched" for the response message.  This is used in conjunction with a "match" option - which allows you to wait for a particular message based on CorrelationID (or any of several other properties, such as MessageID, GroupID, SequenceNumber, Offset, or MsgToken...)

Not only does this give us quite a bit of flexiblity for where response messages should be found (we don't need a fixed receive location anymore) but it also elminates the whole of the problem of implementing the "queueing" contract within orchestrations.  With this feature in place, an orchestration simply uses one solicit-response port, and the work is done.

Needless to say, it would be fantastic to get similar support from the MSMQ Adapter (at least the ability to have a single solicit-response port with a similar "match" criteria option, even without the dynamic receive location.)  But I don't think that is in the cards for BizTalk 2006...

Notes on the download:

  • See the BTMSMQCorrelation.PortBindings.xml file for the names of the two queues and two file directories that must be created for this sample.
  • Install Patrick Wellink's "NOPE" Adapter or choose a different strategy for dummy sends (like Tomas' Null Send Adapter.)
  • Use the included Deployment Framework-based deployment, or deploy manually.
  • To run, launch the "TestQueueServer" console app that will implement the queueing contract (request/response)
  • The sample will import and run just fine in BizTalk 2006, though you will have to deploy manually.  (Still working on the 2006 version of the deployment framework - more later...)

Hopefully this is helpful to those doing queueing work with BizTalk - enjoy!

Friday, November 04, 2005 4:10:26 PM (Central Standard Time, UTC-06:00)  #    Comments [0] -
BizTalk Insights
Comments are closed.
Archive
<July 2008>
SunMonTueWedThuFriSat
293012345
6789101112
13141516171819
20212223242526
272829303112
3456789
About the author:

Scott Colestock lives, writes, and works as an independent consultant in the Twin Cities (Minneapolis, Minnesota) area.

© Copyright 2008
Scott Colestock
Sign In
All Content © 2008, Scott Colestock
DasBlog theme 'Business' created by Christoph De Baene (delarou)