Yossi Dahan [BizTalk]

Google
 

Friday, September 07, 2007

Another dive into the SOAP adapter's behaviour

Being keen to fine tune our solution's performance we turned to look into the message exchanges between the SOAP adapter and one of the web services we're usign quite extensively.

Two things became obvious very quickly (well, to be fair - they may have not been without some very useful tips from our colleagues working for the 3rd party providing that service) -

  • All request messages we've sent specify the "Expect: 100-continue" HTTP header
  • Neither of the request set out use pre-authentication, although we explicitly set credentials on the send port

so we promptly turned to look into them -

100-continue

I'm no network expert; I hold only the basic understanding of HTTP you'd expect from someone dealing with messaging and integration; the way I understand it then is that if a requester sets the "Expect:100-continue" HTTP header on a request, the client would sends the HTTP headers only to the server and then wait to receive a response with "100-continue" status.

It is only when the "100-continue" status is returned from the server that the client sends the actual request.

I also understand that, in order to support older implementations of HTTP (or those that do not support 100-continue), if the client does not receive 100-continue it will send the request anyway, but only after the delay introduced while waiting for the server to respond.

What all that means is about 200ms delay to every message sent from BizTalk.

I'm sure Microsoft have given a lot of consideration to this before deciding this would be the default behaviour of web services in .net but to us, communicating with a long-term partner over a good leased line, it would have been nice to be able to turn it off and skip the extra step.

Unfortunately, although spending some time on this we could not, as of now, find a way to do so.

Our best bet was (and still is) to set the relevant setting in the ServicePointManager in our custom web service proxy, but this does not seem to have the desired effect.

From a bit of reading we've done it seems there's some element of re-use here and that the ServicePointManager may use an existing service point, in which case our newly set property will not be used.

It could be that this is created somewhere in the adapter before our custom proxy or something like that, we truly don't know at this point.

Pre-Authenticate

Fortunately this is an area we’ve had better success in, and it's good because it has a significantly bigger impact on performance than the 100-continue problem.

The web service I've mentioned before uses basic authentication on the wire and then further elements of security in the message, so on the BizTalk port we've set the credentials the SOAP adapter should use when calling this web service, and, as you'd expect, this is working just fine.

However, analysing the traffic going out of BizTalk on the wire we've noticed that for each message we send to the 3rd party, the SOAP adapter would transmit the message out without the security HTTP headers first, receive an HTTP 401 (Unauthorised) error and only then send the request with the HTTP headers baring the credentials we've set in the send port.

Obviously, and especially when relatively large messages are involved, this has a significant impact on the performance of the server (and the farm as a whole as more bandwidth is used for the message exchange).

Again, doing some reading and experimenting, we've learnt that this is pretty much down to the choices made around the DEFAULT behaviour of .net, and again, we thought, we had no control over it.

But, while we could not do anything if we were using the standard configuration of the SOAP adapter, the fact that were using a custom web proxy to call the web service (for other reasons) meant we could try and tweak the way messages were transmitted, and tweaking we did.

It didn't help us in the 100-continue case, but it definitely helped with the pre-authentication requirement - all we had to do is add a line setting the PreAuthenticate property of the HttpWebRequest to true before using it and BizTalk (well, the .net framework web service stack it uses) would quite happily provide the credentials up front avoiding the need to duplicate the round trip to the 3rd party server.

Much better now!

Labels: , , , ,

Thursday, September 06, 2007

Unnecessary limitation when using custom proxy for web services in BizTalk 2006

We're using a third party web service quite extensively; sometimes the size of messages exchanged between us can be quite big, which is not ideal from a performance point of view.

Luckily - they are quire advanced and mature and have a good support for compressed messages exchange.

To use that we needed to compress the stream going out over the wire through the adapter and set the Content-Encoding HTTP header to "gzip" to indicate the stream is compressed, on their end, if the header is set that way, they would decompress the stream before processing the message , and will compress the response in a similar way.

They were even kind enough to provide us with a class that enhances System.Web.Services.Protocols.SoapHttpClientProtocol (through inheritance) to do all of that, so really - all we needed to do to use their web service in our playground windows application was to change the proxy class generated by Visual Studio when we added the web reference to inherit from their class rather than SoapHttpClientProtocol (their class would ultimately ingerit from SoapHttpClientProtocol).

We've played with it for a while and it worked great, which meant we were ready to move it from our playground application to our BizTalk implementation; As you can guess by the fact I'm bothering with this post - it didn't go smooth…

I've played with custom web service proxies before - generated a proxy from a WSDL and configured the port to use it - so I knew that in general principle that works quite nicely;

And so I went on and generated a proxy class for that web service and changed the inheritance to the class they've provided.

When I tried to select it in the Admin Console as the proxy class to use, I could browse to the assembly I've built but, once selected, I would get the following error:



This did not make sense to me - the proxy I generated inherits from their class which, as I have the source code, I'm sure inherits from SoapHttpClientProtocol. This error did not make sense to me.

Trying to figure this one out I went on to create my own class ("TestClass") that inherits from SoapHttpClientProtocol (and had not other implementation otherwise) and changed the generated proxy to inherit from that - so basically I created the same scenario - proxy inherits from class inherits from SoapHttpClientProtocol.

When I tried to select my updated assembly in the admin console I could not select the original proxy (now inheriting from TestClass), I could, however, select TestClass itself (which inherits directly from SoapHttpClientProtocol ) but only to get another error -



This error was quickly solved by adding a public web method to TestClass; now TestClass could be selected in the UI, my proxy class, however, the one I really needed, that does not inherit directly from SoapHttpClientProtocol, could not be selected.

It has also solved the mistery, or so I I believe - it seems the UI will only display classes whose base type is SoapHttpClientProtocol, when it should havem in my view, displayed all classes whose IsSubclassOf(typeof(SoapHttpClientProtocol)) is true.

For us the solution was simple - we took all the code we were kindly given from the 3rd party and merged it into our proxy class which could then be left to inherit directly from SoapHttpClientProtocol . The problem is that this is quite limiting - there's no real scope for re-use and for no good reason.

In fact - I suspect this is just a design time problem, of the kind we spotted quite often recently where the runtime would actually work with something, but you have no way of configuring BizTalk to use it.

Labels: , ,