Ayende @ Rahien

Refunds available at head office

Rhino Queues, Take 6

As I stated before, I started writing a queuing system a few days ago, loosely based on my previous efforts in this area, but aimed to create a production ready queuing infrastructure that I can use in my own applications. The decision to build this was not made lightly, but I wanted a queuing system that met my needs, and was flexible enough to extend at needs.

The design goals stated for Rhino Queues were:

  • XCopy deployable
  • Zero configuration
  • Durable
  • Supports System.Transactions
  • Works well with Load Balancing hardware
  • Supports sub queues
  • Support arbitrarily large messages

It is now publically available, and I think it deserve some discussion.

Here is a very simple example of using it:

var queueManager = new QueueManager(new IPEndPoint(IPAddress.Loopback, 2200), "queues.esent");
queueManager.CreateQueues("Web");
var queue = queueManager.GetQueue("Web");

while(ContinueProcessing)
{
    using(var tx = new TransactionScope())
    {
        var msg = queue.Receive();
        Console.WriteLine("Message from {0}:", msg.Headers["source"]);
        Console.WriteLine(Encoding.Unicode.GetString(msg.Data));
        tx.Complete();
    }
}

This example is merely to show the API.

The actual software is pretty interesting. Communication between different queues are done using TCP, with a protocol that works well with load balancing hardware, making load balancing queued application as easy as balancing any HTTP based app.

You cannot see it in the API example, but the system is supporting System.Transactions fully, so sending a message is delayed until a transaction is committed, and receipt of a message would be rolled back on transaction rollback. We even support recovery after a hard crash, by plugging into the recovery mechanism that MSDTC offers us.

All messages are durable, so a system reboot will not remove them. While the default mode for Rhino Queues is an embedded component in your application (XCopy deployable), we still support the ability to deliver a message to a server that is down, by implementing a fairly flexible message retry mechanism.

Because Rhino Service Bus makes such a heavy use of this, we are also support sub queues, and we have no hard limit on the size of messages that we can deliver.

I delayed announcing this until I could finish integrating this completely into Rhino Service Bus, but this is now done, and should just work. Rhino Service Bus will still supports MSMQ, but I think that a lot of the work that we are going to do now on Rhino Service Bus would be with the new queuing infrastructure.

Comments

jdn
04/02/2009 11:58 PM by
jdn

"It is not publically available,"

Did you mean it was 'now publicly available'?

I assume that it was a misprint what with the link and all, but since I'm currently looking at Rhino Service Bus, wanted to make sure.

TIA.

Ayende Rahien
04/03/2009 01:35 AM by
Ayende Rahien

Yes, that is a misprint, it IS available.

I fixed the post

Nathan
04/03/2009 03:14 AM by
Nathan

Very, very exciting stuff...can't wait to integrate it into our messaging project. Have you done any performance comparisons?

Horses
04/03/2009 05:09 AM by
Horses

Perhaps I'm being facetious, but when you say "arbitrarily large messages", don't you mean messages smaller than 2GB? The length used for message data in SerializationExtensions is only an Int32...

Torkel
04/03/2009 05:12 AM by
Torkel

Great, will check it out.

Hopefully it will make Rhino Service bus easier / more accessible to work with.

Microsoft should hire you, they could then retire most their .NET BCL developers :)

Rafal
04/03/2009 05:32 AM by
Rafal

Congratulations, hope you're not working on your own operating system yet? Question: can subqueues be freely defined, or are they already pre-defined? Thanks for keeping the API simple... with this addition I'll definitely have a look at Rhino SB.

James L
04/03/2009 07:44 AM by
James L

I look forward to playing with this. From the code sample, something strikes me right away, the use of the word 'Manager' in classes is a pet hate of mine as it doesn't mean anything. What does the QueueManager really do?

Everyone's a critic right?!

Yuriy
04/03/2009 09:43 AM by
Yuriy

I guess TCP communication endpoint should be rather named TcpEndpoint, not IPEndpoint, especially since port numbers have nothing to do with raw IP.

Ayende Rahien
04/03/2009 03:05 PM by
Ayende Rahien

Nathan,

Not only didn't I do any perf comparisons, I didn't even try to think about perf yet :-)

Ayende Rahien
04/03/2009 03:06 PM by
Ayende Rahien

Horses,

Well, yes. It is limited to 2GB, for more than one reason.

I consider this to be arbitrarily large :-)

Ayende Rahien
04/03/2009 03:09 PM by
Ayende Rahien

Rafal,

subqueues are freely defined, no need to explicitly create them upfront.

James,

Me too, feel free to suggest a better name.

Yuriy,

Talk to MS about that, IPEndpoint is their name.

Rene
04/03/2009 04:13 PM by
Rene

Hi ayende thanks for this code, im trying to run the tests and get an error on the chemaCreator.Create

Error TaggedNotNULL (JET_errTaggedNotNULL, No non-NULL tagged columns)

i found that changing

ColumndefGrbit.ColumnNotNULL to ColumndefGrbit.None

for columns JET_coltyp.LongText, works fine. im using Windows XP SP3.

V.
04/03/2009 04:28 PM by
V.

Just finished reading the post... i dont know if I like it yet... lol

How do you check how many messages are in the queue? Using MSMQ, the operational staff can just check the message count (computer mgmt/services/queues).

