软件测试中的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)