Add async (long polling) to ring web application
This is the second post of introducing http-kit, the first one A new Clojure HTTP client, concurrent made easy by asynchronous and promise
Ring, Clojure’s way of abstrating HTTP
Ring is defined in terms of handlers, middleware, adapters, requests maps, and response maps
(defn handler [request-req] ;; accept a reuqest-map, return a response-map
response-map)
;; request-map
{:uri "/index"
:query-string "a=b"
:request-method :get
:headers {"user-agent" "... Chrome/23.0.1271.40 Safari/537.11"
"cookie" "...."
"accept-charset" "UTF-8,*;q=0.5"}
...}
;; response-map
{:status 200
:headers {"Content-Type" "text/plain"}
:body "Hello world from ring"
:session ...}
;; adapter
(run-server handler {..options..}}
Request passed in, response expected. There is no support of async(long polling) in ring’s Spec. Most of the time, You don’t need async. When async is needed, like server need to push data to client in realtime, http-kit offers an option:
Async with async-response
http-kit is a HTTP server/client written from scrach for Clojure. The server is a standard ring adapter with async and websocket support, a drop in replacement of ring-jetty-adapter.
[http-kit "2.0-rc1"] ;; add to project.clj
Only a single interface needed to async support: async-response
(defn handler [req-map]
(async-response respond
(future (respond {:status 200
:headers {"Content-Type" "text/plain"}
:body "async hello world"}))))
(run-server handler {:port 8000}}
async-response
is macro, it gives yourespond
respond
is a function, cann be saved, used to send response to client, on any thread, at any time.
Credit for this flexible API goes to Peter Taoussanis.
Code snippet:
(def clients (atom {}))
(async-response respond
;; save it
(swap! clients assoc respond (now-seconds)))
(on-fancy-event (fn [e]
(doseq [client (keys @clients)]
(client {:status 200
:body (str "Fancy event!" e)})
(swap! clients dissoc client))))
What’s goes on under the hook?
request
received by http-kit, goes though all request-level middlewares, get modifiedasync-response
return a standard ring repsonse, except the body is IListenableFuture. http-kit understand the body, holds on the request.- A callback is registed on IListenableFuture, waiting
respond
to return the actual response respond
send to actual response to client (response-level middlewares are not applied), client’s request returns
Efficiency
Keep a connection costs nothing but a few kilobytes of RAM. http-kit runs happily with ~128M heap while ~10K high traffic concurrent connections.
# http-kit runs happily with 128M heap (-Xms128m -Xmx128m), ab confirms that
ab -n 500000 -c 10000 -k http://127.0.0.1:8080/
Open source
http-kit’s website: http-kit.org
Source code is hosted on github: github.com/http-kit/http-kit
To suggest a feature, report a bug, or general discussion: github.com/http-kit/http-kit/issues