Want to follow this site? Here's the RSS feed.

WCF Relative Binding Speeds

Saturday, January 28, 2006

My latest infrastructure design consists of a few services spread over various servers and with many clients in various locations. Some of the clients will be on the same machine as the service, others will be on the same NT-based network, others will be WSE3 services, and even others will be PHP based. I wanted to provide the best solution for each, ergo my love for the Go-live of WCF! With WCF I can have the services expose multiple endpoints to allow a diverse number of clients each with their own maximum efficiency (remember, if you aren't measuring, then you aren't engineering!) PHP clients for example will get a BasicHttpBinding endpoint, while clients on the same machine as the service will use the NetNamedPipeBinding.

What do the speeds look like for each of these bindings? I went ahead and wrote a quick test harness to see the relative speeds of each binding. These are very simple tests, but they do demonstrate how the speeds of the bindings compare with each other. Without a ton of needless talk, let's get right into what's going on...

Here is the service configuration file...

  <system.serviceModel>
    <services>
      <service type="WcfDemo.DemoService">
        <endpoint
          address="WsDualHttpBinding"
          contract="WcfDemo.IDemoService"
          binding="wsDualHttpBinding"
          />
        <endpoint
          address="WsHttpBinding"
          contract="WcfDemo.IDemoService"
          binding="wsHttpBinding"
          />
        <endpoint
          address="BasicHttpBinding"
          contract="WcfDemo.IDemoService"
          binding="basicHttpBinding"></endpoint>
        <endpoint
          address="NetTcpBinding"
          contract="WcfDemo.IDemoService"
          binding="netTcpBinding"
          />
        <endpoint
          address="NetNamedPipeBinding"
          contract="WcfDemo.IDemoService"
          binding="netNamedPipeBinding"></endpoint>
      </service>
    </services>
  </system.serviceModel>

Here is the significant portion of the service code...

Uri netTcpAddress = new Uri("net.tcp://localhost:8080/");
Uri httpAddress = new Uri("http://localhost:8081/");
Uri netPipeAddress = new Uri("net.pipe://localhost/");
using (ServiceHost service = new ServiceHost(typeof(DemoService), new Uri[] { netTcpAddress, httpAddress, netPipeAddress })) {
        service.Open( );

        Console.WriteLine("Listening...");
        Console.ReadLine( );
}

Notice the netPipeAddress is using "net.pipe" as the address scheme and doesn't have a port. This is actually for the netNamedPipeBinding binding which is for inner process communication on the same machine, so there wouldn't be a port for that. The netTcpBinding binding does however have a port as it would really be out on the network somewhere. Also keep in mind that you add a URI for each address scheme, not each address.

Here is the client configuration file...

<system.serviceModel>
    <client>
      <endpoint
        address="http://localhost:8081/WsDualHttpBinding"
        binding="wsDualHttpBinding"
        name="WSDualHttpBinding"
        contract="IDemoService" />
      <endpoint
        address="http://localhost:8081/WsHttpBinding"
        binding="wsHttpBinding"
        name="WSHttpBinding"
        contract="IDemoService" />
      <endpoint
        address="http://localhost:8081/BasicHttpBinding"
        binding="basicHttpBinding"
        name="BasicHttpBinding"
        contract="IDemoService" />
      <endpoint
        address="net.tcp://localhost:8080/NetTcpBinding"
        binding="netTcpBinding"
        name="NetTcpBinding"
        contract="IDemoService" />
      <endpoint
        address="net.pipe://localhost/NetNamedPipeBinding"
        binding="netNamedPipeBinding"
        name="NetNamedPipeBinding"
        contract="IDemoService" />
    </client>
  </system.serviceModel>

Again notice there is no port on the netNamedPipeBinding binding and there is a port on the netTcpBinding. Also notice that each of the addresses end in the name of the endpoint specified by the server; the endpoints also have a name attribute to be accessible by the client-side code as seen in the next code block.

Here is how I start my client code...

DemoServiceClient wSDualHttpClient = new DemoServiceClient("WSDualHttpBinding");
DemoServiceClient wSHttpClient = new DemoServiceClient("WSHttpBinding");
DemoServiceClient basicHttpClient = new DemoServiceClient("BasicHttpBinding");
DemoServiceClient netTcpClient = new DemoServiceClient("NetTcpBinding");
DemoServiceClient netNamedPipeClient = new DemoServiceClient("NetNamedPipeBinding");

