1. 06 Feb 2013

    http-kit is clean and small, less is exponentially more

    • HTTP server: event-driven, ring adapter, websocket extension, asynchronous extension
    • HTTP Client: event-driven, asynchronous with promise, synchronous with @promise, keep-alive
    • Timer facility

    All the above + high concurrency + high performance + nice API + written from scrach = ~3K lines of code.

    Clojure is awesome

    Clojure + JAVA = Performance + Nice API

    Less is exponentially more

        http://cloc.sourceforge.net v 1.56  T=0.5 s (94.0 files/s, 8216.0 lines/s)
        -------------------------------------------------------------------------------
        Language                     files          blank        comment           code
        -------------------------------------------------------------------------------
        Java                            44            534            396           2844
        Clojure                          3             52             15            267
        -------------------------------------------------------------------------------
        SUM:                            47            586            411           3111
        -------------------------------------------------------------------------------
      

    cloc runned time: Tue Feb 5 23:23:58 CST 2013, on master branch, from src directory

    Edit: 2013/3/29 with release 2.0.0, the codebase is slightly larger: 2970 lines of Java, 266 lines of Clojure

  2. 27 Jan 2013

    600k concurrent HTTP connections, with Clojure & http-kit

    Inspired by Scaling node.js to 100k concurrent connections! and Node.js w/250k concurrent connections!. I did some test for http-kit!

    http-kit manages to make 600k concurrent connections, on PC!

    Server’s logic

    The server read the length param from the request, generate a string of that length.

    ;; main.clj
    ;; ~20k string
    (def const-str (apply str (repeat 200 "http-kit is a http server & client written from scrach for high performance clojure web applications, support async and websocket")))
    
    (defn handler [req]
      (let [length (to-int (or (-> req :params :length) 1024))]
        {:status 200
         :headers {"Content-Type" "text/plain"}
         :body (subs const-str 0 (max (min 10240 length) 1))}))
    
    (defn -main [& args]
      (run-server (-> handler wrap-keyword-params wrap-params)
                  {:port 8000})
      (println (str "Server started. listen at 0.0.0.0@8000")))

    Start the server:

    java -server -Xms3072m -Xmx3072m -cp `lein classpath` clojure.main -m main

    Linux config

    The server need to set max allowed open file to a much larger value. The default value is ~1024

    echo 9999999 | sudo tee /proc/sys/fs/nr_open
    echo 9999999 | sudo tee /proc/sys/fs/file-max
    
    # edit /etc/security/limits.conf, add the following line, need logout and login again
    * - nofile 4999999
    
    # set before run the server and test code
    ulimit -n 4999999

    More ports for test code to use

    sudo sysctl -w net.ipv4.ip_local_port_range="1025 65535"

    Hardware & Software

    The server and test code are both run on my desktop:

    • CPU: Intel(R) Core(TM) i7-2600 CPU @ 3.40GHz, 4 core, 8 threads
    • RAM: 16G @ 1333MHZ
    • OS : Linux 3.2.0-2-amd64 #1 SMP Sun Apr 15 16:47:38 UTC 2012 x86_64 GNU/Linux
    • http-kit: “2.0-rc1”
    • JVM: 1.7.0_04

    How to make 600k concurrent connections on a single PC

    A IP can only issue most 65536 connections to a server, since socket port is unsigned short. But we can bypass this limit. On Linux, it’s quite easy to set up virtual network interface:

    for i in `seq 200 230`; do sudo ifconfig eth0:$i 192.168.1.$i up ; done

    Then your computer have many IPs, from 192.168.1.200 to 192.168.1.230. The server bind to 0.0.0.0@port, the client can connect 192.168.1.200@port, or 192.168.1.201@port, etc. Per IP can have about 60K concurrent connections. Then the client can issue as many concurrent connections as it needed.

    Concurrency test code

    The client opens 600k concurrent keep-alived connections to the server, request the server to return string of length randomly between 1 ~ 4096 bytes, read the response, idle 5s ~ 45s (randomly pick a value between), request again.

    output:

    time 0s, concurrency: 100, total requests: 0, thoughput: 0.00M/s, 0.00 requests/seconds
    time 40s, concurrency: 164000, total requests: 230142, thoughput: 11.78M/s, 5688.28 requests/seconds
    ...
    time 89s, concurrency: 340100, total requests: 788985, thoughput: 18.23M/s, 8812.23 requests/seconds
    ...
    time 179s, concurrency: 595166, total requests: 2483174, thoughput: 28.61M/s, 13837.77 requests/seconds
    time 180s, concurrency: 597853, total requests: 2506378, thoughput: 28.71M/s, 13888.67 requests/seconds
    time 183s, concurrency: 600000, total requests: 2529020, thoughput: 28.52M/s, 13788.14 requests/seconds
    time 185s, concurrency: 600000, total requests: 2537212, thoughput: 28.20M/s, 13680.42 requests/seconds
    ...
    time 930s, concurrency: 600000, total requests: 17457773, thoughput: 38.64M/s, 18763.53 requests/seconds
    time 931s, concurrency: 600000, total requests: 17477678, thoughput: 38.69M/s, 18764.73 requests/seconds

    How about ab test when 600k connections are kept

    Issue this command from command line:

    ab -n 100000 -c 10 -k http://127.0.0.1:8000/

    output: ab output

    Server Software:        http-kit
    Server Hostname:        127.0.0.1
    Server Port:            8000
    
    Document Path:          /
    Document Length:        1024 bytes
    
    Concurrency Level:      10
    Time taken for tests:   3.184 seconds
    Complete requests:      100000
    Failed requests:        0
    Write errors:           0
    Keep-Alive requests:    100000
    Total transferred:      117000000 bytes
    HTML transferred:       102400000 bytes
    Requests per second:    31405.53 [#/sec] (mean)
    Time per request:       0.318 [ms] (mean)
    Time per request:       0.032 [ms] (mean, across all concurrent requests)
    Transfer rate:          35883.27 [Kbytes/sec] received
    
    Connection Times (ms)
                  min  mean[+/-sd] median   max
    Connect:        0    0   0.0      0       0
    Processing:     0    0   9.3      0     913
    Waiting:        0    0   9.3      0     913
    Total:          0    0   9.3      0     913
    
    Percentage of the requests served within a certain time (ms)
      50%      0
      66%      0
      75%      0
      80%      0
      90%      0
      95%      0
      98%      0
      99%      0
     100%    913 (longest request)

    The Clojure Server’s CPU usage

    jvisualvm’s snapshot file.

    cpu usage

    The Clojure Server’s heap usage

    heap memory usage

    Run it yourself

    The complete test code is available on github. Checkout and run it yourself!

    To report a bug, or general discussion: https://github.com/http-kit/scale-clojure-web-app/issues