nginx啟動配置加載性能分析(一)-作為http服務器

jopen 10年前發布 | 18K 次閱讀 Web服務器 Nginx

本文測試了在配置文件規模較大時, nginx作為http服務器的啟動速度, 并分析耗時原因。

結論:
1. nginx初始化中影響性能點在于listen IP:PORT, 其中port的匯聚會造成初始化速度變得很慢
2. 對于server_name的初始化相當快, 對初始化性能無影響

一、測試內容

腳本創建3類配置文件, 規則如下
1. 共創建2萬條server{}配置
2. server_name基本定長, PerformanceTestxxx
3. 第1類是listen的IP完全一致,port全不同, 配置文件實例:
http{
  server{
    listen 192.168.0.1:8080;
    server_name PerformanceTest8080;
  } 
  …
  server{
    listen 192.168.0.1:8081;
    server_name PerformanceTest8081; 
    ….
  }
}
3. 第2類是listen的Port完全一致,IP全不同, 配置文件實例:
http{
  server{
    listen 192.168.0.1:80;
    server_name PerformanceTest1;
  } 
  …
  server{
    listen 192.168.0.2:80;
    server_name PerformanceTest2; 
    ….
  }
}
4. 第3類是listen的IP:PORT完全一致, server_name全不同, 配置文件實例:
http{
  server{
    listen 192.168.0.1:80;
    server_name PerformanceTest001;
  } 
  …
  server{
    listen 192.168.0.1:80;
    server_name PerformanceTest002; 
    ….
  }
}

二、 測試數據
對于3類配置, 再加入3組變量:
1. 不配置server_name
2. server_name全配置一樣
3. server_name全配置不一樣
看server_name對于初始化速度的影響

9種組合的性能如下:

  文件大小 文件行數 nginx啟動時間 reload時間
listen相同port
ip全不一樣
無server_name
905K 60015

real    0m23.547s
user    0m8.454s
sys     0m15.075s

real    0m9.503s
user    0m9.339s
sys     0m0.158s

listen相同port
ip全不一樣,
server_name全一樣
1.7M 80015

real    0m23.130s
user    0m8.487s
sys     0m14.626s

real    0m9.033s
user    0m8.856s
sys     0m0.170s

listen相同port
ip全不一樣,
server_name全不一樣
1.8M 80015

real    0m24.942s
user    0m8.722s
sys     0m16.199s

real    0m8.860s
user    0m8.697s
sys     0m0.157s

listen不同的port
ip全一樣
無server_name
880K 60015

real    0m7.210s
user    0m1.005s
sys     0m6.197s

real    0m1.203s
user    0m1.033s
sys     0m0.169s

listen不同的port
ip全一樣
server_name全一樣
1.7M 80015

real    0m7.145s
user    0m1.017s
sys     0m6.121s

real    0m1.238s
user    0m1.043s
sys     0m0.194s

listen不同的port
ip全一樣
server_name全不一樣
1.8M 80015

real    0m8.166s
user    0m1.013s
sys     0m7.144s

real    0m1.229s
user    0m1.049s
sys     0m0.179s

listen相同port, ip, server_name全不一樣 1.8M 80015

real    0m0.841s
user    0m0.693s
sys     0m0.147s

real    0m0.846s
user    0m0.697s
sys     0m0.148s

nginx 啟動時間:    time ./nginx -c /root/nginx.conf.sameport.noloc
nginx reload時間: time ./nginx -c /root/nginx.conf.sameport.noloc –s reload

從測試數據可以看出, 對nginx啟動速度影響的因素為server{}中listen的port, server_name指令基本無影響

三、 原因分析
3.1 http{}初始化流程簡單介紹

image

解析配置文件是遞歸地調用ngx_conf_parse函數完成的, http{}配置塊的解析流程:
1. 解析到http指令, 執行ngx_http_block函數, 創建http module的配置上下文后, 繼續ngx_conf_parse解析http{}內部的內容;
2. 解析到server指令, 執行ngx_http_core_server函數, 創建該server{}的配置上下文后, 繼續ngx_conf_parse解析server{}內部的內容; 
3. 解析到listen指令, 添加到cscf以及cmcf配置中, 如下圖

 

1. cmcf->servers數組保存了所有監聽的server數據

image

2. cmcf->ports數組保存了所有port匯聚的ip的數據. listen同一個ip:port, server_name不同的srv_conf會掛在ngx_http_conf_addr_t的servers數組下。

image 

一個配置好的樣子可能是這樣:

image

3.2 耗時位置定位

代碼中加變量記錄函數耗時總時間, 得到啟動時耗時在2個步驟:
1) 解析配置文件, 對應ngx_conf_parse函數
2) 初始化socket, 對應ngx_open_listening_sockets函數

函數耗時

Port全一致

IP全一致

IP:PORT全一致

ngx_conf_parse

8.8s

1.2s

0.8s

ngx_open_listening_sockets

16.6s

6.2s

0s

 

3.3 ngx_conf_parse耗時分析

主要是ngx_http_block函數的耗時, 分為以下2個部分

統計項

Port全一致

IP全一致

IP:PORT全一致

ngx_conf_parse

8.3s

0.6s

0.3s

ngx_conf_parse
完成后續工作

0.5s

0.5s

0.5s

統計項

Port全一致

IP全一致

ngx_http_add_addresses調用次數

20000

20000

ngx_http_add_address調用次數

20000

20000

ngx_http_add_server調用次數

20000

20000

ngx_http_add_addresses總耗時

7121ms

0ms

ngx_http_add_address總耗時

18ms

21ms

ngx_http_add_server總耗時

10ms

5ms

很明顯, ngx_http_add_addresses函數的性能消耗在對相同port已存在ip的查找上面, 這里用的是線性的遍歷查找,
且需要進行字符串比較ngx_memcmp被執行了2w*2w共4億次

for (i = 0; i < port->addrs.nelts; i++) {
        // 遍歷查找, 如果配置文件中,相同port的IP過多,字符串比較帶來較大性能問題, 2w個listen,這一塊耗時需要8s左右         if (ngx_memcmp(p, addr[i].opt.u.sockaddr_data + off, len) != 0) {
            continue;
        }

        /* the address is already in the address list */         // 找到對應的ip, 添加cscf到IP         if (ngx_http_add_server(cf, cscf, &addr[i]) != NGX_OK) {
            return NGX_ERROR;
        }

3.4 ngx_open_listening_sockets耗時分析

函數中初始化所有listening的socket

/* for each listening socket */  ls = cycle->listening.elts;
 for (i = 0; i < cycle->listening.nelts; i++) {
   if (bind(s, ls[i].sockaddr, ls[i].socklen) == -1) {
   …
   …    if (listen(s, ls[i].backlog) == -1) {
}

統計項

Port全一致

IP全一致

IP:PORT全一致

cycle->listening.nelts

20000

20000

1

bind總耗時

4.2s

0.02s

0

listen總耗時

13.9s

6.2s

0

對于同1個IP, 新建不同port的socket相比新建多個不同IP的socket更省時間.

來自:http://my.oschina.net/chenzhuo/blog/184892

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!