My actual benchmarking code consists of a simple timer that look like this...

Person person = new Person( );
DateTime a = DateTime.Now;
int i = 0;
while ((DateTime.Now - a).Seconds < 10) {
    wSDualHttpClient.RecordPerson(person);
    i++;
}
Console.WriteLine("WSDualHttpBinding: Processed {0} calls in 10 seconds", i);

That basically just means I want to run as many tests as possible in 10 seconds.

I actually ordered the tests based on my predictions of their relative speeds and it turns out my predictions were correct. Take a look for yourself...

WSDualHttpBinding: Processed 1602 calls in 10 seconds
WSHttpBinding: Processed 2531 calls in 10 seconds
BasicHttpBinding: Processed 17913 calls in 10 seconds
NetTcpBinding: Processed 39957 calls in 10 seconds
NetNamedPipeBinding: Processed 48255 calls in 10 seconds

This isn't really that surprising. The more complex web services protocols are slower than the most basic ones, which are in turn slower than a direct TCP connection, which in turn is slower than the named-pipes binding connection for use on the same machine.  So, if you are going to be doing something chatty, you may want to think twice before using SOAP.

That was actually an out-of-the-box benchmark; that is, I didn't customize the bindings in any way. In another test I turned off security for the WsDualHttpBinding and WsHttpBinding bindings.

First, I added the following binding sections to both the service and the client configuration files.

<bindings>
  <wsDualHttpBinding>
    <binding name="noSecurity">
      <security mode="None"></security>
    </binding>
  </wsDualHttpBinding>
  <wsHttpBinding>
    <binding name="noSecurity">
      <security mode="None"></security>
    </binding>
  </wsHttpBinding>
</bindings>

Then I altered the endpoints to point to the appropriate binding. Here is the service configuration alteration.

<endpoint
    address="WsDualHttpBinding"
    bindingConfiguration="noSecurity"
    contract="WcfDemo.IDemoService"
    binding="wsDualHttpBinding"
    />
<endpoint
    address="WsHttpBinding"
    bindingConfiguration="noSecurity"
    contract="WcfDemo.IDemoService"
    binding="wsHttpBinding"
    />

here is the client configuration alteration.

<endpoint
    address="http://localhost:8081/WsDualHttpBinding"
    binding="wsDualHttpBinding"
    name="WSDualHttpBinding"
    bindingConfiguration="noSecurity"
    contract="IDemoService" />
<endpoint
    address="http://localhost:8081/WsHttpBinding"
    binding="wsHttpBinding"
    name="WSHttpBinding"
    bindingConfiguration="noSecurity"
    contract="IDemoService" />

The results for both WSDualHttpBinding and WSHttpBinding were much faster!

WSDualHttpBinding: Processed 5773 calls in 10 seconds
WSHttpBinding: Processed 17257 calls in 10 seconds
BasicHttpBinding: Processed 19528 calls in 10 seconds
NetTcpBinding: Processed 39756 calls in 10 seconds
NetNamedPipeBinding: Processed 47457 calls in 10 seconds

So as you can see you can get really different levels of performance based on the different binding you use. The ability to have these various endpoints provides a great mechanism for my services to reach all kinds of different clients with great interoperability and maximized speed for network and local communication. Best of all, it's all free...no extra coding! All I had to do to add the new binding was add the endpoint to the service configuration file. WCF takes care of all the rest for you...

Supplemental material - Sample Projects

Video 3 (FWD) - "Introduction to the Firefox Console"

Sunday, January 22, 2006

Here's the next video in the Firefox Web Developer series: Using the Firefox Console. The Firefox Console is a tremendously underused tool built right into Firefox. It allows for safe and efficient JavaScript development that may otherwise be a complete nightmare. This tool is also an absolute must for remote scripting developers (yes, I still refuse to call it "Ajax"). You can think of this tool as being much like a service tracer. C# programmers will feel right at home with this guy.

