软件测试中的C, Erlang, Java, Go 语言的Web Server 性能测试
对C, Erlang, Java and the Go 编程语言的Hello world web服务进行了测试 .
* C, 使用高性能的web server-nginx,以及一个 hello world 的nginx模块
* Erlang/OTP
* Java, 使用MINA 2.0框架.
* Go, http://golang.org/
2 Linux boxes in a gigabit ethernet LAN, 1 server and 1 test client
Linux Centos 5.2 64bit
Intel(R) Xeon(R) CPU E5410 @ 2.33GHz (L2 cache: 6M), Quad-Core * 2
8G memory
SCSI disk (standalone disk, no other aclearcase/" target="_blank" >ccess)
nginx, nginx-0.7.63.tar.gz
Erlang, otp_src_R13B02-1.tar.gz
Java, jdk-6u17-linux-x64.bin, mina-2.0.0-RC1.tar.gz
Go, hg clone -r release https://go.googlecode.com/hg/ $GOROOT (Nov 12, 2009)
Linux, run sysctl -p
net.ipv4.ip_forward = 0 net.ipv4.conf.default.rp_filter = 1 net.ipv4.conf.default.accept_source_route = 0 kernel.sysrq = 0 kernel.core_uses_pid = 1 net.ipv4.tcp_syncookies = 1 kernel.msgmnb = 65536 kernel.msgmax = 65536 kernel.shmmax = 68719476736 kernel.shmall = 4294967296 kernel.panic = 1 net.ipv4.tcp_rmem = 8192 873800 8738000 net.ipv4.tcp_wmem = 4096 655360 6553600 net.ipv4.ip_local_port_range = 1024 65000 net.core.rmem_max = 16777216 net.core.wmem_max = 16777216
# ulimit -n
150000
C: ngnix hello world module, copy the code ngx_http_hello_module.c from http://timyang.net/web/nginx-module/
in nginx.conf, set “worker_processes 1; worker_connections 10240″ for 1 cpu test, set “worker_processes 4; worker_connections 2048″ for multi-core cpu test. Turn off all access or debug log in nginx.conf, as follows
worker_processes 1; worker_rlimit_nofile 10240; events { worker_connections 10240; } http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 0; server { listen 8080; server_name localhost; location / { root html; index index.html index.htm; } location /hello { ngx_hello_module; hello 1234; } error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } } }
$ taskset -c 1 ./nginx or $ taskset -c 1-7 ./nginx
Erlang hello world server
The source code is available at yufeng’s blog, see http://blog.yufeng.info/archives/105
Just copy the code after “cat ehttpd.erl”, and compile it.
$ erlc ehttpd.erl
$ taskset -c 1 erl +K true +h 99999 +P 99999 -smp enable +S 2:1 -s ehttpd
$ taskset -c 1-7 erl +K true -s ehttpd
We use taskset to limit erlang vm to use only 1 CPU/core or use all CPU cores. The 2nd line is run in single CPU mode, and the 3rd line is run in multi-core CPU mode.
Java source code, save the 2 class as HttpServer.java and HttpProtocolHandler.java, and do necessary import.
public class HttpServer { public static void main(String[] args) throws Exception { SocketAcceptor acceptor = new NioSocketAcceptor(4); acceptor.setReuseAddress( true ); int port = 8080; String hostname = null; if (args.length > 1) { hostname = args[0]; port = Integer.parseInt(args[1]); } // Bind acceptor.setHandler(new HttpProtocolHandler()); if (hostname != null) acceptor.bind(new InetSocketAddress(hostname, port)); else acceptor.bind(new InetSocketAddress(port)); System.out.println("Listening on port " + port); Thread.currentThread().join(); } } public class HttpProtocolHandler extends IoHandlerAdapter { public void sessionCreated(IoSession session) { session.getConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10); session.setAttribute(SslFilter.USE_NOTIFICATION); } public void sessionClosed(IoSession session) throws Exception {} public void sessionOpened(IoSession session) throws Exception {} public void sessionIdle(IoSession session, IdleStatus status) {} public void exceptionCaught(IoSession session, Throwable cause) { session.close(true); } static IoBuffer RESULT = null; public static String HTTP_200 = "HTTP/1.1 200 OK\r\nContent-Length: 13\r\n\r\n" + "hello world\r\n"; static { RESULT = IoBuffer.allocate(32).setAutoExpand(true); RESULT.put(HTTP_200.getBytes()); RESULT.flip(); } public void messageReceived(IoSession session, Object message) throws Exception { if (message instanceof IoBuffer) { IoBuffer buf = (IoBuffer) message; int c = buf.get(); if (c == 'G' || c == 'g') { session.write(RESULT.duplicate()); } session.close(false); } } }
$ taskset -c 1-7 \
java -Xmx1024m -Xms1024m -XX:+UseConcMarkSweepGC -classpath . test.HttpServer 192.168.10.1 8080
We use taskset to limit java only use cpu1-7, and not use cpu0, because we want cpu0 dedicate for system call.
Go language, source code
package main import ( "http"; "io"; ) func HelloServer(c *http.Conn, req *http.Request) { io.WriteString(c, "hello, world!\n"); } func main() { runtime.GOMAXPROCS(8); // 8 cores http.Handle("/", http.HandlerFunc(HelloServer)); err := http.ListenAndServe(":8080", nil); if err != nil { panic("ListenAndServe: ", err.String()) } }
$ 6g httpd2.go
$ 6l httpd2.6
$ taskset -c 1-7 ./6.out
ApacheBench client, for 30, 100, 1,000, 5,000 concurrent threads
ab -c 30 -n 1000000 http://192.168.10.1:8080/
ab -c 100 -n 1000000 http://192.168.10.1:8080/
ab -c 1000 -n 1000000 http://192.168.10.1:8080/
ab -c 5000 -n 1000000 http://192.168.10.1:8080/
30 (threads) | 100 | 1000 | 5000 | |
Nginx html(1C) | 21,301 | 21,331 | 17,803 | 11,901 |
Nginx module(1C) | 25,809 | 25,735 | 17,562 | 12,412 |
Nginx module(Multi-core) | 25,057 | 24,507 | 18,110 | 11,681 |
Erlang(1C) | 11,585 | 12,367 | 12,185 | 10,200 |
Erlang(Multi-Core) | 15,101 | 20,255 | 18,687 | 11,613 |
Java, Mina2 | 30,631 | 26,846 | 18,134 | 11,945 |
Go | 14,080 | 14,748 | 13,292 | 7,754 |
30 | 100 | 1,000 | 5,000 | |
Nginx html(1C) | 1 | 4 | 69 | 504 |
Nginx module(1C) | 1 | 4 | 73 | 502 |
Nginx module(Multi-core) | 1 | 6 | 70 | 501 |
Erlang(1C) | 3 | 8 | 3,023 | 1,874 |
Erlang(Multi-Core) | 2 | 7 | 73 | 495 |
Java, Mina2 | 3 | 5 | 67 | 501 |
Go | 26 | 33 | 3,006 | 9,059 |
* On large concurrent connections, C, Erlang, Java no big difference on their performance, all are about 18k/s (1,000 threads) .
* Java runs better on small connections, but the code in this test doesn’t parse the HTTP request header.
* Although Mr. Yu Feng (the Erlang guru in China) mentioned that Erlang performance better on single CPU(prevent context switch), but the result tells that Erlang has big latency(> 1S) under 1,000 or 5,000 connections.
* Go language is very close to Erlang, but still not good under heavy load (5,000 threads)