Net.MSMQ and Poison Messages

Link. May 27, 2008. Comments [0]. Posted in: Vista | WCF

The Net.MSMQ Binding in Windows Communication Foundation has some built-in support for detecting and handling "Poison Messages".

One of the most useful ways of handling Poison Messages in queuing systems is to get them out of the way so that following messages can still be processed (if you're not doing in-order processing), but still keep the messages around so that they are not lost.

Unfortunately, this behavior is only supported by WCF in MSMQ 4.0 with the use of a poison sub-queue, which is new in the Vista and WinServer2k8 versions of MSMQ. It's really a shame because, honestly, this could've been implemented on MSMQ 3.0 just by using a separate queue instead of sub-queues.

Instead, we're stuck with the PoisonErrorHandler sample included in the SDK. The sample provides a way to mimic this behavior by introducing an IErrorHandler implementation that detects the poison message and moves it to another queue. To be able to do this, you need to set the binding properties so that the ServiceHost listening to the queue faults when the poison message is detected. The error handler then moves the message to a new queue and starts a new service host instance to resume processing of following messages.

The Sample itself isn't all that bad, but does provide insight into a rather significant feature left out of the way the Net.MSMQ Binding reports poison messages: All it provides the error handler with is the MSMQ LookupId of the poison message. Unfortunately, this LookupId is queue-specific and the exception does not provide information on which queue or at least which endpoint was the one that received the poison message, which is a huge gaping hole in this feature.

Because of that the LookupId is useless without more information provided out-of-band [1]. The sample works around this limitation by adding an entry in the <appSettings/> section of the app.config file with the path to the queue where messages are being received and the path to the queue to move poison messages to.

This works OK, until you need a single service host listening on multiple endpoints, and then the sample won't really work anymore because you don't know which of the endpoints caused the error.

Working around the limitation

I spent some time recently playing around with WCF and ran into this same problem, so I tried to find a way of working around this limitation and still use the basic functionality the sample provided.

I found one possible workaround, which seems to work so far in my limited tests. I'm not sure yet how well it will hold, as it seems to me there's always the possibility of some race conditions here, but let me illustrate at least the basic mechanics.

First we keep the basic code of the PoisonErrorHandler class. However, instead of using appSettings to keep track of the queue we're listening to, we just try to find out dynamically which endpoint configured for the service is causing the error, and extract the path to the queue from there based on the URI of the endpoint address.

To do this, in my implementation of IErrorHandler.HandleError() I grab the current OperationContext, reach out to the ServiceHost associated with it, and then iterate over the ChannelDispatchers attached to it. The one that's in the Faulted state is very likely the one that caused the MsmqPoisonMessageException in the first place.

Code could be something like this:

private String FindFaultedQueue() {
   ServiceHostBase host = OperationContext.Current.Host;
   Uri faultedQueue = null;
   // the queue that fired the exception will be the faulted one
   foreach ( ChannelDispatcher chd in host.ChannelDispatchers ) {
      if ( chd.State == CommunicationState.Faulted ) {
         faultedQueue = chd.Listener.Uri;
      }
   }
   // translate URI into a regular MSMQ format name 
   StringBuilder fn = new StringBuilder("FormatName:DIRECT=OS:");
   fn.Append(faultedQueue.Host).Append("\\");
   string path = faultedQueue.PathAndQuery;
   if ( path.StartsWith("/private/") ) {
      fn.Append("private$\\");
      path = path.Substring("/private".Length);
   }
   path = path.Substring(1);
   fn.Append(path);
   return fn.ToString();
}

This lacks some error handling and a few other things, but it illustrates the point. Now that we have the FormatName of the queue where the poison message came from, we can try to move it to the poison message  queue. We could handle this by convention: for queue Q1, the corresponding poison message queue could be Q1Poison. The rest of the sample is pretty much the same.

I probably wouldn't use this in production, as it doesn't feel very reliable given the basic limitations imposed by the WCF implementation, but it was interesting to take a stab at. YMMV.

[1] I'll leave to you, dear reader, to wonder about the sense of providing a sample that so clearly points out such a giant hole in a product feature rather than actually fixing it. Oh wait, guess they did fix it by changing MSMQ for Vista and WinServer2k8.

Retrying Web Service Calls in BizTalk

Link. May 22, 2008. Comments [0]. Posted in: BizTalk | WCF

BizTalk Server has some pretty strong facilities for making a "best effort" at ensuring message delivery, by using the automatic retry facilities available to BizTalk Send Ports.

This functionality is available when calling Web Services as well either with the WCF or SOAP adapters, and it's very useful. However, it's not always enough by itself.

One such scenario comes up when the Web Service you're calling does not return SOAP Faults when it fails, but instead returns some kind of status/result indicator as part of a normal response message. In that case, the SOAP/WCF adapter can't tell on its own that the service call failed and retry it as necessary.

There are a few ways you can deal with this.

For example, you can handle retries manually from an orchestration, using a Loop shape to call the service, check the result and retry the call as necessary. It's not complex (though a bit cumbersome) and in many scenarios works very well.

Unfortunately, this isn't an option if you are in a messaging-only scenario, or have complex routing rules leading to the service invocation (common with direct-binding scenarios).

One possible workaround for this if you're using the WCF Adapter in R2 that I've been experimenting lately with is to use the WCF-Custom adapter and configure a custom Endpoint Behavior that adds an IClientMessageInspector implementation that checks the response message and transforms it to a fault if it indicates.

BTSWCFFault

A few things to keep in mind if you decide to try this:

  1. For your behavior to be listed on the BizTalk Administration console, you'll need to implement a BehaviorExtensionElement for it, deploy it to the GAC and register it in machine.config (or at least this was the way I did it).
  2. Inside your IClientMessageInspector.AfterReceiveReply() implementation, you might need to create a buffered copy of the message and return a new Message instance built from it to the WCF Adapter runtime; otherwise BizTalk might not be able read the message because you've already consumed it and it will be in the closed state by then.
  3. Make sure you leave the "Propagate Fault Message" option unchecked; otherwise your fault will just be submitted to BizTalk as a regular message and won't cause the send port to retry the call.

My friend Carlos suggested that another way to deal with this issue (particularly if you're not using the WCF adapter) might be by building a custom pipeline component and using it in a receive pipeline configured in the Send Port. The component would then read the response from the Web Service and throw an exception to force a failure during the response processing if necessary, thus causing the entire exchange to fail. I haven't tried this myself, but sounds like another useful idea!

A Generic WCF Service for BizTalk

Link. May 19, 2008. Comments [4]. Posted in: BizTalk | WCF

I've been playing lately with a few WCF features as well as with the WCF Adapter in BizTalk Server 2006 R2. As part of that I had a need to set up a receive location using one of the WCF HTTP-based bindings.

Normally, this isn't a big deal; you can easily use the BizTalk WCF Service Publishing Wizard to create the receive location and IIS service application for an orchestration or from a set of BizTalk schemas.

However, this time I did not want to go that way. What I really wanted was just a simple receive location that would simply receive messages and submit them over to BizTalk without tying the receive location to a specific service definition. As far as I could see, the Service Publishing Wizard didn't really have an option for this, but I was confident it could be made to work.

To be honest, I did have a set of schemas I wanted to work with and even a bunch of predefined WSDL files. However, for many reasons (including the sheer complexity of the schemas and WSDL files involved) I didn't want to get that involved in my initial BizTalk message receiver and processor.

Fortunately, turns out that doing what I wanted was fairly easy stuff. In fact, it was so easy that was able to leverage an existing service I had previously created. I basically just copied the .SVC file (and renamed it) alongside the web.config file. I explicitly ignored all the stuff that normally gets generated under the App_Data directory.

I then manually created a matching receive location in BizTalk using the WCF-BasicHTTP adapter, and this worked right away perfectly for my needs!

Just in case you've never looked at what a WCF HTTP receive location generated files look like, it's actually fairly simple stuff. The SVC file contains just a single directive as expected:

<%@ ServiceHost Language="c#" Factory="Microsoft.BizTalk.Adapter.Wcf.Runtime.BasicHttpWebServiceHostFactory, Microsoft.BizTalk.Adapter.Wcf.Runtime, Version=3.0.1.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" %>

You can see here that it references the WCF-BasicHTTP adapter. If you wanted to use, say, the WCF-WSHttp Adapter, then you'd use Microsoft.BizTalk.Adapter.Wcf.Runtime.WSHttpWebServiceHostFactory class instead.

The config file is pretty much the default config file generated by the Service Publishing Wizard as well:

<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
   <configSections>
      <section 
         name="bizTalkSettings" 
         type="Microsoft.BizTalk.Adapter.Wcf.Runtime.BizTalkConfigurationSection,
            Microsoft.BizTalk.Adapter.Wcf.Runtime, Version=3.0.1.0,
            Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
   </configSections>
   <bizTalkSettings>
      <mexServiceHostFactory debug="false">
         <receiveLocationMappings>
            <!--add markupFileName="*.svc"
               receiveLocationName="?"
               publicBaseAddress="protocol://host[:port]" /-->
         </receiveLocationMappings>
      </mexServiceHostFactory>
      <webServiceHostFactory debug="false" />
      <isolatedReceiver disable="false" />
      <btsWsdlExporter disable="false" />
   </bizTalkSettings>
   <appSettings />
   <connectionStrings />
   <system.web>
      <compilation defaultLanguage="c#" debug="false">
         <assemblies>
            <add assembly="mscorlib, version=2.0.0.0, culture=neutral,
               publickeytoken=b77a5c561934e089" />
            <add assembly="Microsoft.BizTalk.Adapter.Wcf.Common, Version=3.0.1.0,
               Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
            <add assembly="Microsoft.BizTalk.Adapter.Wcf.Runtime, Version=3.0.1.0,
               Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
         </assemblies>
      </compilation>
      <authentication mode="Windows" />
   </system.web>
   <system.serviceModel>
      <behaviors>
         <serviceBehaviors>
            <behavior name="ServiceBehaviorConfiguration">
               <serviceDebug 
                  httpHelpPageEnabled="true" 
                  httpsHelpPageEnabled="false" 
                  includeExceptionDetailInFaults="false" />
               <serviceMetadata 
                  httpGetEnabled="false" 
                  httpsGetEnabled="false" />
            </behavior>
         </serviceBehaviors>
      </behaviors>
      <services>
         <service 
            name="Microsoft.BizTalk.Adapter.Wcf.Runtime.BizTalkServiceInstance" 
            behaviorConfiguration="ServiceBehaviorConfiguration">
            <!--<endpoint
               name="HttpMexEndpoint"
               address="mex"
               binding="mexHttpBinding"
               bindingConfiguration=""
               contract="IMetadataExchange" />-->
            <!--<endpoint
               name="HttpsMexEndpoint"
               address="mex"
               binding="mexHttpsBinding"
               bindingConfiguration=""
               contract="IMetadataExchange" />-->
         </service>
      </services>
   </system.serviceModel>
</configuration>

It's extremely nice to see how the WCF adapter in BizTalk leverages a bunch of my favorite features in WCF to make this a lot simpler (compared to, say, the whole bunch of code that needed to be generated for the original SOAP adapter).

I should mention though, that part of what made it so easy was that my needs were pretty simple: I wanted a simple two-way (request/response) port, and needed no metadata (WSDL) publishing at all (as I said, I already had working WSDL files I could provide to consumers of the service).  Making it one-way wouldn't have been a problem though; as the WCF adapter handles it very gracefully as well.

BasicHttpBinding with Transport Security

Link. November 14, 2007. Comments [0]. Posted in: WCF | Web Services

This is just something to remind myself next time I need to implement a WCF service using the BasicHttpBinding with transport-only security (SSL + client certificates)':

  • WCF expects that the "Require SSL" option in IIS is enabled.
  • If Metadata Exchange is needed, configure the MEX endpoint with a custom binding with the right settings; otherwise the service won't start.
  • Remember to set httpsGetEnabled="true" option in the <serviceMetadata> service behavior instead of the usual httpGetEnabled attribute.
  • WCF does not understand the "Accept Client Certificates" option in IIS. It treats it the same as "Ignore Client Certificates" option.

To use client certificates:

    • Enable the "Require Client Certificates" option in IIS
    • On the service side, use a custom binding to set the requireClientCertificate option of the httpsTransport binding element:

      <bindings>

         <customBinding>

            <binding name="sslBasicHttp">

               <textMessageEncoding messageVersion="Soap11" />

               <httpsTransport requireClientCertificate="true"/>

            </binding>

         </customBinding>

      </bindings>

    • On the client side, use basicHttpBinding with the security mode set to "Transport" and the option clientCredentialType set to "Certificate" on the <transport/> element.

SvcUtil Generated Config

Link. September 28, 2007. Comments [2]. Posted in: WCF

Scott Seely seems to object to my previous comment on being careful about the defaults used by svcutil.exe. Scott goes on to describe in detail how svcutil figures out what should get written in the default configuration, which is pretty useful.

But, seems to me like Scott may have misunderstood my comment. Yes, I do know how svcutil.exe works and even why it chooses the defaults it chooses. I'm not arguing against it configuring default values (even for all binding properties, as cumbersome as that can be). I'm arguing against the rationale of the default values themselves, as they make little sense for a client side proxy.

Don't get me wrong, I fully agree with Scott's comment that "Bigger defaults would have made it easier to blow up your average WCF endpoint, and that wouldn't have been a good thing", but that's an assertion that makes a lot of sense for a service endpoint. A client side proxy, on the other hand, will rarely require such stringent settings and in fact it constantly gets in the way (only case where this would be significant is in the case of duplex contracts).

In other words, I'm arguing for SvcUtil being slightly more smart about the default values depending on the context. And even more, I'm arguing that the default security constraints configured by default on WCF bindings (SvcUtil or no SvcUtil) are too small. I'm not saying to make them unlimited, but they could benefit from slightly larger values, particularly for WSHttpBinding and friends.

But as long as we're on the topic, let's add one more to the mix: The truth is, that these kind of security constraints are sitting right in no man's land. Developers will very rarely look at them (unless they are already aware of the issue) simply because they test their services in limited environments, where, for example, messages might be small enough to cause no trouble. But then they go blow up in their faces in the production environment. Administrators on the other end, have no clue what this settings are or what they configure (in fact, they have no clue they even exist) and will simply ignore them... until they blow up in their faces as well. This is not a good situation by any means, because you know what people will do? They'll just get used to configure them always to the maximum possible value.

About

Tomas Restrepo is co-founder of devdeo. His interests include .NET, Connected Systems, PowerShell and, lately, dynamic programming languages. More...

email: tomas@winterdom.com
msn: tomasr@passport.com
twitter: tomas_restrepo

Technorati Profile

devdeo logo

View my profile on LinkedIn

MVP logo

Syndicate

Ads


Links

Categories

Statistics

Total Posts: 1006
This Year: 76
This Month: 7
This Week: 0
Comments: 771

Blogroll

Post Archive

Other

Copyright © 2002-2008, Tomas Restrepo.

Powered by: newtelligence dasBlog 1.9.7174.0

Sign In