This blog has moved, permanently, to http://software.safish.com.

Monday, November 23, 2009

nServiceBus - The Good and the Bad

We've been using nServiceBus at work for the last week or two to send messages between a client and server in a (hopefully) robust fashion.

As a tool, I really like nServiceBus. Once you have it up and running, the use of MSMQ is just brilliant - force a crash in your application and when you start up again they're just there still, ready to be processed - right out the box. Wrap them in a transaction (MSMQ supports MSDTC) and anything goes wrong, the sending of the message gets rolled back. Awesome stuff - it's definitely something I will use again.

But that's not the point of this post. I'm going to point out all the BAD stuff - in the hope that someone else doesn't have the same fights I did. Let me say upfront that I recommend using it - just be aware that the document isn't just bad, it's DISMAL. The author seems to assume that we all understand how the bus works and how to use it - methods are undocumented and even the documentation on how to get it up and running is hopelessly inadequate. So, here goes.

Configuration

The nServiceBus site does have some documentation regarding publish/subscribe scenarios at http://www.nservicebus.com/PubSubApiAndConfiguration.aspx. However, the way it's worded just didn't make sense to me, and it was Rohland who finally worked out what the hell they mean.

Exhibit A

<MsmqTransportConfig
    InputQueue="myqueue"
    ErrorQueue="errors"
    NumberOfWorkerThreads="1"
    MaxRetries="5"
  />
The above configuration specifies the INPUT to your assembly's message handler. In this example, the application using this configuration will listen for messages on the MSMQ "myqueue" on the local machine. It will NOT place messages on that queue - this is the queue it will pick messages up from.

Exhibit B

  <UnicastBusConfig DistributorControlAddress="" DistributorDataAddress="">
    <MessageEndpointMappings>
      <add Messages="My.Messages" Endpoint="somequeue" />
    </MessageEndpointMappings>
  </UnicastBusConfig>
This configuration specifies where your application will publish messages TO! In this example, any messages in the "My.Messages" assembly will be placed on the "somequeue" MSMQ on the local machine. You can specify message types instead of assemblies if you want to. I quite like this - it makes it very easy to publish different messages to different queues, and it's all configurable. Also note that the end point doesn't have to be local - you can specify remote machines as per the nServiceBus documentation.

Message Handlers

In order to receive messages, your assembly that handles those messages needs to incorporate a message handler class, with support for the message types you want to receive. For example, if you want to subscribe to message types "A" and B", you can create a message handler like so:
public class MessageHandler : IMessageHandler<A>, IMessageHandler<B>
{
    public void Handle(A message)
    {
        // handle messages of type A
    }

    public void Handle(B message)
    {
        // handle messages of type B
    }

}


You don't hook this class up anywhere - nServiceBus will find it and instantiate it. I don't like that. It makes it really easy to make mistakes - make a typo on your namespace and your event handler is never invoked, and you sit there scratching your head wondering why until you finally realise your config is fine, and your typing sucks. Another downer is that this is running on a separate thread to your application, so you need to invoke a delegate on appplication's main thread to handle the code, which can be a little iffy in forms development - you need to write code like the following to get a handle on the form that you want to use, like so:
FormMain fm = FormMain.GetInstance();
fm.Invoke(fm.AddInboundMessageHandler, message);


Enumerations and/or More Complex Data Structures

One problem I found, when using version 1.9, was when sending an enumeration of custom objects via a message. My message class contained an array of custom objects, which themselves had two properties: a string property and a byte array property. Nothing complex here. However, when I picked up the message on the subscriber, the collection was there, with the correct number, but the properties of the objects in the array were always null. I just couldn't get this to work at all. Converting the array to a dictionary was even worse - I got exceptions from nServiceBus, which seemed to fall over on some rather dodgy reflection code.

One thing I did find out is that your sub-entities do need to have a default, parameterless constructor, which makes sense as nServiceBus needs to recreate the serialized objects from the queue on the other end. However, try as I might, I never got this working on 1.9. Upgrading to version 2.0, however, and it all started working. I did try converting my array into a typed list for convenience, but this didn't work off the bat and wasn't that important to me so I reverted back to an array.

That's all I can think of for now - I'm sure I'll update this post in the future.

References When I started looking at this there was NOTHING out there - but people are certainly using it. Her are some decent articles on nServiceBus:

