Nginx 單機百萬QPS環境搭建


一、背景

  最近公司在做一些物聯網產品,物物通信用的是MQTT協議,內部權限與內部關系等業務邏輯准備用HTTP實現。leader要求在本地測試中要模擬出百萬用戶同時在線的需求。雖然該產品最后不一定有這么多,但是既然要求到了就要模擬出來。MQTT用的是erlang的emqtt,這個已經有同事測試在本機可以百萬用戶在線了。不過HTTP服務器就一直很難。

  所以這篇博客准備介紹如何在本地模擬一台支持百萬qps的HTTP服務器。簡單說一下,MQTT的百萬在線是指支持百萬TCP連接,連接后,只要保持心跳包就可以了,這個比較簡單,一般稍微調整一下Linux一些系統參數就可以了。而HTTP的百萬在線不一樣。HTTP是發送一個連接請求后獲得數據然后就關閉連接,百萬並發就比較麻煩,而且這個概念用在這里不是很合適。現在比較通用的一種參考值是每秒處理的並發數。就是QPS。(這一點就很多人不理解。包括我leader,這個要在接下來慢慢解釋給他聽)

 

二、准備

  我以前也沒有搞過高並發的網絡,最近看了一些博客文章,准備用一個最簡單的模型來實現這個百萬QPS的模擬。后台我是用Java寫的,Tomcat服務器。這種帶業務的請求,單機絕對不可能到達百萬QPS(1m-qps)。網上所說的1m-qps測試環境是一個靜態的網頁來測試的。既然帶業務的http單機不能達到要求,那就需要用到集群了。就我目前所學到的知識,基本就只會搭建下面如圖所示的集群架構了。

  一台性能比較好的機器按照nginx作為負載均衡,剩下的一些普通的機器就在后面。數據庫目前只用到一個,后續如何優化和擴展不在本次討論。

  如果使用上面的架構,出去數據庫后,整個HTTP的性能瓶頸就在最前面的Nginx負載均衡上了。其他的HTTP-jsp-tomcat機器如果性能不夠,可以通過多加幾台機器進行水平擴展。而最前面的Nginx就不行了。所以本次所要測試的就是如何讓nginx支持1m-qps,並且機器正常工作。nginx只是做數據轉發而已。

  硬件:服務器88核、128G內存

  軟件:Debian 8, Nginx 1.11,  wrk壓力測試工具

 

三、操作

1. 首先設置一些Linux系統參數 在/etc/sysctl.conf 中增加如下配置

 1 vm.swappiness = 0
 2 net.ipv4.neigh.default.gc_stale_time=120
 3 net.ipv4.conf.all.rp_filter=0
 4 net.ipv4.conf.default.rp_filter=0
 5 net.ipv4.conf.default.arp_announce = 2
 6 net.ipv4.conf.all.arp_announce=2
 7 net.ipv4.tcp_max_tw_buckets = 100
 8 net.ipv4.tcp_syncookies = 0
 9 net.ipv4.tcp_max_syn_backlog = 3240000
10 net.ipv4.tcp_window_scaling = 1
11 #net.ipv4.tcp_keepalive_time = 60
12 net.ipv4.tcp_synack_retries = 2
13 net.ipv6.conf.all.disable_ipv6 = 1
14 net.ipv6.conf.default.disable_ipv6 = 1
15 net.ipv6.conf.lo.disable_ipv6 = 1
16 net.ipv4.conf.lo.arp_announce=2
17 fs.file-max = 40000500
18 fs.nr_open = 40000500
19 net.ipv4.tcp_tw_reuse = 1
20 net.ipv4.tcp_tw_recycle = 1
21 net.ipv4.tcp_keepalive_time = 1
22 net.ipv4.tcp_keepalive_intvl = 15
23 net.ipv4.tcp_keepalive_probes = 3
24 
25 net.ipv4.tcp_fin_timeout = 5
26 net.ipv4.tcp_mem = 768432 2097152 15242880
27 net.ipv4.tcp_rmem = 4096 4096 33554432
28 net.ipv4.tcp_wmem = 4096 4096 33554432
29 net.core.somaxconn = 6553600
30 net.ipv4.ip_local_port_range = 2048 64500
31 net.core.wmem_default = 183888608
32 net.core.rmem_default = 183888608
33 net.core.rmem_max = 33554432
34 net.core.wmem_max = 33554432
35 net.core.netdev_max_backlog = 2621244
36 kernel.sem=250 65536 100 2048
37 kernel.shmmni = 655360
38 kernel.shmmax = 34359738368
39 kerntl.shmall = 4194304
40 kernel.msgmni = 65535
41 kernel.msgmax = 65536
42 kernel.msgmnb = 65536
43 
44 net.netfilter.nf_conntrack_max=1000000
45 net.nf_conntrack_max=1000000
46 net.ipv4.netfilter.ip_conntrack_max=1000000
47 kernel.perf_cpu_time_max_percent=60
48 kernel.perf_event_max_sample_rate=6250
49 
50 net.ipv4.tcp_max_orphans=1048576
51 kernel.sched_migration_cost_ns=5000000
52 net.core.optmem_max = 25165824
53 
54 kernel.sem=10000 2560000 10000 256

