06 Feb 2013
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
27 Jan 2013
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 .
The Clojure Server’s heap 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