8 comments:

  1. Matt,

    Have you seen what's currently online at NServiceBus.com?

    There's a FAQ outlining many of the points you mentioned here:

    http://www.NServiceBus.com/Documentation.aspx

    Are there other open source service buses with better documentation - something that could be emulated?

    ReplyDelete
  2. Hi Udi

    Thanks for responding. I have read the documentation on the site, and although it's not the worst documentation in the world, I think my problem with it is that it's only easy to understand once you understand HOW NSERVICEBUS WORKS.

    Maybe I'm just thick, but it really took a lot of trawling to get my application to work. Admittedly, my application is fairly complex - but I didn't find the site very useful at in understanding how nServiceBus works.

    When I go back to the documentation online now and look at it, it makes sense - but as a newcomer to your product it didn't help me much. Most of the points I mention are documented on your site, but their explanation was not detailed enough for me to really grasp what was going on - it's too segmented without a decent example of how to bring it all together. The sample code is there for download, but the sample projects contain almost no comments. I learned a lot more from those posts by mookie (I provided a link) than from the nServiceBus documentation - the problem for me was that those articles were only posted a few days ago and up until then I struggled.

    What you really need is a more organised, central documentation space on your site - currently documentation is spread between various pages that aren't easy to find. For example, this page is very useful, but is hidden away within the site.

    To answer your question, no, I'm not aware of any other open source service buses with better documentation - but that doesn't mean yours can't be a lot better. I think you've got a really great tool here that is incredibly useful and I will definitely be using it in more projects in the future, but the documentation is in my opinion a barrier to entry in terms of people adopting it.

    If you're looking into ways of presenting your documentation, I found the Castle ActiveRecord documentation very helpful in terms of getting started a few years back, although admittedly their documentation does fall down with more complicated examples.

    Does that make sense, or do you think I'm off base?

    Cheers
    Matt

    ReplyDelete
  3. Matt,

    The biggest challenge we have with documentation is that NServiceBus is designed to be a framework for a different kind of architecture than most people are used to. Documenting that architecture has been the theme of my blog for the past several years and I provide 5-day courses teaching it. Besides putting up some recorded presentations on point-topics, the NServiceBus documentation probably won't ever do justice to the architecture.

    On the other points you've mentioned, yes, work does need to be done - and is being done. Your feedback is valued. For example, the main documentation link now links to all the other pages - any other suggestions you have would be most welcome.

    One thing you mentioned:

    "make a type on your namespace and your event handler is never invoked"

    NServiceBus invokes every handler, regardless of the namespace it's in.

    Can you provide some more information on the specific thing you did, and what happened - what worked and what didn't?

    ReplyDelete
  4. Hi Udi

    I absolutely agree with you - it's difficult documenting what you have - it's the concepts that are the difficult part to grasp rather than the code aspects of it.

    Firstly, thanks very much for updating the documentation section on your site - I really think that'll be a huge helper for anyone starting with NServiceBus. I think one of the difficulties I had was that the fragmentation of the documentation meant it didn't "gel" nicely - there wasn't a single reference point to look at when confronted with an issue. I think this will help.

    In terms of my comment on the typo, I have to admit I have no idea what happened - it was a couple months ago. All I remember was that the solution was to correct a typo in a namespace, and it all started working. This was an issue in version 1.9 though - although I'd love to be able to recreate what happened as it doesn't seem like it was possible.

    I think the situation was something like the Assembly was "MyAssembly" with a default namespace of "MyAssembly.Namespace", and the message handler was in a namespace "MyTypoAssembly.Namespace". Fixing the typo resulted in the messages popping up.

    I assume you're using reflection to iterate through the types, so I don't see how this is possible. Maybe there was something screwy on my machine at the time, I don't really think it's worth looking into. Also, it was version 1.9 and I've had no issues with version 2 - I think it's safe to say it's either resolved or the problem was a user error (me).

    When I get some time I'll see if I can reproduce what I did.

    Matt

    ReplyDelete
  5. Udi

    Another issue we have encountered is debugging with NServiceBus. On a web application, if we fire up a bus instance, our visual studio debugging doesn't work. We literally have to comment out the service bus instantiation to debug other parts of the application.

    Have you had any other queries like this?

    Matt

    ReplyDelete
  6. Matt,

    I haven't heard of anybody else having those issues - maybe post it to the discussion group and see what others say.

    ReplyDelete
  7. Matt,

    Have you guys resolved the issue with not being to debug in visual studio?

    Thanks
    Johan

    ReplyDelete
  8. Johan,

    We never got to the bottom of it, and since I posted that comment we've started using Visual Studio 2010 where the problem doesn't seem to occur. Must be some kind of bug with VS 2008 I guess, as I haven't seen it in ages.

    Cheers
    Matt

    ReplyDelete

Note: Only a member of this blog may post a comment.