Slacker 0.6: Exposing multiple namespaces

After 98 commits in about one month, I’m glad to announce [slacker "0.6.1"].

One thing in slacker 0.6.x is you can expose multiple namespaces from a single server.

Suppose you have two namespaces `redday.stats` and `redday.api`, both contains functions you want to expose.

  (start-slacker-server [(the-ns 'redday.stats)
                         (the-ns 'redday.api)]
                        6565)

This will expose `redday.stats` and `redday.api` on port 6565.

On the client side, we have a new `use-remote` behaviors like clojure’s use. Instead of local one, it imports functions from a remote namespace to your current namespace.

(use 'slacker.client)
;; create a slacker client
(def scp (slackerc "127.0.0.1:6565"))

(use-remote 'scp 'redday.api) ;; caution, use the symbol of 'scp here
(use-remote 'scp 'redday.stats)

;;top-titles is a function in redday.api
;;now you can use the remote function transparently
(top-titles "programming")

;;check function metadata you can find more slacker properties
(meta top-titles)

If you need to configure callback to a particular function, you can still use `defn-remote` to specify the callback function. In slacker 0.6.0, a `:remote-ns` is required when you define such a remote function.

(defn-remote top-titles :remote-ns "redday.api" :callback #(println %))

The complete code example (both server and client) can be found here.

In next post, I will explain another big new feature of 0.6.x, cluster support.

slacker 0.4.0 released

Slacker 0.4.0 has been released to clojars.org . There are new features and breaking changes in this release.

Breaking Changes

  • New maven coordinator: [slacker "0.4.0"] (groupId renamed to slakcer)
  • defremote renamed to defn-remote
  • SlackerException removed. slacker now uses slingshot for exception handling
  • Rename :async option of defn-remote to :async?

What’s new in 0.4.0

  • Add new serialization type :clj
  • New interceptors: execution time stats, args logger, slow watch dog
  • New HTTP interface
  • Server inspect commands
  • utility functions/macros defn-remote-all, defn-remote-batch and meta-remote

Get more information on github.

Extend slacker server with interceptors

An interceptor framework was introduced in slacker 0.3.0. It’s designed to allow user to add custom functionality without hacking into the internal of slacker.

Like many server frameworks, slacker abstracts the request processing as a pipeline. The request object is modified by adding or updating attributes through each node of the pipeline. So it’s easy to add your interceptor into the pipeline, with which you can get the data before and after function executed.

To create such an interceptor, you should use the slacker.interceptor/definterceptor macro and slacker.interceptor/definterceptor+ macro:

(definterceptor name
:before interceptor-function
:after interceptor-function)

(definterceptor+ name [arguments]
:before interceptor-function
:after interceptor-function)

definterceptor+ can accept arguments so you can configure the interceptor when you use it.

See a simple example:

(definterceptor log-function-call
  :before (fn [req] (println (str "calling " (:fname req))) req))

(definterceptor+ log-function-call-prefixed [prefix]
  :before (fn [req] (println (str
                               (if (fn? prefix) (prefix) prefix)
                               " calling "
                               (:fname req)))
                    req))

Then, add it to your slacker server by

(use '[slacker.interceptor])
(import '[java.util Date])
(start-slacker (the-ns 'slapi) 2104
  :interceptors (interceptors log-function-call
                              (log-function-call-prefixed
                                (fn [] (.toString (Date.)))))

Now you can log every function call of your slacker server.

For more detail about the interceptor framework, especially the request data, please check the wiki page.

In slacker 0.3.0, there is a built-in interceptor to stats function calls. You can find it at slacker.interceptors.stats. The stats data is expose via JMX. You can also write monitoring application to retrieve the data.

And there will be more built-in interceptors in 0.4.0, includes function call time stats and logging.

slacker 0.2.0 is out

Slacker 0.2.0 has been pushed to clojars today. Connection pooling and json serialization are available in this release.

Connection Pool

Generally, pooling connection is a good idea in high concurrence application. To make slacker for real world, connection pool support is a high-prioritized feature in its development. The new connection pool is backended by commons-pool which you might familiar with. To use connection pool, just create slacker client with a new function `slackerc-pool`

(def scp (slackerc-pool "localhost" 2104))

Then you can use this pool just like a single client.

Some options are available to configure the pool by your wish:

  • :max-active, max connections opened by the pool
  • :exhausted-action
    • :fail throw an exception when pool exhausted.
    • :block block current thread and wait until max-wait exceed (throw an exception)
    • :grow automatically create new connection and add it to pool
  • :max-wait max wait time before throwing an exception
  • :min-idle minimal number of pool hold idle connections

The options are inherited from GenericObjectPool, you can find detailed information from their javadoc.

JSON Serialization

slacker just added json serialization provided by clj-json. According to my test, clj-json is 1x faster than carbonite in serialization.

(def sc (slackerc "localhost" 2104 :content-type :json))

However, with json serialization, you may lost some clojure types like keyword and set in type conversion. You should be caution when using json as serialization method.

In next release, I am planning to use fastjson as json lib which provides option to write type name into json so it could be a full featured serialization for clojure. And fastjson is claimed even faster than jackson.

Performance

slacker gains high performance with its non-blocking server, serialization and direct function call. As tested on a dual 6 core server, it reaches 10000+ TPS for a single client (50 connections, 50 threads). The server just use 35% CPU so I consider it could have even more TPS if there is two or more client machines.

So if you are interested in some benchmarks, you can test it with client like this. All the requests are using synchronous call because I believe it’s the most common case you use slacker.

Next steps

Inspired by discussion in cn-clojure mailing list, I’m going to add HTTP transport for slacker. With HTTP transport, it’s easier to debug and evaluate your clojure functions, it also makes slacker available to ClojureScript.

At lst, thanks Zach Tellman for reviewing my client code.