编写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