views:

102

answers:

3

I'm working on Clojure app where a client needs to send some commands to a server. These will happen in quite large volumes, so I'd like it to be reasonably efficient, both in terms of processing and over-the-wire serialised size.

What would be the best way of doing this in Clojure?

Currently I'm thinking of:

  • Creating a simple standard representation e.g. {:command-id 1, :params [1 2 3 "abc"]}
  • Serialising using some efficient Java library such as Kryo, and configuring it to understand the Clojure data types
  • Hacking together an appropriate Client/Server socket implementation using the Java NIO libraries for the transmission over TCP/IP

However this seems a little convoluted and I'm sure other people have come up with smarter approaches. Any ideas / advice much appreciated!

+1  A: 

My answer is not Clojure-specific, but I tend to prefer strings over http - it's reasonably standard and reasonably efficient.

There are libraries for JSON in pretty much every language, I'd go with that (along with your simple standard command format) unless the data volume is massive.

My experience is that the less you need to fiddle with specialized formats, sockets and protocols the more likely it is that you can spend the weekend on the beach :).

I'd reserve anything more complicated than JSON over http until after benchmarking shows a need for something else.

j-g-faustus
+2  A: 

How about Google's protocol buffers? There's a library for dealing with them from Clojure: clojure-protobuf. I remember someone on Freenode #clojure is doing a Haskell vs. OCaml vs. Clojure comparison on a serious task (processing loads of Twitter data); s/he's been lavishing praise on the lib.

Update: Here's the relevant utterance from the #clojure conversation I had in mind.

Michał Marczyk
Yeah, I looked at protocol buffers a while back, I seem to remember at the time that it looked very good though I preferred Kryo because it was a bit more dynamic (you can programatically define your serialization format rather than specifying it in a fixed external .proto file)
mikera
+3  A: 

If parameters aren't too big and source is trusted, why not send s-expressions back and fort,

(eval (read-string "(println \"Hello World\")"))

Clojure being a LISP dialect code is data.

EDIT:

For safety, after reading the string you check the command against a valid set of commands,

(contains? #{'println} 
           (first (read-string "(println \"Hello World\")")))

or you can use a library designed for this such as

http://github.com/Licenser/clj-sandbox

Hamza Yerlikaya
Interesting idea! Probably a bit risky for an over-the-internet channel where a client may be compromised but could work very well if you could figure out how to constrain the input to known-safe commands
mikera
For a hand-rolled solution, remember to rebind `*read-eval*` to `false`! Though hand-rolling a solution for this is probably a bad idea; clj-sandbox is likely to be more robust *and* more efficient (and if you know how to improve it, you'd be doing the Clojure community a great service by sharing your ideas!).
Michał Marczyk
Well, if you filter out all function calls and leave only data, you are left with a Clojure version of JSON. CLON? The primary difference would presumably be support for keywords, ratios and regexp.
j-g-faustus
Thanks for this everyone! Looks like the most appropriate solution. I'm going to do a bit more exploring as to how to handle the safety issue :-)
mikera