One of the cool points about Windows Communication Foundation (WCF) is that it lets you change the type of connection you’re using without changing any code. All you need are a few config changes, and hey presto! You can go from HTTP to Named Pipes faster than David Blaine can escape from a lead box full of snakes.
At least that’s the theory. Being so easy to change ‘bindings’ as they’re known in the world of WCF, I thought I’d do a bit of test to see how fast some of the these bindings were. There are many bindings to choose from, but in this case I’ve gone for HTTP (standard protocol for web services – quite verbose, with data transferred as XML), TCP (which is similar to HTTP but has a more compressed, binary data transfer) and Named Pipes (which is normally used for inter-process communication, so should be very efficient).
Using Multiple Endpoints
Although not strictly necessary for this test, one thing I wanted to try was to define a service which had several different bindings at the same time. The idea with this is that you can have a single service, but connect to it in multiple different ways – for example, use Named Pipes to call it from the same machine, TCP from within your own network, or HTTP for anything external. Or maybe you want to offer different SOAP versions for clients of differing advancement. For my test, this means I can have a single service and a single client, and connect using whatever binding I like to do my speed measurements.
So, to begin with, I created my service and added a standard HTTP endpoint along with a MEX endpoint so I could easily create the client from it. (In this case, I’m hosting the service in a simple console app host which I created). I created the client proxy using the ‘Add Service Reference’ functionality in VS2008, got that working, and then set about trying to update the config files to enable different binding types.
As it turns out, this is actually very easy. To enable this at the service end, you simply have to add some endpoints and corresponding base addresses to your config:
You don’t even need to tie them up, or do anything different in your code – to start the host it’s simply:
And bob’s your uncle! Add a bit of a printout on the host to get the service details, we now have the following (notice the three addresses with different ‘Schemes’):
That’s the service sorted – so what about the client? Well, in the normal case of the client connecting to only one service, we’d just need to put the corresponding endpoint details in – but as I want to connect to all three endpoints from the same client, I put all three in:
You might notice here that I’ve stripped the config file down to the bare bones, by removing all of the specific HTTP configuration and the MEX endpoint, just to make things clearer (although this config works fine and is what I used for the test).
In terms of opening the client, again because we’re using multiple endpoints, I’ve had to tell it which one to use by referring to the name I gave the endpoint in the config file:
So I’ve done this for each of the endpoints. Of course, if I’d wanted to be clever I could have read the names from the config file and not had to be so specific in the code – but it’s Sunday, and Top Gear’s on soon.
So that’s it! I now have a service which you can connect to over three different protocols, and a client which can use them all – and David Blaine got out ages ago…
The Speed Test
So onto the speed test. First, I’ll give you a little info about how it was done (although don’t be fooled into thinking this is in any way scientific or tightly controlled).
I created two methods, one passing a simple data type and one a more complex data type:
These methods don’t do anything except pass back the data that was passed in.
Performing the tests was simply a case of calling both of these methods in a loop once, then 10 times, then 100 times each. I also did one ‘warm-up’ call to each method before starting the timing, as WCF services take a little while to get going the first time. This was all done on my local PC, there was no networking involved.
I timed all these calls using a stopwatch, calculated the average, and that’s about all there was to it.
So, you want to know the results? Very well, but I have to give you a number of disclaimers first:
- These results are using a WCF host not IIS, which could make quite a difference
- I haven’t optimised any of the connections (e.g. by putting parameters in the config file)
- The first call was not timed, so if you’re only calling a method once, this won’t mean so much to you
Essentially, this boils down to the standard advice for performance testing – you need to try it yourself, with your particular environment and setup, making the kind of calls you make, to get any meaningful numbers. And with that, here are the meaningless numbers!:
You can see the final averages at the bottom there. Pretty much what you might expect – Named Pipes was slightly faster than TCP, both of which were considerably faster than HTTP.
Of course, it’s all in milliseconds so really not worth paying a lot of attention to, unless you happen to be making hundreds or thousands of calls – and even then, your choice is likely to be made based on security issues, what kind of network you’re using, and the types of client connecting to your service.
So my time’s been pretty much wasted then… Never mind, it’s time for Top Gear now anyway.
I’ve finally gotten around to putting a bit of time into Windows Communication Foundation (WCF), more of which to follow.
There’s one particular ‘feature’ I came across which doesn’t seem too well advertised, which I though would be worth sharing, related to how you close the connection.
Now, with most things in .Net that implement IDisposable (database connections, network connections, and so on), you simply have to call Close() or Dispose() – or more commonly, just put them in a Using statement:
The problem with this approach when it comes to WCF is that, unlike most things that you can Close(), when you call Close() on a WCF client, an exception might be thrown:
If you don’t know about the Using statement, it just calls Close() for you at the end of the code block so you don’t have to remember it. You can see the same thing happening here using Close() explicitly:
So, what’s happening here? Well, unlike some of the other things you might call Close() on, the WCF client is doing some actual work in this method. For a start, it’s connecting back to the service, which is something that could always fail due to network problems and so on – in the example above, I simply stopped the service before the client was closed. There may also be transactions to be finalised, and many other things I don’t know about.
But do we really need to care? Well, it’s hopefully not something that will happen a lot, but knowing the systems I’ve worked on, I’m sure it will happen at some point. And the chances are, you don’t really care about this exception – you’ve finished using the service anyway. So although it may be something you want to log, it probably isn’t a good enough reason to stop your app running, or disturb the user with a random message. It also means that your connection hasn’t been closed down properly, which could cause problems.
‘So what’s the fix already?!’, I hear you cry. Alright, I’m getting there! In the basic case, it’s as simple as putting a try-catch around the call to Close(), and calling Abort() on the client if something bad happens (which will make sure everything’s cleaned up properly):
And if you want to go the full hog and get one more tick in your ‘best practices’ box, you can follow this pattern (as pinched from Steve Smith’s blog):
Obviously, you can add logging in as appropriate.
This is admittedly a bit of a hassle, but you can always put it into a helper class or, as Steve suggests in the above blog, write an extension method on ICommunicationObject which will then work for all of your clients. I haven’t tried this, but it seems like a good approach.
So there you have it – one less thing to worry about with WCF! (Or is that one more thing to worry about..)