On this particular day, however, I had to make a call around whether to make multiple round-trips to the web service, or whether to optimise a bit by sending all the data through at once, and perhaps batching all the records that needed to be updated. Typically, I follow the "write clean code, optimise later" train of thought, and after a few minutes of estimating volumes I decided to use the former approach, which would fit in better with the architecture of the application anyways. This decision was mainly based on rules of thumb that I have for how many transactions one should be able to perform through a web service in a second, as well as a healthy amount of pressure to move along and get things working :)
To my surprise, the solution turned out to be radically slower than I expected, effectively only achieving around 3 to 4 hits per second to the web service... in my experience this is a couple of orders of magnitude less than expected, so we decided to do some digging for the cause. We quickly ruled out several suspects - lack of caching on some validations, network latency, etc, and were left with two identical web services which had a large performance difference, but basically the same functionality. At this point, the penny dropped as we realised that the only difference was that our real web service was using Windows Authentication (which was necessary for integration to certain 3rd party applications). After some discussion, we decided that Windows Authentication would not be necessary for this web service, and after enabling anonymous access, performance was restored to around 1500 hits per second (and don't worry too much, the web service has application layer security in any case).
Nevertheless, this solution wasn't particularly satisfying - I was sure there had to be a "proper" solution, and I decided to dig a bit more. From the beginning, it was quite obvious that the additional overhead with Windows Authentication was the handshake that needed to happen when the web service was invoked. What I wasn't sure about was whether there was a simple way to do anything about it. I eventually stumbled upon an article on MSDN comparing the performance of web services against remoting, etc. In this article, I found an extremely simple solution - authenticated connection sharing. It seems that the HttpWebClientProtocol class - which is the base class for the code generated by Visual Studio when you import a web reference - has a property called UnsafeAuthenticatedConnectionSharing. When set to true, all calls made from that instance of the class will share their credentials, effectively doing away with multiple handshakes. I've tested the performance with this approach, and it's effectively identical to anonymous access.
There are a few things that I would love to hear from you guys:
- Do you use any of the standard authentication mechanisms with web services? Which one do you use and is there any specific reason?
- The MSDN documentation around the UnsafeAuthenticatedConnectionSharing refers specifically to NTLM. Does anyone know if this property will still work if Kerberos is used?