Are you planning to create services to monitor your queues? Maybe add them to the computer mgmt. just like MSMQ...

I never got why you must create your own stuff... looks like everything else has lots of problems... It's your time, I'm not here to judge. I just would like to have a better understanding of your reasons. Maybe you could write a blog post about that. (;

A good one woule be a comparison...

nServiceBus x Rhino Service Bus x Mass Transit.

Rhino Queues x MSMQ x ActiveMQ.

I know you have some posts about that, but its never enough.

Keep up the good work.

Ayende Rahien
04/03/2009 05:48 PM by
Ayende Rahien

Rene,

Thanks, I fixed that.

Ayende Rahien
04/03/2009 05:50 PM by
Ayende Rahien

V,

One of the planned features is a full stats report from the queue, yes.

As for why I create my own, the answer is quite simple, whatever is out there doesn't meet the requirements that I have.

RSB was created to be super simple to use, highly opinionated, developer friendly ESB.

RQ was created to be xcopy deployed with load balancing friendliness.

meisinger
04/03/2009 08:26 PM by
meisinger

interesting... good stuff

a couple of questions

well really one question and one note

first the note:

i noticed in one of your tests that you are newing up a new Reciever object in a "using" statement. didn't you have a post about not too long ago stating that it was bad to do that? just curious

now the question:

i feel like i am missing something when looking at the code

i think that i understand that you have to create a Reciever object and handle the CompletedRecievingMessages event in order to know if a message has been sent to the queue or not

i am missing how or where the information is to send a message back

is there something in the Message that tells me where the message came from?

is that something that i have to add in the Headers?

hope the question makes sense

Nathan
04/03/2009 10:50 PM by
Nathan

I guess you are using a patched version of the managed Esent API? JetEscrowUpdate doesn't seem to exist on the codeplex version...

Rafal
04/04/2009 06:21 AM by
Rafal

I know it's bad to optimize the code too early, but I think Reciever should be spelled Receiver

Ayende Rahien
04/04/2009 11:31 AM by
Ayende Rahien

Nathan,

Yes, I am using a private build.

But I spoke with Laurion, and he said he would add it to the public build

Frank Quednau
04/04/2009 12:00 PM by
Frank Quednau

Ayende,

With 2GB being arbitrarily large, my customer would beg to differ. They like to send around 4GB avi files in their intranet as if there is no tomorrow. :) Well, no messaging for that one, that project became more of a streaming thing anyway...

Ayende Rahien
04/04/2009 12:47 PM by
Ayende Rahien

Frank,

I wouldn't call an AVI file a message.

And stream is a much better alternative for that.

Ayende Rahien
04/04/2009 12:49 PM by
Ayende Rahien

Rafal,

Thanks, I fixed that.

Ayende Rahien
04/04/2009 12:52 PM by
Ayende Rahien

Mike,

using for receiver - I think you were talking about my post regarding using and ISession.

Different things all together.

No, the receiver get a Func{Messages[], IMessageAcceptance}, which is how it let the rest of the app know about new messages.

The event is just to facilitate easier testing.

The information about the source message is located in the message headers.

message.Headers["source"], to be exact.

That is beyond the scope of Rhino Queues, it is something that you need to add.

Rafal
04/04/2009 07:29 PM by
Rafal

Ayende, as far as I remember Rhino Service Bus required windows 2008 because of some functions available only in MSMQ 4.0. With the introduction of Rhino queues will it work on windows 2003, xp or vista?

Ayende Rahien
04/04/2009 11:44 PM by
Ayende Rahien

Rafal,

RSB can run on all of those for a while now.

MSMQ 3.0 is supported as well.

And yes, RQ runs on XP, so that is a positive.

meisinger
04/06/2009 05:40 PM by
meisinger

thanks for the info... guess i am too stuck in the MSMQ world

Dev
04/23/2009 06:42 PM by
Dev

hi Ayende im doing some tests with the latest src on svn and i have a prob

first i start a server with:

using (var tx = new TransactionScope())

        {

            receiver = new QueueManager(new IPEndPoint(IPAddress.Loopback, 4545), "werwer.esent");

            receiver.CreateQueues("uno");

            tx.Complete();                

        }

while (true)

        {

            //System.Threading.Thread.Sleep(1000);

            using (var tx = new TransactionScope())

            {

                   var msg = receiver.Receive("uno", null, new TimeSpan(0,0,10));                      

                    if (msg != null)

                    {

                        Console.WriteLine(Encoding.ASCII.GetString(msg.Data) +"   " + msg.Id);                            

                    }

now in a client i do:

        using (var tx = new TransactionScope())

        {

            sender = new QueueManager(new System.Net.IPEndPoint(IPAddress.Loopback, 4546), "client.esent");


            tx.Complete();

        }

using (var tx = new TransactionScope())

       {

        sender.Send(new Uri("rhino.queues://localhost:4545/uno"),

                    new MessagePayload

                    {

                        Data = Encoding.ASCII.GetBytes("Message "  + x.ToString())

                    }

          tx.Complete(); 

       }




            }

        }

now, i send one msg and receive one in the server

then i run it again and receive 3 times the new msg.

maybe im missing something there?

Ayende Rahien
04/23/2009 06:43 PM by
Ayende Rahien

Dev,

Please post this to the mailing list

Comments have been closed on this topic.