還有在命令行中輸入 ulimit -n 20000500 

上面的所有參數,這里就不多講了,具體想要了解的可以上網找資料

2.Nginx 安裝 及其配置

nginx用最簡單的apt-get 也可以,用源碼按照也可以。沒有什么特殊要求。下面的配置就有點要求了。 $NGINX/conf/nginx.conf (/etc/nginx/conf/nginx.conf)

 1 worker_processes  88;  #這個根據硬件有多少核CPU而定
 2 pid        logs/nginx.pid;
 3 
 4 events {
 5     worker_connections  1024;
 6 }
 7 
 8 http {
 9     include       mime.types;
10     default_type  application/octet-stream;
11     sendfile        on;
12     tcp_nopush     on;
13 
14     keepalive_timeout  65;
15 
16     gzip  off;
17     access_log off;   #日志功能要關閉
18 
19     server {
20         listen       888 backlog=168888;
21         server_name  localhost;
22       root /dev/shm/;
23     }
24 
25 }

  上面這個是最簡單的配置,具體的Nginx還有很多可以調優的,還有nginx負載均衡配置,請參考我的另外一片博客<Nginx + Tomcat 動靜分離實現負載均衡 http://www.cnblogs.com/wunaozai/p/5001742.html>

 

 1 worker_processes 4;
 2 
 3 error_log /var/log/nginx/error.log info;
 4 
 5 pid /var/run/nginx.pid;
 6 
 7 
 8 events{
 9     use epoll;
10     worker_connections 409600;
11     multi_accept on;
12     accept_mutex off;
13 }
14 
15 
16 http{
17     sendfile on;
18     tcp_nopush on;
19     tcp_nodelay on;
20     open_file_cache max=200000 inactive=200s;
21     open_file_cache_valid 300s;
22     open_file_cache_min_uses 2;
23     keepalive_timeout 5;
24     keepalive_requests 20000000;
25     client_header_timeout 5;
26     client_body_timeout 5;
27     reset_timedout_connection on;
28     send_timeout 5;
29 
30     #日志
31     access_log off;
32     #access_log /var/log/nginx/access.log;
33     #error_log /var/log/nginx/error.log;
34     #gzip 壓縮傳輸
35     gzip off;
36     #最小1K
37     #gzip_min_length 1k;  
38     #gzip_buffers 16 64K;
39     #gzip_http_version 1.1;
40     #gzip_comp_level 6;
41     #gzip_types text/plain application/x-javascript text/css application/xml application/javascript;
42     #gzip_vary on;
43     #負載均衡組
44     #靜態服務器組
45     #upstream static.zh-jieli.com {
46     #    server 127.0.0.1:808 weight=1;
47     #}
48     #動態服務器組
49     upstream zh-jieli.com {
50         server 127.0.0.1:8080;
51     }
52     #配置代理參數
53     #proxy_redirect off;
54     #proxy_set_header Host $host;
55     #proxy_set_header X-Real-IP $remote_addr;
56     #proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
57     #client_max_body_size 10m;
58     #client_body_buffer_size 128k;
59     #proxy_connect_timeout 65;
60     #proxy_send_timeout 65;
61     #proxy_read_timeout 65;
62     #proxy_buffer_size 4k;
63     #proxy_buffers 4 32k;
64     #proxy_busy_buffers_size 64k;
65     #緩存配置
66     #proxy_cache_key '$host:$server_port$request_uri';
67     #proxy_temp_file_write_size 64k;
68     ##proxy_temp_path /dev/shm/JieLiERP/proxy_temp_path;
69     ##proxy_cache_path /dev/shm/JieLiERP/proxy_cache_path levels=1:2 keys_zone=cache_one:200m inactive=5d max_size=1g;
70     #proxy_ignore_headers X-Accel-Expires Expires Cache-Control Set-Cookie;
71     
72     server{
73         listen 88 backlog=163840;
74         server_name test2;
75         root /dev/shm/;
76     }
77 
78     server {
79         listen 443 ssl;
80         server_name test;
81         location / {
82             index index;
83         }
84 
85         location ~ .*$ {
86             index index;
87             proxy_pass http://zh-jieli.com;
88         }
89         ssl on;
90         ssl_certificate keys/client.pem;
91         ssl_certificate_key keys/client.key.unsecure;
92     }
93 }
View Code

 

 

 

3. 創建一個簡單的html文件

  看到 root  /dev/shm/ 了嗎, 這個就是http服務器根目錄了。 echo "a" > /dev/shm/a.html

4. 安裝wrk

  git clone https://github.com/wg/wrk.git  然后 make

 

