I'm looking for the clojure-way of building a live streaming server. The particular problem I'm struggling with is how to send the values from a single provider (the webcam) to an undefined number of threads (the connected clients). Obviously when a client connects it's not interested in the full video file from the webcam, basically it needs to be sent a header and then whatever packages are arriving from the webcam at that exact moment.
In straight java, I think it would be easy. Whenever a client connects, add the connection to an array, when it disconnects remove the connection from the array, and whenever a new package from the webcam arrives send it to each entry in the array. Lock the array so that either we're adding/removing entries, or looping through it to send packets to. Of course we could build the same in clojure, but this sounds really evil.
In a message passing multi-threaded architecture this sounds equally easy.
The only solution I could think of in clojure is with a lazy sequence of promises. Indeed it works, but I was wondering whether there is another way that leads to cleaner code and more clojure-zen :)
Just to illustrate: a simplified problem, with promises and atoms:
One provider function generating data, one thread that reads this data. Later some other threads are created that would like to get the data from this first thread, but can't get to it.
(defn provider []
(lazy-seq
(do
(Thread/sleep 100)
(cons (rand) (provider)))))
(def printer (agent nil))
(defn log [& line]
(send-off printer (fn [x] (apply println line))))
(def promises (atom (repeatedly promise)))
(defn client-connected-thread [x input]
(log "Client connection " x " is connected with the provider and just received" @(first input))
(recur x (rest input)))
(.start (Thread. (fn []
(loop [stream (provider)]
(when-let [item (first stream)]
(log "I received " item", will share now")
(deliver (first @promises) item)
(swap! promises rest))
(recur (rest stream))))))
(Thread/sleep 300)
(.start (Thread. #(client-connected-thread 1 @promises)))
(Thread/sleep 100)
(.start (Thread. #(client-connected-thread 2 @promises)))
(Thread/sleep 50)
(.start (Thread. #(client-connected-thread 3 @promises)))
So, basically the question is: is this the right way to tackle this problem?
Also, we're talking about a streaming media server here, so the provider function will provide tens of thousands of items per second, and there may be 10 clients connected. Is the promise-system meant for such heavy use?