I say it in the video as well, but let me emphasis it here: the Firefox console is not the JavaScript console. Firefox is a complete standalone Web Development Suite -- not just a web browser. These tools are not addons, plugins, or extensions, but rather are natively built-in tools in Firefox. In later videos I will discuss other tools also built right into Firefox.

OK, so here ya go...

Indigo Girl

Friday, January 20, 2006

OK, everyone interested in WCF needs to stop what they are doing and head on over to http://www.thatindigogirl.com/. Michele has posted chapters to her up amazing, soon-to-be-released WCF book up there. Not just that, but there are a few things that really impress me.

First off, she has to be my new technology role model (move over Brad Abrams and Andrew Trolsen) as she to be the highest ranking MVP in the world. Sheesh, just go look at her profile at http://www.idesign.net/.

Secondly, she's probably the best writer I've ever read. Picky doesn't even begin to describe me. If I don't consider the author absolutely outstanding, I don't bother flipping the pages. She is one of the very few people I can actually put up with. Her bypassing of the silly Hello World example for a more useful one was very respectable and it demonstrated to me that she wants to get right to the point (in turn this shows technical security; insecure writers have SO padding and PCness!)

Third, she seems to be a bottomless pit of powerful and useful training. Today was the first time I read her work and in the first 30 pages of her book I was able to overcome a load of WCF obstacles I was hitting with the go-live version.

Fourth, she has a very beautiful website. Not that she did it, but it does show that she cares about her environment. It rather bothers me when my fellow architects feel the need to go all hardcore and boring on me. She seems to be immune to that. Um...yes, I rate my favorite architects by more than their technical skills!

I swore to only buy book from Abrams and Troelsen, but I'm going to pre-order her book...something I've never done before. I'm extremely impressed with her work. Seriously, go check her blog, website, and book out!

WCF: A self-hosted service with multiple endpoints

Friday, January 20, 2006

Last night I started playing with more more interesting things in WCF (multiple endpoints) and I kept running into a wall: how do you set more than one http endpoint on the same service!? Nothing I was doing was working, then I ran into Indigo Girl's WCF book chapters online (see my next post). Her examples clearly explain what I was doing wrong. Let me explain how to do what I wanted to do in my own words...

Instead of having a different "server" for each endpoint of the same address scheme (i.e. http), you have a a base address for and then give each a name. There is one WSDL for all the endpoints of a service. That is: one service, one WSDL; not one endpoint, one WSDL. This stems from the fact that by default there are two endpoints repackages: mex and HTTP Get. The HTTP get one is the one that will help out with the WSDL.

Here is the fixed up code. By running this up and generating the proxy, we are able to have the complete mechanism for communication from the client to the service, including the configuration file (below).

Program.cs
Uri httpUri = new Uri("http://localhost:8081/");
using(ServiceHost hService = new ServiceHost(typeof(DemoService), httpUri)) {
 hService.AddServiceEndpoint(typeof(IDemoService), new BasicHttpBinding( ), "BasicService");
 hService.AddServiceEndpoint(typeof(IDemoService), new WSDualHttpBinding( ), "DualHttpService");

 hService.Open( );

 Console.WriteLine("Listening...");
 Console.ReadLine( );
}
client app.config

  <system.serviceModel>
    <client>
      <endpoint
        address="http://localhost:8081/BasicService"
        binding="basicHttpBinding"
        name="BasicHttpService"
        contract="IDemoService" />
      <endpoint
        address="http://localhost:8081/DualHttpService"
        binding="wsDualHttpBinding"
        name="DualHttpService"
        contract="IDemoService" />
    </client>
  </system.serviceModel>
Even when using entitely different protocols (the higher level http and say the lower level tcp), you have one WSDL. Of course in this case, since there are more one address scheme (http and net.tcp), you have more than one base address; one per scheme.
Program.cs
Uri netTcpAddress = new Uri("net.tcp://localhost:8080/");
Uri httpAddress = new Uri("http://localhost:8081/");
using (ServiceHost service = new ServiceHost(typeof(DemoService), new Uri[] { netTcpAddress, httpAddress })) {
 service.Open( );

 Console.WriteLine("Listening...");
 Console.ReadLine( );
}
server app.config

  <system.serviceModel>
    <services>
      <service type="WcfDemo.DemoService">
        <endpoint
          address="NetTcpService"
          contract="WcfDemo.IDemoService"
          binding="netTcpBinding"
          />
        <endpoint
          address="DualHttpService"
          contract="WcfDemo.IDemoService"
          binding="wsDualHttpBinding"
          />
      </service>
    </services>
  </system.serviceModel>
client app.config

  <system.serviceModel>
    <client>
      <endpoint
        address="net.tcp://localhost:8080/NetTcpService"
        binding="netTcpBinding"
        name="NetTcpService"
        contract="IDemoService" />
      <endpoint
        address="http://localhost:8081/DualHttpService"
        binding="wsDualHttpBinding"
        name="DualHttpService"
        contract="IDemoService" />
    </client>
  </system.serviceModel>

As you can see WCF really allows for some pretty powerful scenarios...

WCF Beta 2 and Go-live license!

Wednesday, January 18, 2006

At last! We now have beta 2 of WCF and a corresponding Go live license! This is incredible news for architects and developers alike. We can finally use the foundation to help unite our current (and future) component services, web services, and MSMQ applications in production

Just the other day I was designing an architecture and I was thinking "Gosh, this would be so nice in WCF. I *REALLY* don't want to do this in the old school way...if only the beta 2 and the Go-live license would be released!" Three days later...BAM!

Woohoo!! Not only that but beta 2 of WWF with a corresponding Go-live license is also available. I haven't been this excited since Firefox 1.5 came out!!!

Here's the Go-live license and the WCF beta 2 download (as well as WWF beta 2).

User Control Error: The base class includes the field 'xxx', but its type (yyy) is not compatible with the type of control (zzz).

Monday, January 16, 2006

I'm not a huge user of user controls, but today I was porting an ASP app over to the real world and I figured the particular scenario I was working with didn't really warrant the power of a server control. So after I went an took the 12 seconds out of the day to create a user control all was well. It was working beautifully and all that jazz...until I published the website to the test server!

The base class includes the field 'cltFemaleItems', but its type (Items)
is not compatible with the type of control (ASP.items_ascx).

WHAT!!!! I'm marking this one as an ASP.NET 2.0 bug. After a bit of searching online I realized that there aren't really ANY workarounds to this online.

I went back and read the message again and noticed this small hint: "( ASP.items_ascx)". Below is what my user control class declaration looked like and what the declarative directive looks like. Everything looks fine...

<%@ Control Language="C#" AutoEventWireup="true" CodeFile=" Items.ascx.cs" Inherits="Items" %>

public partial class Items : System.Web.UI.UserControl

Based on the error message hint, however, changed the name of the contro. Here is what I changed the above code to...

<%@ Control Language="C#" AutoEventWireup="true" CodeFile"Items.ascx.cs " Inherits="items_ascx" %>

public partial class items_ascx : System.Web.UI.UserControl 

Then I republished and viola! All it well... That's one really weird ASP.NET 2.0 bug. Anyhow, this workaround seems to work well.

Constrainted try with retry mechanism for .NET 2.0

Tuesday, January 10, 2006

Lately I've been working on building a framework of abstractions and features that I feel are rather missing from my daily life. One of these features is a try/retry. In the past I used gotos and a ton of code to do this...but such extra coding was the nature of .NET 1.x. This is the world of .NET 2.0 and therefore I can take it to the next level! My new ExceptionFramework includes a few items to help me with exceptions and I figured I would share the following bit with the world.

using System;

public static class ExceptionManager
{
    public static void TryWithRetryConstraints(
        Int32 retryCount,
        Int32 sleepTime,
        ExceptionConstraintCollection constraints,
        ExceptionTryBlock tryBlock,
        ExceptionCatchBlock catchBlock) {
        Int32 n = 0;
    retry:
        try {
            tryBlock.DynamicInvoke( );
        }
        catch (Exception ex) {
            if (++n < retryCount) {
                foreach (Object constraint in constraints) {
                    if (constraint is Type) {
                        Boolean isException = false;
                        if (((Type)constraint).Name == "Exception") {
                            isException = true;
                        }

                        Type parent = ((Type)constraint).BaseType;
                        while (!isException && parent.Name != "Object") {
                            if (parent.Name == "Exception") {
                                isException = true;
                            }
                            parent = parent.BaseType;
                        }
                        if (isException) {
                            Exception thrownException = ex;
                            while (thrownException != null) {
                                if (thrownException.GetType( ).ToString( ) == constraint.ToString( )) {
                                    Thread.Sleep(sleepTime);
                                    goto retry;
                                }

                                thrownException = thrownException.InnerException;
                            }
                        }
                    }
                    else if (constraint is String) {
                        if (ex.Message.Contains((String)constraint)) {
                            Thread.Sleep(sleepTime);
                            goto retry;
                        }
                    }
                    catchBlock.DynamicInvoke(ex);
                    return;
                }
            }
            else {
                catchBlock.DynamicInvoke(ex);
            }
        }
    }
}

Here are some of the support items...

public delegate void ExceptionTryBlock( );
public delegate void ExceptionCatchBlock(Exception ex);

public class ExceptionConstraintCollection : Collection<object>
{
    public ExceptionConstraintCollection( ) {
        // Always declare default constructors!
    }

    public ExceptionConstraintCollection(params object[] constraintSet) {
        for (int n = 0; n < constraintSet.Length; n++) {
            this.Add(constraintSet[n]);
        }
    }
}

This version allows me to retry only for certain types of failures. There is a simplified version which doesn't take the constraints, but this is one is a bit more useful for my scenarios (I actually think it's a good idea to always constrain it!) I found that the times I wanted to retry involved cases where I either wanted to retry based on a certain type of exception type or based on some text in the string.

Here's is how I use this version of the try/retry in one of my WPF applications. This is my little way of testing my laptop's RAM speed (as well as the CLR speed). I would grab 1GB of ram, free it, and quickly try to get it again (won't work). The below try/retry helps me get a feel or how fast .NET 2.0 can free up the memory and get it again (sometimes it CAN get the 1GB back).

First, I set up the constraints collection which holds the types and strings to which retries will be constrained to. This could of course be inline with the primary call.

ExceptionConstraintCollection constraints = new ExceptionConstraintCollection(
        typeof(OutOfMemoryException),
        "invocation");

Here is the actual call to the try/retry. I tell the try/retry manager how many times to retry (6), how many milliseconds to wait between each try (1000), what constraints to put on the try/retry (the above constraints collection), the logic to run as the try block (a delegate; anonymous in this case), and the code to run as the catch block (also a delegate; also anonymous in this case) and then let it do all the work for me.

ExceptionManager.TryWithRetryConstraints(6, 1000, constraints,
    delegate {
        group = new MemoryGroup();
        MessageBox.Show("Success");
    },
    delegate(Exception ex) {
        ExceptionManager.Report("Unable to allocate memory group.", ex);
    });

Now there are some obvious issues with this. The first: speed. Well, that's the nature of abstraction. You get ease of use and simplicity to life, but it costs a bit. The second is...this just seem weird, doesn't it? It just seems like there's a whole new bag of issues associated with this. Actually, this doesn't really bother me either. I'm keeping my code small enough so that I don't break the Ritcher anonymous delegate rule (never more than 3 lines of code in an anonymous block including comments) and I'm keeping in my mind that top level exceptions will really have the text of "Exception has been thrown by the target of an invocation." (which is, of course, true, but this means I better not be doing this stuff straight with COM interop!) As long as I keep that last one in mind I should be fine.

In any case, this has really been helping me out and have been proving to be a very powerful solution to the lack of a try/retry in the CLR.

Video 2 (FWD) - "Introduction to the Firefox JavaScript Console"

Tuesday, January 10, 2006

Firefox comes with many incredible utilities right out of the box. One of these tools is the JavaScript console. Most developers who have done any web development at all utilizing Firefox has used this great tool, but few know of some of the more powerful features. In this video I touch lightly on a few features that you may have overlooked.

In the next video we will dive hard core into some code to see the Firefox Web Development suite in action.

Creative Commons License
This work is licensed under a Creative Commons Attribution 2.5 License.

Built on Themelia Pro 2.0

Mini-icons are part of the Silk Icons set of icons at famfamfam.com