四、運行

  啟動nginx

  啟動wrk 進行並發請求測試

./wrk -t88 -c10000 -d20s "http://127.0.0.1:888/a.html"

  top結果圖

  wrk 結果圖

  基本在120萬QPS。nginx設置一些調優參數,加上如果不在本機上運行wrk的話,150萬QPS基本是沒有問題的。

 

五、要達到1m-qps需要注意

 Linux 參數一定要進行調整 (坑: 這個坑是比較小的,基本提到高並發Linux,就會有提到要修改這些參數了)

 Nginx 里 access_log off; 日志功能一定要關閉 (坑: 這個影響還是比較大的,由於Nginx日志是寫在磁盤的,而我服務器的磁盤是普通的磁盤,所以這里會成為磁盤IO瓶頸,所以一開始用最簡單的配置,然后根據硬件的不同修改參數。有些參數在其他機器很快,在我的機器就很慢)

 測試並發工具最好用我上面的wrk進行測試 (坑: 一開始用apache的ab進行測試,后面發現ab測試高性能時不行,最后使用wrk工具)

 最好在本機測試wrk (坑: 從上面的wrk圖可以看到,傳輸的數據在每秒262MB左右,普通的100M網卡是遠遠達不到的。雖然我的a.html只有一個字節,但是http頭部信息太多了。這里還有一個小坑,就是我的服務器是有千兆網卡的,但是我用另外一台測試機也是千兆網卡,但是中間的交換機或者路由器或者網線都達不到要求,所以這里會成為網絡瓶頸) 

 上面那幾個就是我遇到的坑了,特別是最后兩個,一度還懷疑nginx性能問題。因為網上很多評測都說Nginx做代理可以達到百萬QPS,我總是測試不到,基本在2~3萬QPS而已。最后發現是測試工具和網卡原因。

 

六、最后多說兩句

  其實整個安裝搭建測試環境都比較簡單,這篇博客最主要的點還是最后的幾點注意,由於以前沒有搞過這方面,所以沒有經驗,這里記錄下來,以后提醒自己。前端的負載均衡器保證沒有問題后,接下來的問題是后面的HTTP服務集群了。我現在的Java功能后台一台普通的服務器(Tomcat)就只能支持一萬多的簡單請求(因為jvm和web框架等種種原因),然后如果是那種需要簡單查詢數據庫的功能API就只有2~3K的QPS,最后那些需要事務操作的一台服務器僅僅只能支持120~150QPS。第一個還好,通過加機器就可以水平擴展,但是后面那兩個跟數據庫相關的就比較麻煩了,各種新的名詞(NoSQL、緩存、讀寫分離、分庫分表、索引、優化SQL語句等)都來了。能力有限,后續要學的東西太多了。

 

---- 2016-11-22 19:27:18  增加----- 

  單機進行測試的時候好多配置都沒有用到,原因是,所有的請求和應答基本都是在內存處理的,沒有創建socket連接,沒有經過網卡網線路由器交換機等。這兩天測試發現經過nginx代理后連接並發數上不去,查了一天,發現問題是nginx的keepalive參數沒有打開。這個參數沒有打開的話, 會造成每次代理,都會創建一個http-socket,處理完就關閉,這樣會比較占用資源,同時連接數上不去。加上keepalive參數后,外網的N個並發請求就可以通過一條socket發送多次請求。(這個描述比較不清楚, 如果對http1.0 http1.1 http2.0 里面的關於HTTP協議連接問題了解的話, 上面的描述就不難理解。)

  集群的upstream設置為

1 # 這里要設置keepalive 表示本機與后端機器的連接數
2 # 同時這里還有一些其他設置,如權重,負載類型等
3 upstream wunaozai.cnblogs.com {
4     server 192.168.25.106:888;
5     server 192.168.25.100:888;
6     server 192.168.9.201:888;
7     keepalive 1000;
8 }

  server代理設置為

 1 server {
 2     listen 88 backlog=168888;
 3     server_name localhost2;
 4     location ~ .*$ {
 5         index index;
 6         proxy_pass http://wunaozai.cnblogs.com;
 7         proxy_set_header Connection "keep-alive";
 8         proxy_http_version 1.1;
 9         proxy_ignore_client_abort on;
10         #下面的timeout跟自己的業務相關設置對應的timeout
11         proxy_connect_timeout 600;
12         proxy_read_timeout 600;
13         proxy_send_timeout 600;
14     }
15 }

 

 

 

參考資料:

  http://datacratic.com/site/blog/1m-qps-nginx-and-ubuntu-1204-ec2

  http://serverfault.com/questions/408546/how-to-achieve-500k-requests-per-second-on-my-webserver

  https://lowlatencyweb.wordpress.com/2012/03/20/500000-requestssec-modern-http-servers-are-fast/

  http://blog.jobbole.com/87531/

  https://github.com/wg/wrk 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM