This post originated from an RSS feed registered with Java Buzz
by Brian McCallister.
Original Post: Pushing Logic Away
Feed Title: Waste of Time
Feed URL: http://kasparov.skife.org/blog/index.rss
Feed Description: A simple waste of time and weblog experiment
Listening to Adam Bosworth's talk on scaling
database stuff from the MySQL Users Conference. Adam is, as
usual, very interesting to listen to, and quite inspiring.
It got me thinking about a side-project I have been pinging on for a
while, setting up a solid cross-platform (which to me means Ruby --
Java -- Perl) messaging (as in MQ) system. I've been working with ActiveMQ because it is open,
fast, and reliable + James has been very willing to help with
cross-platform stuff. I think we have just been taking the wrong
approach to cross platform.
Right now ActiveMQ has its openwire protocol. This protocol
is derived from a bunch of constants defined in the Java sources,
and ActiveMQ uses it to build a bunch of different
marshalling/unmarshalling code in a variety of languages. In effect,
the protocol is dereived from the capabilities of the source. This
works if the goal is to provide the full capability of ActiveMQ on
any platform, but... that is not what I want. I want basic,
reliable, messaging between arbitrary platforms based on an easy
wire protocol.
There is a SOAP spec for this. Okay, yeah, let's not go there.
So, what do we actually need? Well, a simple and effective protocol
with a reasonable lowest common denominator -- text. We need to be
able to subscribe, by event or by poll. Let's start with what a
message looks like:
<message>
<headers>
<destination>http://messaging.example.org/queue/SAMPLE</destination>
<reply-to>auto</reply-to>
<x-header id="something">something else></x-header>
</headers>
<body>
Message Body Goes Here
</body>
</message>
That's it. Let's look at some details.
For the body element, we'll say that it must be text,
must. It may not be xml. I think sending xml may be great, but CDATA
it if you do. Encoding for XML in the body is CDATA, period. Clients
can run it through a parser themselves.
For the headers, I'll start right with the standard JMS headers. JMS
works really well, good starting point.
destination
Absolutely relevant here, and we'll force the message to contain
the destination so that we can do routing at this
level. Desinations will be urls in WOMP's scheme of things.
delivery-mode
May be PERSISTENT or NONPERSISTENT, defaults to... default is configured on the provider =)
message-id
We'll follow the JMS spec on this one. It will be assigned by the
provider, possibly being returned from the mechanism used to send
the message. It will be an error for a client to specify this.
timestamp
Plunked on by the provider when the message is accepted by the
provider. Values assigned by client are ignored.
correlation-id
Optionally provided my the client.
reply-to
Destination url the client expects to receive a reply on. The
special value 'auto' indicates that the client wants to receive a
temporary one in the response to the send operation. In the case
of auto, a queue will automatically be created and the client
"subscribed" -- whatever that means for the transport in
question. No default as we'll make "fire and forget" the logical
default operation.
redelivered
Follows JMS rules, meaning provider deals with it. Basically to
help consumers de-dupe messages.
type
Always text in JMS terms, header doesn't exist for us.
expiration
We'll use a time-to-live header instead as an offset from the
timestamp. Default will be "forever."
priority
Stick with the JMS 0-9 values, what the provider does with it is
up to the provider. Defaults will probably be normal, definately
not required.
x-header
Arbitrary header which must be passed along with the message.
So far, nothing fancy. It's worth dealing with JMS semantics as this
is just a wire format (and I'll go ahead and spec a couple
transports as well), ideally one which can have implementations
against any messaging system. Universal type 4 jms driver ;-)
Okay, so we have a "send message" format. What about receiving?
Let's define a message to say we are interested in receiving
messages. As JMS has done a nice job of the heavy lifting, we'll go
ahead and stick with semantics from that and just define a wire
format for messages to subscribe:
So, that says we are interested, the subscribe operation may return
a result, depending on the transport. So far I am worried about this
transport-dependent stuff. We'll have to work that out. The
durable element is optional and says the subscription
is durable in jms terms. There is an unsubscribe
message as well to remove interest in a destination.
So, looking at the format, it is verbose and limited. I love it.
A REST style transport should be dirt simple, and will be first
go. It is definately a first go, and I think *too* complicated
already. I want to drop a bunch of the JMS stuff to simplify
further, but need to think about the right way to do that.
A TCP based transport is also dirt easy to envision. It is actually
easier. May do this first, dunno. Need to think about it. For that
I'd do an awfully thin wrapper around ActiveMQ's JMS API as a first
implementation =)
Both will be slower than an optimized binary protocol, but WOMP
(Wide Open Messaging protocol) is about interop, not about
performance. ASN1 is more efficient than XML, after all...