编写Go程序对Nginx服务器进行性能测试的方法
目前有很多提供Go语言HTTP应用服务的方法,但其中最好的选择取决于每个应用的实际情况。目前,Nginx看起来是每个新项目的标准Web服务器,即使在有其他许多不错Web服务器的情况下。然而,在Nginx上提供Go应用服务的开销是多少呢?我们需要一些nginx的特性参数(vhosts,负载均衡,缓存,等等)或者直接使用Go提供服务?如果你需要nginx,最快的连接机制是什么?这就是在这我试图回答的问题。该基准测试的目的不是要验证Go比nginx的快或慢。那将会很愚蠢。
下面是我们要比较不同的设置:
- GoHTTPstandalone(asthecontrolgroup)
- NginxproxytoGoHTTP
- NginxfastcgitoGoTCPFastCGI
- NginxfastcgitoGoUnixSocketFastCGI
硬件
因为我们将在相同的硬件下比较所有设置,硬件选择的是廉价的一个。这不应该是一个大问题。
- Samsung笔记本NP550P5C-AD1BR
- IntelCorei73630QM@2.4GHz(quadcore,8threads)
- CPUcaches:(L1:256KiB,L2:1MiB,L3:6MiB)
- RAM8GiBDDR31600MHz
软件
- Ubuntu13.10amd64SaucySalamander(updated)
- Nginx1.4.4(1.4.4-1~saucy0amd64)
- Go1.2(linux/amd64)
- wrk3.0.4
设置
内核
只需很小的一点调整,将内核的limits调高。如果你对这一变量有更好的想法,请在写在下面评论处:
fs.file-max 9999999 fs.nr_open 9999999 net.core.netdev_max_backlog 4096 net.core.rmem_max 16777216 net.core.somaxconn 65535 net.core.wmem_max 16777216 net.ipv4.ip_forward 0 net.ipv4.ip_local_port_range 1025 65535 net.ipv4.tcp_fin_timeout 30 net.ipv4.tcp_keepalive_time 30 net.ipv4.tcp_max_syn_backlog 20480 net.ipv4.tcp_max_tw_buckets 400000 net.ipv4.tcp_no_metrics_save 1 net.ipv4.tcp_syn_retries 2 net.ipv4.tcp_synack_retries 2 net.ipv4.tcp_tw_recycle 1 net.ipv4.tcp_tw_reuse 1 vm.min_free_kbytes 65536 vm.overcommit_memory 1 Limits
供root和www-data打开的最大文件数限制被配置为200000。
Nginx
有几个必需得Nginx调整。有人跟我说过,我禁用了gzip以保证比较公平。下面是它的配置文件/etc/nginx/nginx.conf:
userwww-data; worker_processesauto; worker_rlimit_nofile200000; pid/var/run/nginx.pid; events{ worker_connections10000; useepoll; multi_accepton; } http{ sendfileon; tcp_nopushon; tcp_nodelayon; keepalive_timeout300; keepalive_requests10000; types_hash_max_size2048; open_file_cachemax=200000inactive=300s; open_file_cache_valid300s; open_file_cache_min_uses2; open_file_cache_errorson; server_tokensoff; dav_methodsoff; include/etc/nginx/mime.types; default_typeapplication/octet-stream; access_log/var/log/nginx/access.logcombined; error_log/var/log/nginx/error.logwarn; gzipoff; gzip_varyoff; include/etc/nginx/conf.d/*.conf; include/etc/nginx/sites-enabled/*.conf; } Nginxvhosts upstreamgo_http{ server127.0.0.1:8080; keepalive300; } server{ listen80; server_namego.http; access_logoff; error_log/dev/nullcrit; location/{ proxy_passhttp://go_http; proxy_http_version1.1; proxy_set_headerConnection""; } } upstreamgo_fcgi_tcp{ server127.0.0.1:9001; keepalive300; } server{ listen80; server_namego.fcgi.tcp; access_logoff; error_log/dev/nullcrit; location/{ includefastcgi_params; fastcgi_keep_connon; fastcgi_passgo_fcgi_tcp; } } upstreamgo_fcgi_unix{ serverunix:/tmp/go.sock; keepalive300; } server{ listen80; server_namego.fcgi.unix; access_logoff; error_log/dev/nullcrit; location/{ includefastcgi_params; fastcgi_keep_connon; fastcgi_passgo_fcgi_unix; } }
Go源码
packagemain import( "fmt" "log" "net" "net/http" "net/http/fcgi" "os" "os/signal" "syscall" ) var( abortbool ) const( SOCK="/tmp/go.sock" ) typeServerstruct{ } func(sServer)ServeHTTP(whttp.ResponseWriter,r*http.Request){ body:="HelloWorld\n" //Trytokeepthesameamountofheaders w.Header().Set("Server","gophr") w.Header().Set("Connection","keep-alive") w.Header().Set("Content-Type","text/plain") w.Header().Set("Content-Length",fmt.Sprint(len(body))) fmt.Fprint(w,body) } funcmain(){ sigchan:=make(chanos.Signal,1) signal.Notify(sigchan,os.Interrupt) signal.Notify(sigchan,syscall.SIGTERM) server:=Server{} gofunc(){ http.Handle("/",server) iferr:=http.ListenAndServe(":8080",nil);err!=nil{ log.Fatal(err) } }() gofunc(){ tcp,err:=net.Listen("tcp",":9001") iferr!=nil{ log.Fatal(err) } fcgi.Serve(tcp,server) }() gofunc(){ unix,err:=net.Listen("unix",SOCK) iferr!=nil{ log.Fatal(err) } fcgi.Serve(unix,server) }() <-sigchan iferr:=os.Remove(SOCK);err!=nil{ log.Fatal(err) } }
检查HTTPheader
为公平起见,所有的请求必需大小相同。
$curl-sIhttp://127.0.0.1:8080/ HTTP/1.1200OK Connection:keep-alive Content-Length:12 Content-Type:text/plain Server:gophr Date:Sun,15Dec201314:59:14GMT $curl-sIhttp://127.0.0.1:8080/|wc-c 141 $curl-sIhttp://go.http/ HTTP/1.1200OK Server:nginx Date:Sun,15Dec201314:59:31GMT Content-Type:text/plain Content-Length:12 Connection:keep-alive $curl-sIhttp://go.http/|wc-c 141 $curl-sIhttp://go.fcgi.tcp/ HTTP/1.1200OK Content-Type:text/plain Content-Length:12 Connection:keep-alive Date:Sun,15Dec201314:59:40GMT Server:gophr $curl-sIhttp://go.fcgi.tcp/|wc-c 141 $curl-sIhttp://go.fcgi.unix/ HTTP/1.1200OK Content-Type:text/plain Content-Length:12 Connection:keep-alive Date:Sun,15Dec201315:00:15GMT Server:gophr $curl-sIhttp://go.fcgi.unix/|wc-c 141
启动引擎
- 使用sysctl配置内核
- 配置Nginx
- 配置Nginxvhosts
- 用www-data启动服务
- 运行基准测试
基准测试
GOMAXPROCS=1
Gostandalone
#wrk-t100-c5000-d30shttp://127.0.0.1:8080/ Running30stest@http://127.0.0.1:8080/ 100threadsand5000connections ThreadStats Avg Stdev Max +/-Stdev Latency 116.96ms 17.76ms173.96ms 85.31% Req/Sec 429.16 49.20 589.00 69.44% 1281567requestsin29.98s,215.11MBread Requests/sec: 42745.15 Transfer/sec: 7.17MB Nginx+GothroughHTTP
#wrk-t100-c5000-d30shttp://go.http/ Running30stest@http://go.http/ 100threadsand5000connections ThreadStats Avg Stdev Max +/-Stdev Latency 124.57ms 18.26ms209.70ms 80.17% Req/Sec 406.29 56.94 0.87k 89.41% 1198450requestsin29.97s,201.16MBread Requests/sec: 39991.57 Transfer/sec: 6.71MB